mirror of
https://github.com/XTLS/Xray-core.git
synced 2025-08-23 01:56:48 +08:00
Compare commits
62 Commits
v1.250608.
...
v1.250803.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
bd86732f68 | ||
![]() |
d4f11e6d68 | ||
![]() |
00f3147242 | ||
![]() |
7cbf5b004c | ||
![]() |
87fff12fd9 | ||
![]() |
a02723e63f | ||
![]() |
146b14ab55 | ||
![]() |
b2829219a0 | ||
![]() |
116cd70a3a | ||
![]() |
c569f478af | ||
![]() |
b6b51c51c8 | ||
![]() |
fb7a9d8d61 | ||
![]() |
3fe02a658a | ||
![]() |
5f93ff6c3a | ||
![]() |
10376f5b4d | ||
![]() |
1ea00fad81 | ||
![]() |
cfcf2a63d1 | ||
![]() |
66025f2889 | ||
![]() |
c9cd26d6d3 | ||
![]() |
caee152adf | ||
![]() |
eb433d9462 | ||
![]() |
87d8b97d9a | ||
![]() |
9d15ecf1f9 | ||
![]() |
4f45c5faa5 | ||
![]() |
26de58933f | ||
![]() |
31b508d372 | ||
![]() |
955a569181 | ||
![]() |
d141d01d0c | ||
![]() |
4e826abebf | ||
![]() |
4433641e30 | ||
![]() |
a196a16c55 | ||
![]() |
8c0bf15901 | ||
![]() |
dbd9125686 | ||
![]() |
923b5d7229 | ||
![]() |
f90fae22aa | ||
![]() |
b065595f58 | ||
![]() |
308f8a7459 | ||
![]() |
050f596e8f | ||
![]() |
3b47d0846e | ||
![]() |
7f23a1cb65 | ||
![]() |
446315cf1f | ||
![]() |
eed05549fc | ||
![]() |
2b4a8d235b | ||
![]() |
83686ebfaa | ||
![]() |
79c6f99384 | ||
![]() |
ca8ef209a7 | ||
![]() |
cbcab89c7e | ||
![]() |
abd551e9f7 | ||
![]() |
10dbeb4335 | ||
![]() |
6afd721ced | ||
![]() |
5c0bc361d3 | ||
![]() |
3a2ac9d0bf | ||
![]() |
1785178762 | ||
![]() |
1976d02ec9 | ||
![]() |
3ba733079e | ||
![]() |
6a8a85f83a | ||
![]() |
409e4e8f12 | ||
![]() |
486d005986 | ||
![]() |
cb1afb33e6 | ||
![]() |
38ed2cc387 | ||
![]() |
b043db8260 | ||
![]() |
27742da2c6 |
136
.github/workflows/docker.yml
vendored
136
.github/workflows/docker.yml
vendored
@@ -1,36 +1,77 @@
|
|||||||
name: Build docker image
|
name: Build and Push Docker Image
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types:
|
||||||
|
- published
|
||||||
|
- released
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
description: "Docker image tag:"
|
||||||
|
required: true
|
||||||
|
latest:
|
||||||
|
description: "Set to latest"
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-image:
|
build-and-push:
|
||||||
|
if: (github.event.action != 'published') || (github.event.action == 'published' && github.event.release.prerelease == true)
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Set repository and image name to lowercase
|
||||||
|
env:
|
||||||
|
IMAGE_NAME: "${{ github.repository }}"
|
||||||
|
run: |
|
||||||
|
echo "IMAGE_NAME=${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
||||||
|
echo "FULL_IMAGE_NAME=ghcr.io/${IMAGE_NAME,,}" >>${GITHUB_ENV}
|
||||||
|
|
||||||
- name: Docker metadata
|
- name: Validate and extract tag
|
||||||
id: meta
|
run: |
|
||||||
uses: docker/metadata-action@v5
|
SOURCE_TAG="${{ github.event.inputs.tag }}"
|
||||||
with:
|
if [[ -z "$SOURCE_TAG" ]]; then
|
||||||
images: ghcr.io/${{ github.repository_owner }}/xray-core
|
SOURCE_TAG="${{ github.ref_name }}"
|
||||||
flavor: latest=auto
|
fi
|
||||||
tags: |
|
if [[ -z "$SOURCE_TAG" ]]; then
|
||||||
type=semver,pattern={{version}}
|
SOURCE_TAG="${{ github.event.release.tag_name }}"
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Docker metadata (unsupported architectures)
|
if [[ -z "$SOURCE_TAG" ]]; then
|
||||||
id: metausa
|
echo "Error: Could not determine a valid tag source. Input tag and context tag (github.ref_name) are both empty."
|
||||||
uses: docker/metadata-action@v5
|
exit 1
|
||||||
with:
|
fi
|
||||||
images: ghcr.io/${{ github.repository_owner }}/xray-core
|
|
||||||
flavor: |
|
if [[ "$SOURCE_TAG" =~ ^v[0-9]+\.[0-9] ]]; then
|
||||||
latest=auto
|
IMAGE_TAG="${SOURCE_TAG#v}"
|
||||||
suffix=-usa,onlatest=true
|
else
|
||||||
tags: |
|
IMAGE_TAG="$SOURCE_TAG"
|
||||||
type=semver,pattern={{version}}
|
fi
|
||||||
|
|
||||||
|
echo "Docker image tag: '$IMAGE_TAG'."
|
||||||
|
echo "IMAGE_TAG=$IMAGE_TAG" >>${GITHUB_ENV}
|
||||||
|
|
||||||
|
LATEST=false
|
||||||
|
if [[ "${{ github.event_name }}" == "release" && "${{ github.event.release.prerelease }}" == "false" ]] || [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.latest }}" == "true" ]]; then
|
||||||
|
LATEST=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Latest: '$LATEST'."
|
||||||
|
echo "LATEST=$LATEST" >>${GITHUB_ENV}
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
@@ -39,13 +80,12 @@ jobs:
|
|||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Build Docker image (main architectures)
|
||||||
uses: docker/setup-buildx-action@v3
|
id: build_main_arches
|
||||||
|
|
||||||
- name: Build and push
|
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
|
file: .github/docker/Dockerfile
|
||||||
platforms: |
|
platforms: |
|
||||||
linux/amd64
|
linux/amd64
|
||||||
linux/arm/v7
|
linux/arm/v7
|
||||||
@@ -53,39 +93,41 @@ jobs:
|
|||||||
linux/ppc64le
|
linux/ppc64le
|
||||||
linux/s390x
|
linux/s390x
|
||||||
provenance: false
|
provenance: false
|
||||||
file: .github/docker/Dockerfile
|
outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
|
||||||
push: true
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
|
|
||||||
- name: Build and push (unsupported architectures)
|
- name: Build Docker image (additional architectures)
|
||||||
|
id: build_additional_arches
|
||||||
uses: docker/build-push-action@v6
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
|
file: .github/docker/Dockerfile.usa
|
||||||
platforms: |
|
platforms: |
|
||||||
linux/386
|
linux/386
|
||||||
linux/arm/v6
|
linux/arm/v6
|
||||||
linux/riscv64
|
linux/riscv64
|
||||||
linux/loong64
|
linux/loong64
|
||||||
provenance: false
|
provenance: false
|
||||||
file: .github/docker/Dockerfile.usa
|
outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true
|
||||||
push: true
|
|
||||||
tags: ${{ steps.metausa.outputs.tags }}
|
|
||||||
|
|
||||||
- name: Merge Multi-Arch Manifests
|
- name: Create manifest list and push
|
||||||
run: |
|
run: |
|
||||||
echo "Starting to merge multi-architecture manifests..."
|
echo "Creating multi-arch manifest with tag: '${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}'."
|
||||||
|
docker buildx imagetools create \
|
||||||
|
--tag ${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }} \
|
||||||
|
${{ env.FULL_IMAGE_NAME }}@${{ steps.build_main_arches.outputs.digest }} \
|
||||||
|
${{ env.FULL_IMAGE_NAME }}@${{ steps.build_additional_arches.outputs.digest }}
|
||||||
|
|
||||||
# Convert newlines to spaces and split into array
|
if [[ "${{ env.LATEST }}" == "true" ]]; then
|
||||||
TAGS=($(echo "${{ steps.meta.outputs.tags }}" | tr '\n' ' '))
|
echo "Adding 'latest' tag to manifest: '${{ env.FULL_IMAGE_NAME }}:latest'."
|
||||||
|
docker buildx imagetools create \
|
||||||
echo "Total tags to process: ${#TAGS[@]}"
|
--tag ${{ env.FULL_IMAGE_NAME }}:latest \
|
||||||
for tag in "${TAGS[@]}"; do
|
${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}
|
||||||
echo "Merging tag: $tag with unsupported architectures ($tag-usa)"
|
|
||||||
docker buildx imagetools create --append --tag "$tag" "$tag-usa"
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Error: Failed to merge $tag-usa into $tag"
|
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
done
|
|
||||||
|
|
||||||
echo "Multi-architecture manifest merge completed successfully."
|
- name: Inspect image
|
||||||
|
run: |
|
||||||
|
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }}
|
||||||
|
|
||||||
|
if [[ "${{ env.LATEST }}" == "true" ]]; then
|
||||||
|
docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:latest
|
||||||
|
fi
|
||||||
|
21
README.md
21
README.md
@@ -6,9 +6,13 @@
|
|||||||
|
|
||||||
## Donation & NFTs
|
## Donation & NFTs
|
||||||
|
|
||||||
|
### [Collect a Project X NFT to support the development of Project X!](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1)
|
||||||
|
|
||||||
|
[<img alt="Project X NFT" width="150px" src="https://raw2.seadn.io/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/7fa9ce900fb39b44226348db330e32/8b7fa9ce900fb39b44226348db330e32.svg" />](https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/1)
|
||||||
|
|
||||||
- **ETH/USDT/USDC: `0xDc3Fe44F0f25D13CACb1C4896CD0D321df3146Ee`**
|
- **ETH/USDT/USDC: `0xDc3Fe44F0f25D13CACb1C4896CD0D321df3146Ee`**
|
||||||
- **Project X NFT: [Announcement of NFTs by Project X](https://github.com/XTLS/Xray-core/discussions/3633)**
|
- **REALITY NFT: https://opensea.io/item/ethereum/0x5ee362866001613093361eb8569d59c4141b76d1/2**
|
||||||
- **REALITY NFT: [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113)**
|
- **Related links: https://opensea.io/collection/xtls, [Announcement of NFTs by Project X](https://github.com/XTLS/Xray-core/discussions/3633), [XHTTP: Beyond REALITY](https://github.com/XTLS/Xray-core/discussions/4113)**
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
@@ -81,28 +85,35 @@
|
|||||||
- [v2rayN](https://github.com/2dust/v2rayN)
|
- [v2rayN](https://github.com/2dust/v2rayN)
|
||||||
- [Furious](https://github.com/LorenEteval/Furious)
|
- [Furious](https://github.com/LorenEteval/Furious)
|
||||||
- [Invisible Man - Xray](https://github.com/InvisibleManVPN/InvisibleMan-XRayClient)
|
- [Invisible Man - Xray](https://github.com/InvisibleManVPN/InvisibleMan-XRayClient)
|
||||||
|
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
|
||||||
- Android
|
- Android
|
||||||
- [v2rayNG](https://github.com/2dust/v2rayNG)
|
- [v2rayNG](https://github.com/2dust/v2rayNG)
|
||||||
- [X-flutter](https://github.com/XTLS/X-flutter)
|
- [X-flutter](https://github.com/XTLS/X-flutter)
|
||||||
- [SaeedDev94/Xray](https://github.com/SaeedDev94/Xray)
|
- [SaeedDev94/Xray](https://github.com/SaeedDev94/Xray)
|
||||||
- [SimpleXray](https://github.com/lhear/SimpleXray)
|
- [SimpleXray](https://github.com/lhear/SimpleXray)
|
||||||
- iOS & macOS arm64
|
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
|
||||||
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215)
|
- iOS & macOS arm64 & tvOS
|
||||||
|
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) ([tvOS](https://apps.apple.com/us/app/happ-proxy-utility-for-tv/id6748297274))
|
||||||
- [Streisand](https://apps.apple.com/app/streisand/id6450534064)
|
- [Streisand](https://apps.apple.com/app/streisand/id6450534064)
|
||||||
- [OneXray](https://github.com/OneXray/OneXray)
|
- [OneXray](https://github.com/OneXray/OneXray)
|
||||||
- macOS arm64 & x64
|
- macOS arm64 & x64
|
||||||
|
- [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215)
|
||||||
- [V2rayU](https://github.com/yanue/V2rayU)
|
- [V2rayU](https://github.com/yanue/V2rayU)
|
||||||
- [V2RayXS](https://github.com/tzmax/V2RayXS)
|
- [V2RayXS](https://github.com/tzmax/V2RayXS)
|
||||||
- [Furious](https://github.com/LorenEteval/Furious)
|
- [Furious](https://github.com/LorenEteval/Furious)
|
||||||
- [OneXray](https://github.com/OneXray/OneXray)
|
- [OneXray](https://github.com/OneXray/OneXray)
|
||||||
|
- [GoXRay](https://github.com/goxray/desktop)
|
||||||
|
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
|
||||||
- Linux
|
- Linux
|
||||||
- [v2rayA](https://github.com/v2rayA/v2rayA)
|
- [v2rayA](https://github.com/v2rayA/v2rayA)
|
||||||
- [Furious](https://github.com/LorenEteval/Furious)
|
- [Furious](https://github.com/LorenEteval/Furious)
|
||||||
- [GorzRay](https://github.com/ketetefid/GorzRay)
|
- [GorzRay](https://github.com/ketetefid/GorzRay)
|
||||||
|
- [GoXRay](https://github.com/goxray/desktop)
|
||||||
|
- [AnyPortal](https://github.com/AnyPortal/AnyPortal)
|
||||||
|
|
||||||
## Others that support VLESS, XTLS, REALITY, XUDP, PLUX...
|
## Others that support VLESS, XTLS, REALITY, XUDP, PLUX...
|
||||||
|
|
||||||
- iOS & macOS arm64
|
- iOS & macOS arm64 & tvOS
|
||||||
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
|
- [Shadowrocket](https://apps.apple.com/app/shadowrocket/id932747118)
|
||||||
- [Loon](https://apps.apple.com/us/app/loon/id1373567447)
|
- [Loon](https://apps.apple.com/us/app/loon/id1373567447)
|
||||||
- Xray Tools
|
- Xray Tools
|
||||||
|
@@ -204,7 +204,12 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Static host lookup
|
// Static host lookup
|
||||||
switch addrs := s.hosts.Lookup(domain, option); {
|
switch addrs, err := s.hosts.Lookup(domain, option); {
|
||||||
|
case err != nil:
|
||||||
|
if go_errors.Is(err, dns.ErrEmptyResponse) {
|
||||||
|
return nil, 0, dns.ErrEmptyResponse
|
||||||
|
}
|
||||||
|
return nil, 0, errors.New("returning nil for domain ", domain).Base(err)
|
||||||
case addrs == nil: // Domain not recorded in static host
|
case addrs == nil: // Domain not recorded in static host
|
||||||
break
|
break
|
||||||
case len(addrs) == 0: // Domain recorded, but no valid IP returned (e.g. IPv4 address with only IPv6 enabled)
|
case len(addrs) == 0: // Domain recorded, but no valid IP returned (e.g. IPv4 address with only IPv6 enabled)
|
||||||
|
@@ -2,6 +2,8 @@ package dns
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/common/net"
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/common/strmatcher"
|
"github.com/xtls/xray-core/common/strmatcher"
|
||||||
@@ -31,7 +33,15 @@ func NewStaticHosts(hosts []*Config_HostMapping) (*StaticHosts, error) {
|
|||||||
ips := make([]net.Address, 0, len(mapping.Ip)+1)
|
ips := make([]net.Address, 0, len(mapping.Ip)+1)
|
||||||
switch {
|
switch {
|
||||||
case len(mapping.ProxiedDomain) > 0:
|
case len(mapping.ProxiedDomain) > 0:
|
||||||
|
if mapping.ProxiedDomain[0] == '#' {
|
||||||
|
rcode, err := strconv.Atoi(mapping.ProxiedDomain[1:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ips = append(ips, dns.RCodeError(rcode))
|
||||||
|
} else {
|
||||||
ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
|
ips = append(ips, net.DomainAddress(mapping.ProxiedDomain))
|
||||||
|
}
|
||||||
case len(mapping.Ip) > 0:
|
case len(mapping.Ip) > 0:
|
||||||
for _, ip := range mapping.Ip {
|
for _, ip := range mapping.Ip {
|
||||||
addr := net.IPAddress(ip)
|
addr := net.IPAddress(ip)
|
||||||
@@ -58,38 +68,51 @@ func filterIP(ips []net.Address, option dns.IPOption) []net.Address {
|
|||||||
return filtered
|
return filtered
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *StaticHosts) lookupInternal(domain string) []net.Address {
|
func (h *StaticHosts) lookupInternal(domain string) ([]net.Address, error) {
|
||||||
ips := make([]net.Address, 0)
|
ips := make([]net.Address, 0)
|
||||||
found := false
|
found := false
|
||||||
for _, id := range h.matchers.Match(domain) {
|
for _, id := range h.matchers.Match(domain) {
|
||||||
|
for _, v := range h.ips[id] {
|
||||||
|
if err, ok := v.(dns.RCodeError); ok {
|
||||||
|
if uint16(err) == 0 {
|
||||||
|
return nil, dns.ErrEmptyResponse
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
ips = append(ips, h.ips[id]...)
|
ips = append(ips, h.ips[id]...)
|
||||||
found = true
|
found = true
|
||||||
}
|
}
|
||||||
if !found {
|
if !found {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return ips
|
return ips, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) []net.Address {
|
func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) ([]net.Address, error) {
|
||||||
switch addrs := h.lookupInternal(domain); {
|
switch addrs, err := h.lookupInternal(domain); {
|
||||||
|
case err != nil:
|
||||||
|
return nil, err
|
||||||
case len(addrs) == 0: // Not recorded in static hosts, return nil
|
case len(addrs) == 0: // Not recorded in static hosts, return nil
|
||||||
return addrs
|
return addrs, nil
|
||||||
case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Try to unwrap domain
|
case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Try to unwrap domain
|
||||||
errors.LogDebug(context.Background(), "found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it")
|
errors.LogDebug(context.Background(), "found replaced domain: ", domain, " -> ", addrs[0].Domain(), ". Try to unwrap it")
|
||||||
if maxDepth > 0 {
|
if maxDepth > 0 {
|
||||||
unwrapped := h.lookup(addrs[0].Domain(), option, maxDepth-1)
|
unwrapped, err := h.lookup(addrs[0].Domain(), option, maxDepth-1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if unwrapped != nil {
|
if unwrapped != nil {
|
||||||
return unwrapped
|
return unwrapped, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return addrs
|
return addrs, nil
|
||||||
default: // IP record found, return a non-nil IP array
|
default: // IP record found, return a non-nil IP array
|
||||||
return filterIP(addrs, option)
|
return filterIP(addrs, option), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts.
|
// Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts.
|
||||||
func (h *StaticHosts) Lookup(domain string, option dns.IPOption) []net.Address {
|
func (h *StaticHosts) Lookup(domain string, option dns.IPOption) ([]net.Address, error) {
|
||||||
return h.lookup(domain, option, 5)
|
return h.lookup(domain, option, 5)
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,11 @@ import (
|
|||||||
|
|
||||||
func TestStaticHosts(t *testing.T) {
|
func TestStaticHosts(t *testing.T) {
|
||||||
pb := []*Config_HostMapping{
|
pb := []*Config_HostMapping{
|
||||||
|
{
|
||||||
|
Type: DomainMatchingType_Subdomain,
|
||||||
|
Domain: "lan",
|
||||||
|
ProxiedDomain: "#3",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Type: DomainMatchingType_Full,
|
Type: DomainMatchingType_Full,
|
||||||
Domain: "example.com",
|
Domain: "example.com",
|
||||||
@@ -54,7 +59,14 @@ func TestStaticHosts(t *testing.T) {
|
|||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
{
|
{
|
||||||
ips := hosts.Lookup("example.com", dns.IPOption{
|
_, err := hosts.Lookup("example.com.lan", dns.IPOption{})
|
||||||
|
if dns.RCodeFromError(err) != 3 {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ips, _ := hosts.Lookup("example.com", dns.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
IPv6Enable: true,
|
IPv6Enable: true,
|
||||||
})
|
})
|
||||||
@@ -67,7 +79,7 @@ func TestStaticHosts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
domain := hosts.Lookup("proxy.xray.com", dns.IPOption{
|
domain, _ := hosts.Lookup("proxy.xray.com", dns.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
IPv6Enable: false,
|
IPv6Enable: false,
|
||||||
})
|
})
|
||||||
@@ -80,7 +92,7 @@ func TestStaticHosts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
domain := hosts.Lookup("proxy2.xray.com", dns.IPOption{
|
domain, _ := hosts.Lookup("proxy2.xray.com", dns.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
IPv6Enable: false,
|
IPv6Enable: false,
|
||||||
})
|
})
|
||||||
@@ -93,7 +105,7 @@ func TestStaticHosts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ips := hosts.Lookup("www.example.cn", dns.IPOption{
|
ips, _ := hosts.Lookup("www.example.cn", dns.IPOption{
|
||||||
IPv4Enable: true,
|
IPv4Enable: true,
|
||||||
IPv6Enable: true,
|
IPv6Enable: true,
|
||||||
})
|
})
|
||||||
@@ -106,7 +118,7 @@ func TestStaticHosts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
ips := hosts.Lookup("baidu.com", dns.IPOption{
|
ips, _ := hosts.Lookup("baidu.com", dns.IPOption{
|
||||||
IPv4Enable: false,
|
IPv4Enable: false,
|
||||||
IPv6Enable: true,
|
IPv6Enable: true,
|
||||||
})
|
})
|
||||||
|
@@ -32,7 +32,7 @@ type QUICNameServer struct {
|
|||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
cacheController *CacheController
|
cacheController *CacheController
|
||||||
destination *net.Destination
|
destination *net.Destination
|
||||||
connection quic.Connection
|
connection *quic.Conn
|
||||||
clientIP net.IP
|
clientIP net.IP
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, option dns_
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isActive(s quic.Connection) bool {
|
func isActive(s *quic.Conn) bool {
|
||||||
select {
|
select {
|
||||||
case <-s.Context().Done():
|
case <-s.Context().Done():
|
||||||
return false
|
return false
|
||||||
@@ -229,8 +229,8 @@ func isActive(s quic.Connection) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *QUICNameServer) getConnection() (quic.Connection, error) {
|
func (s *QUICNameServer) getConnection() (*quic.Conn, error) {
|
||||||
var conn quic.Connection
|
var conn *quic.Conn
|
||||||
s.RLock()
|
s.RLock()
|
||||||
conn = s.connection
|
conn = s.connection
|
||||||
if conn != nil && isActive(conn) {
|
if conn != nil && isActive(conn) {
|
||||||
@@ -263,7 +263,7 @@ func (s *QUICNameServer) getConnection() (quic.Connection, error) {
|
|||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *QUICNameServer) openConnection() (quic.Connection, error) {
|
func (s *QUICNameServer) openConnection() (*quic.Conn, error) {
|
||||||
tlsConfig := tls.Config{}
|
tlsConfig := tls.Config{}
|
||||||
quicConfig := &quic.Config{
|
quicConfig := &quic.Config{
|
||||||
HandshakeIdleTimeout: handshakeTimeout,
|
HandshakeIdleTimeout: handshakeTimeout,
|
||||||
@@ -283,7 +283,7 @@ func (s *QUICNameServer) openConnection() (quic.Connection, error) {
|
|||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *QUICNameServer) openStream(ctx context.Context) (quic.Stream, error) {
|
func (s *QUICNameServer) openStream(ctx context.Context) (*quic.Stream, error) {
|
||||||
conn, err := s.getConnection()
|
conn, err := s.getConnection()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@@ -90,6 +90,8 @@ type HealthPingConfig struct {
|
|||||||
SamplingCount int32 `protobuf:"varint,4,opt,name=samplingCount,proto3" json:"samplingCount,omitempty"`
|
SamplingCount int32 `protobuf:"varint,4,opt,name=samplingCount,proto3" json:"samplingCount,omitempty"`
|
||||||
// ping timeout, int64 values of time.Duration
|
// ping timeout, int64 values of time.Duration
|
||||||
Timeout int64 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"`
|
Timeout int64 `protobuf:"varint,5,opt,name=timeout,proto3" json:"timeout,omitempty"`
|
||||||
|
// http method to make request
|
||||||
|
HttpMethod string `protobuf:"bytes,6,opt,name=httpMethod,proto3" json:"httpMethod,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *HealthPingConfig) Reset() {
|
func (x *HealthPingConfig) Reset() {
|
||||||
@@ -157,6 +159,13 @@ func (x *HealthPingConfig) GetTimeout() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *HealthPingConfig) GetHttpMethod() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.HttpMethod
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
var File_app_observatory_burst_config_proto protoreflect.FileDescriptor
|
var File_app_observatory_burst_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_app_observatory_burst_config_proto_rawDesc = []byte{
|
var file_app_observatory_burst_config_proto_rawDesc = []byte{
|
||||||
@@ -173,7 +182,7 @@ var file_app_observatory_burst_config_proto_rawDesc = []byte{
|
|||||||
0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x62, 0x75, 0x72,
|
0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x62, 0x75, 0x72,
|
||||||
0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e,
|
0x73, 0x74, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e,
|
||||||
0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22,
|
0x66, 0x69, 0x67, 0x52, 0x0a, 0x70, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22,
|
||||||
0xb4, 0x01, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x43, 0x6f,
|
0xd4, 0x01, 0x0a, 0x10, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50, 0x69, 0x6e, 0x67, 0x43, 0x6f,
|
||||||
0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
|
0x6e, 0x66, 0x69, 0x67, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
|
||||||
0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69,
|
0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x69,
|
||||||
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
|
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
|
||||||
@@ -184,7 +193,9 @@ var file_app_observatory_burst_config_proto_rawDesc = []byte{
|
|||||||
0x6e, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x73,
|
0x6e, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x73,
|
||||||
0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07,
|
0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07,
|
||||||
0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74,
|
0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74,
|
||||||
0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x42, 0x70, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
|
0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x68, 0x74, 0x74, 0x70, 0x4d, 0x65,
|
||||||
|
0x74, 0x68, 0x6f, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x68, 0x74, 0x74, 0x70,
|
||||||
|
0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x42, 0x70, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72,
|
||||||
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f,
|
0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x6f,
|
||||||
0x72, 0x79, 0x2e, 0x62, 0x75, 0x72, 0x73, 0x74, 0x50, 0x01, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68,
|
0x72, 0x79, 0x2e, 0x62, 0x75, 0x72, 0x73, 0x74, 0x50, 0x01, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68,
|
||||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
|
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
|
||||||
|
@@ -26,4 +26,7 @@ message HealthPingConfig {
|
|||||||
int32 samplingCount = 4;
|
int32 samplingCount = 4;
|
||||||
// ping timeout, int64 values of time.Duration
|
// ping timeout, int64 values of time.Duration
|
||||||
int64 timeout = 5;
|
int64 timeout = 5;
|
||||||
|
// http method to make request
|
||||||
|
string httpMethod = 6;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -19,6 +19,7 @@ type HealthPingSettings struct {
|
|||||||
Interval time.Duration `json:"interval"`
|
Interval time.Duration `json:"interval"`
|
||||||
SamplingCount int `json:"sampling"`
|
SamplingCount int `json:"sampling"`
|
||||||
Timeout time.Duration `json:"timeout"`
|
Timeout time.Duration `json:"timeout"`
|
||||||
|
HttpMethod string `json:"httpMethod"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HealthPing is the health checker for balancers
|
// HealthPing is the health checker for balancers
|
||||||
@@ -37,12 +38,21 @@ type HealthPing struct {
|
|||||||
func NewHealthPing(ctx context.Context, dispatcher routing.Dispatcher, config *HealthPingConfig) *HealthPing {
|
func NewHealthPing(ctx context.Context, dispatcher routing.Dispatcher, config *HealthPingConfig) *HealthPing {
|
||||||
settings := &HealthPingSettings{}
|
settings := &HealthPingSettings{}
|
||||||
if config != nil {
|
if config != nil {
|
||||||
|
|
||||||
|
var httpMethod string
|
||||||
|
if config.HttpMethod == "" {
|
||||||
|
httpMethod = "HEAD"
|
||||||
|
} else {
|
||||||
|
httpMethod = strings.TrimSpace(config.HttpMethod)
|
||||||
|
}
|
||||||
|
|
||||||
settings = &HealthPingSettings{
|
settings = &HealthPingSettings{
|
||||||
Connectivity: strings.TrimSpace(config.Connectivity),
|
Connectivity: strings.TrimSpace(config.Connectivity),
|
||||||
Destination: strings.TrimSpace(config.Destination),
|
Destination: strings.TrimSpace(config.Destination),
|
||||||
Interval: time.Duration(config.Interval),
|
Interval: time.Duration(config.Interval),
|
||||||
SamplingCount: int(config.SamplingCount),
|
SamplingCount: int(config.SamplingCount),
|
||||||
Timeout: time.Duration(config.Timeout),
|
Timeout: time.Duration(config.Timeout),
|
||||||
|
HttpMethod: httpMethod,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if settings.Destination == "" {
|
if settings.Destination == "" {
|
||||||
@@ -164,7 +174,7 @@ func (h *HealthPing) doCheck(tags []string, duration time.Duration, rounds int)
|
|||||||
}
|
}
|
||||||
time.AfterFunc(delay, func() {
|
time.AfterFunc(delay, func() {
|
||||||
errors.LogDebug(h.ctx, "checking ", handler)
|
errors.LogDebug(h.ctx, "checking ", handler)
|
||||||
delay, err := client.MeasureDelay()
|
delay, err := client.MeasureDelay(h.Settings.HttpMethod)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ch <- &rtt{
|
ch <- &rtt{
|
||||||
handler: handler,
|
handler: handler,
|
||||||
@@ -251,7 +261,7 @@ func (h *HealthPing) checkConnectivity() bool {
|
|||||||
h.Settings.Connectivity,
|
h.Settings.Connectivity,
|
||||||
h.Settings.Timeout,
|
h.Settings.Timeout,
|
||||||
)
|
)
|
||||||
if _, err := tester.MeasureDelay(); err != nil {
|
if _, err := tester.MeasureDelay(h.Settings.HttpMethod); err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@@ -2,6 +2,7 @@ package burst
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -51,20 +52,28 @@ func newHTTPClient(ctxv context.Context, dispatcher routing.Dispatcher, handler
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MeasureDelay returns the delay time of the request to dest
|
// MeasureDelay returns the delay time of the request to dest
|
||||||
func (s *pingClient) MeasureDelay() (time.Duration, error) {
|
func (s *pingClient) MeasureDelay(httpMethod string) (time.Duration, error) {
|
||||||
if s.httpClient == nil {
|
if s.httpClient == nil {
|
||||||
panic("pingClient not initialized")
|
panic("pingClient not initialized")
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest(http.MethodHead, s.destination, nil)
|
|
||||||
|
req, err := http.NewRequest(httpMethod, s.destination, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rttFailed, err
|
return rttFailed, err
|
||||||
}
|
}
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
resp, err := s.httpClient.Do(req)
|
resp, err := s.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rttFailed, err
|
return rttFailed, err
|
||||||
}
|
}
|
||||||
// don't wait for body
|
if httpMethod == http.MethodGet {
|
||||||
|
_, err = io.Copy(io.Discard, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return rttFailed, err
|
||||||
|
}
|
||||||
|
}
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
|
|
||||||
return time.Since(start), nil
|
return time.Since(start), nil
|
||||||
}
|
}
|
||||||
|
@@ -103,6 +103,13 @@ func (s *handlerServer) AlterInbound(ctx context.Context, request *AlterInboundR
|
|||||||
func (s *handlerServer) ListInbounds(ctx context.Context, request *ListInboundsRequest) (*ListInboundsResponse, error) {
|
func (s *handlerServer) ListInbounds(ctx context.Context, request *ListInboundsRequest) (*ListInboundsResponse, error) {
|
||||||
handlers := s.ihm.ListHandlers(ctx)
|
handlers := s.ihm.ListHandlers(ctx)
|
||||||
response := &ListInboundsResponse{}
|
response := &ListInboundsResponse{}
|
||||||
|
if request.GetIsOnlyTags() {
|
||||||
|
for _, handler := range handlers {
|
||||||
|
response.Inbounds = append(response.Inbounds, &core.InboundHandlerConfig{
|
||||||
|
Tag: handler.Tag(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
for _, handler := range handlers {
|
for _, handler := range handlers {
|
||||||
response.Inbounds = append(response.Inbounds, &core.InboundHandlerConfig{
|
response.Inbounds = append(response.Inbounds, &core.InboundHandlerConfig{
|
||||||
Tag: handler.Tag(),
|
Tag: handler.Tag(),
|
||||||
@@ -110,6 +117,8 @@ func (s *handlerServer) ListInbounds(ctx context.Context, request *ListInboundsR
|
|||||||
ProxySettings: handler.ProxySettings(),
|
ProxySettings: handler.ProxySettings(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -368,6 +368,8 @@ type ListInboundsRequest struct {
|
|||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
IsOnlyTags bool `protobuf:"varint,1,opt,name=isOnlyTags,proto3" json:"isOnlyTags,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *ListInboundsRequest) Reset() {
|
func (x *ListInboundsRequest) Reset() {
|
||||||
@@ -400,6 +402,13 @@ func (*ListInboundsRequest) Descriptor() ([]byte, []int) {
|
|||||||
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{8}
|
return file_app_proxyman_command_command_proto_rawDescGZIP(), []int{8}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *ListInboundsRequest) GetIsOnlyTags() bool {
|
||||||
|
if x != nil {
|
||||||
|
return x.IsOnlyTags
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type ListInboundsResponse struct {
|
type ListInboundsResponse struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -993,9 +1002,11 @@ var file_app_proxyman_command_command_proto_rawDesc = []byte{
|
|||||||
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70,
|
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x54, 0x79, 0x70,
|
||||||
0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61,
|
0x65, 0x64, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61,
|
||||||
0x74, 0x69, 0x6f, 0x6e, 0x22, 0x16, 0x0a, 0x14, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x62,
|
0x74, 0x69, 0x6f, 0x6e, 0x22, 0x16, 0x0a, 0x14, 0x41, 0x6c, 0x74, 0x65, 0x72, 0x49, 0x6e, 0x62,
|
||||||
0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x15, 0x0a, 0x13,
|
0x6f, 0x75, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x0a, 0x13,
|
||||||
0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75,
|
0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75,
|
||||||
0x65, 0x73, 0x74, 0x22, 0x53, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75,
|
0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x54, 0x61, 0x67,
|
||||||
|
0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x69, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x54,
|
||||||
|
0x61, 0x67, 0x73, 0x22, 0x53, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x6e, 0x62, 0x6f, 0x75,
|
||||||
0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x69,
|
0x6e, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x69,
|
||||||
0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
|
0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
|
||||||
0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e,
|
0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e,
|
||||||
|
@@ -37,7 +37,9 @@ message AlterInboundRequest {
|
|||||||
|
|
||||||
message AlterInboundResponse {}
|
message AlterInboundResponse {}
|
||||||
|
|
||||||
message ListInboundsRequest {}
|
message ListInboundsRequest {
|
||||||
|
bool isOnlyTags = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message ListInboundsResponse {
|
message ListInboundsResponse {
|
||||||
repeated core.InboundHandlerConfig inbounds = 1;
|
repeated core.InboundHandlerConfig inbounds = 1;
|
||||||
|
@@ -17,7 +17,7 @@ import (
|
|||||||
// Manager manages all inbound handlers.
|
// Manager manages all inbound handlers.
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
access sync.RWMutex
|
access sync.RWMutex
|
||||||
untaggedHandler []inbound.Handler
|
untaggedHandlers []inbound.Handler
|
||||||
taggedHandlers map[string]inbound.Handler
|
taggedHandlers map[string]inbound.Handler
|
||||||
running bool
|
running bool
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ func (m *Manager) AddHandler(ctx context.Context, handler inbound.Handler) error
|
|||||||
}
|
}
|
||||||
m.taggedHandlers[tag] = handler
|
m.taggedHandlers[tag] = handler
|
||||||
} else {
|
} else {
|
||||||
m.untaggedHandler = append(m.untaggedHandler, handler)
|
m.untaggedHandlers = append(m.untaggedHandlers, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.running {
|
if m.running {
|
||||||
@@ -94,8 +94,8 @@ func (m *Manager) ListHandlers(ctx context.Context) []inbound.Handler {
|
|||||||
m.access.RLock()
|
m.access.RLock()
|
||||||
defer m.access.RUnlock()
|
defer m.access.RUnlock()
|
||||||
|
|
||||||
var response []inbound.Handler
|
response := make([]inbound.Handler, len(m.untaggedHandlers))
|
||||||
copy(m.untaggedHandler, response)
|
copy(response, m.untaggedHandlers)
|
||||||
|
|
||||||
for _, v := range m.taggedHandlers {
|
for _, v := range m.taggedHandlers {
|
||||||
response = append(response, v)
|
response = append(response, v)
|
||||||
@@ -117,7 +117,7 @@ func (m *Manager) Start() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, handler := range m.untaggedHandler {
|
for _, handler := range m.untaggedHandlers {
|
||||||
if err := handler.Start(); err != nil {
|
if err := handler.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -138,7 +138,7 @@ func (m *Manager) Close() error {
|
|||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, handler := range m.untaggedHandler {
|
for _, handler := range m.untaggedHandlers {
|
||||||
if err := handler.Close(); err != nil {
|
if err := handler.Close(); err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
|
@@ -161,6 +161,7 @@ type udpConn struct {
|
|||||||
uplink stats.Counter
|
uplink stats.Counter
|
||||||
downlink stats.Counter
|
downlink stats.Counter
|
||||||
inactive bool
|
inactive bool
|
||||||
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *udpConn) setInactive() {
|
func (c *udpConn) setInactive() {
|
||||||
@@ -203,6 +204,9 @@ func (c *udpConn) Write(buf []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *udpConn) Close() error {
|
func (c *udpConn) Close() error {
|
||||||
|
if c.cancel != nil {
|
||||||
|
c.cancel()
|
||||||
|
}
|
||||||
common.Must(c.done.Close())
|
common.Must(c.done.Close())
|
||||||
common.Must(common.Close(c.writer))
|
common.Must(common.Close(c.writer))
|
||||||
return nil
|
return nil
|
||||||
@@ -259,6 +263,7 @@ func (w *udpWorker) getConnection(id connID) (*udpConn, bool) {
|
|||||||
defer w.Unlock()
|
defer w.Unlock()
|
||||||
|
|
||||||
if conn, found := w.activeConn[id]; found && !conn.done.Done() {
|
if conn, found := w.activeConn[id]; found && !conn.done.Done() {
|
||||||
|
conn.updateActivity()
|
||||||
return conn, true
|
return conn, true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +311,8 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
|
|||||||
common.Must(w.checker.Start())
|
common.Must(w.checker.Start())
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
ctx := w.ctx
|
ctx, cancel := context.WithCancel(w.ctx)
|
||||||
|
conn.cancel = cancel
|
||||||
sid := session.NewID()
|
sid := session.NewID()
|
||||||
ctx = c.ContextWithID(ctx, sid)
|
ctx = c.ContextWithID(ctx, sid)
|
||||||
|
|
||||||
|
@@ -150,8 +150,8 @@ func (m *Manager) ListHandlers(ctx context.Context) []outbound.Handler {
|
|||||||
m.access.RLock()
|
m.access.RLock()
|
||||||
defer m.access.RUnlock()
|
defer m.access.RUnlock()
|
||||||
|
|
||||||
var response []outbound.Handler
|
response := make([]outbound.Handler, len(m.untaggedHandlers))
|
||||||
copy(m.untaggedHandlers, response)
|
copy(response, m.untaggedHandlers)
|
||||||
|
|
||||||
for _, v := range m.taggedHandler {
|
for _, v := range m.taggedHandler {
|
||||||
response = append(response, v)
|
response = append(response, v)
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
func (c *Control) FillInRandom() {
|
func (c *Control) FillInRandom() {
|
||||||
randomLength := dice.Roll(64)
|
randomLength := dice.Roll(64)
|
||||||
|
randomLength++
|
||||||
c.Random = make([]byte, randomLength)
|
c.Random = make([]byte, randomLength)
|
||||||
io.ReadFull(rand.Reader, c.Random)
|
io.ReadFull(rand.Reader, c.Random)
|
||||||
}
|
}
|
||||||
|
@@ -170,7 +170,7 @@ func (p *StaticMuxPicker) PickAvailable() (*mux.ClientWorker, error) {
|
|||||||
if w.draining {
|
if w.draining {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if w.client.Closed() {
|
if w.IsFull() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if w.client.ActiveConnections() < minConn {
|
if w.client.ActiveConnections() < minConn {
|
||||||
@@ -211,6 +211,7 @@ type PortalWorker struct {
|
|||||||
writer buf.Writer
|
writer buf.Writer
|
||||||
reader buf.Reader
|
reader buf.Reader
|
||||||
draining bool
|
draining bool
|
||||||
|
counter uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) {
|
func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) {
|
||||||
@@ -244,7 +245,7 @@ func NewPortalWorker(client *mux.ClientWorker) (*PortalWorker, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *PortalWorker) heartbeat() error {
|
func (w *PortalWorker) heartbeat() error {
|
||||||
if w.client.Closed() {
|
if w.Closed() {
|
||||||
return errors.New("client worker stopped")
|
return errors.New("client worker stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,17 +261,22 @@ func (w *PortalWorker) heartbeat() error {
|
|||||||
msg.State = Control_DRAIN
|
msg.State = Control_DRAIN
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
w.client.GetTimer().Reset(time.Second * 16)
|
||||||
common.Close(w.writer)
|
common.Close(w.writer)
|
||||||
common.Interrupt(w.reader)
|
common.Interrupt(w.reader)
|
||||||
w.writer = nil
|
w.writer = nil
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w.counter = (w.counter + 1) % 5
|
||||||
|
if w.draining || w.counter == 1 {
|
||||||
b, err := proto.Marshal(msg)
|
b, err := proto.Marshal(msg)
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
mb := buf.MergeBytes(nil, b)
|
mb := buf.MergeBytes(nil, b)
|
||||||
return w.writer.WriteMultiBuffer(mb)
|
return w.writer.WriteMultiBuffer(mb)
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (w *PortalWorker) IsFull() bool {
|
func (w *PortalWorker) IsFull() bool {
|
||||||
return w.client.IsFull()
|
return w.client.IsFull()
|
||||||
|
@@ -12,6 +12,8 @@ import (
|
|||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
feature_stats "github.com/xtls/xray-core/features/stats"
|
feature_stats "github.com/xtls/xray-core/features/stats"
|
||||||
grpc "google.golang.org/grpc"
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
// statsServer is an implementation of StatsService.
|
// statsServer is an implementation of StatsService.
|
||||||
@@ -30,7 +32,7 @@ func NewStatsServer(manager feature_stats.Manager) StatsServiceServer {
|
|||||||
func (s *statsServer) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
|
func (s *statsServer) GetStats(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
|
||||||
c := s.stats.GetCounter(request.Name)
|
c := s.stats.GetCounter(request.Name)
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return nil, errors.New(request.Name, " not found.")
|
return nil, status.Error(codes.NotFound, request.Name+" not found.")
|
||||||
}
|
}
|
||||||
var value int64
|
var value int64
|
||||||
if request.Reset_ {
|
if request.Reset_ {
|
||||||
@@ -49,7 +51,7 @@ func (s *statsServer) GetStats(ctx context.Context, request *GetStatsRequest) (*
|
|||||||
func (s *statsServer) GetStatsOnline(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
|
func (s *statsServer) GetStatsOnline(ctx context.Context, request *GetStatsRequest) (*GetStatsResponse, error) {
|
||||||
c := s.stats.GetOnlineMap(request.Name)
|
c := s.stats.GetOnlineMap(request.Name)
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return nil, errors.New(request.Name, " not found.")
|
return nil, status.Error(codes.NotFound, request.Name+" not found.")
|
||||||
}
|
}
|
||||||
value := int64(c.Count())
|
value := int64(c.Count())
|
||||||
return &GetStatsResponse{
|
return &GetStatsResponse{
|
||||||
@@ -64,7 +66,7 @@ func (s *statsServer) GetStatsOnlineIpList(ctx context.Context, request *GetStat
|
|||||||
c := s.stats.GetOnlineMap(request.Name)
|
c := s.stats.GetOnlineMap(request.Name)
|
||||||
|
|
||||||
if c == nil {
|
if c == nil {
|
||||||
return nil, errors.New(request.Name, " not found.")
|
return nil, status.Error(codes.NotFound, request.Name+" not found.")
|
||||||
}
|
}
|
||||||
|
|
||||||
ips := make(map[string]int64)
|
ips := make(map[string]int64)
|
||||||
|
152
app/version/config.pb.go
Normal file
152
app/version/config.pb.go
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.35.1
|
||||||
|
// protoc v5.28.2
|
||||||
|
// source: app/version/config.proto
|
||||||
|
|
||||||
|
package version
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
CoreVersion string `protobuf:"bytes,1,opt,name=core_version,json=coreVersion,proto3" json:"core_version,omitempty"`
|
||||||
|
MinVersion string `protobuf:"bytes,2,opt,name=min_version,json=minVersion,proto3" json:"min_version,omitempty"`
|
||||||
|
MaxVersion string `protobuf:"bytes,3,opt,name=max_version,json=maxVersion,proto3" json:"max_version,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) Reset() {
|
||||||
|
*x = Config{}
|
||||||
|
mi := &file_app_version_config_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Config) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *Config) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_app_version_config_proto_msgTypes[0]
|
||||||
|
if x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use Config.ProtoReflect.Descriptor instead.
|
||||||
|
func (*Config) Descriptor() ([]byte, []int) {
|
||||||
|
return file_app_version_config_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetCoreVersion() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.CoreVersion
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetMinVersion() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.MinVersion
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetMaxVersion() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.MaxVersion
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_app_version_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_app_version_config_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x18, 0x61, 0x70, 0x70, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2f, 0x63, 0x6f,
|
||||||
|
0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x78, 0x72, 0x61, 0x79,
|
||||||
|
0x2e, 0x61, 0x70, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x6d, 0x0a, 0x06,
|
||||||
|
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x76,
|
||||||
|
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f,
|
||||||
|
0x72, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e,
|
||||||
|
0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
|
||||||
|
0x6d, 0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61,
|
||||||
|
0x78, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
|
0x0a, 0x6d, 0x61, 0x78, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x52, 0x0a, 0x14, 0x63,
|
||||||
|
0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x76, 0x65, 0x72, 0x73,
|
||||||
|
0x69, 0x6f, 0x6e, 0x50, 0x01, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
|
||||||
|
0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
|
||||||
|
0x2f, 0x61, 0x70, 0x70, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0xaa, 0x02, 0x10, 0x58,
|
||||||
|
0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x62,
|
||||||
|
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_app_version_config_proto_rawDescOnce sync.Once
|
||||||
|
file_app_version_config_proto_rawDescData = file_app_version_config_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_app_version_config_proto_rawDescGZIP() []byte {
|
||||||
|
file_app_version_config_proto_rawDescOnce.Do(func() {
|
||||||
|
file_app_version_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_app_version_config_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_app_version_config_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_app_version_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||||
|
var file_app_version_config_proto_goTypes = []any{
|
||||||
|
(*Config)(nil), // 0: xray.app.version.Config
|
||||||
|
}
|
||||||
|
var file_app_version_config_proto_depIdxs = []int32{
|
||||||
|
0, // [0:0] is the sub-list for method output_type
|
||||||
|
0, // [0:0] is the sub-list for method input_type
|
||||||
|
0, // [0:0] is the sub-list for extension type_name
|
||||||
|
0, // [0:0] is the sub-list for extension extendee
|
||||||
|
0, // [0:0] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_app_version_config_proto_init() }
|
||||||
|
func file_app_version_config_proto_init() {
|
||||||
|
if File_app_version_config_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_app_version_config_proto_rawDesc,
|
||||||
|
NumEnums: 0,
|
||||||
|
NumMessages: 1,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_app_version_config_proto_goTypes,
|
||||||
|
DependencyIndexes: file_app_version_config_proto_depIdxs,
|
||||||
|
MessageInfos: file_app_version_config_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_app_version_config_proto = out.File
|
||||||
|
file_app_version_config_proto_rawDesc = nil
|
||||||
|
file_app_version_config_proto_goTypes = nil
|
||||||
|
file_app_version_config_proto_depIdxs = nil
|
||||||
|
}
|
14
app/version/config.proto
Normal file
14
app/version/config.proto
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package xray.app.version;
|
||||||
|
option csharp_namespace = "Xray.App.Version";
|
||||||
|
option go_package = "github.com/xtls/xray-core/app/version";
|
||||||
|
option java_package = "com.xray.app.version";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
|
||||||
|
message Config {
|
||||||
|
string core_version = 1;
|
||||||
|
string min_version = 2;
|
||||||
|
string max_version = 3;
|
||||||
|
}
|
77
app/version/version.go
Normal file
77
app/version/version.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package version
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Version struct {
|
||||||
|
config *Config
|
||||||
|
ctx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context, config *Config) (*Version, error) {
|
||||||
|
if config.MinVersion != "" {
|
||||||
|
result, err := compareVersions(config.MinVersion, config.CoreVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if result > 0 {
|
||||||
|
return nil, errors.New("this config must be run on version ", config.MinVersion, " or higher")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if config.MaxVersion != "" {
|
||||||
|
result, err := compareVersions(config.MaxVersion, config.CoreVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if result < 0 {
|
||||||
|
return nil, errors.New("this config should be run on version ", config.MaxVersion, " or lower")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Version{config: config, ctx: ctx}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareVersions(v1, v2 string) (int, error) {
|
||||||
|
// Split version strings into components
|
||||||
|
v1Parts := strings.Split(v1, ".")
|
||||||
|
v2Parts := strings.Split(v2, ".")
|
||||||
|
|
||||||
|
// Pad shorter versions with zeros
|
||||||
|
for len(v1Parts) < len(v2Parts) {
|
||||||
|
v1Parts = append(v1Parts, "0")
|
||||||
|
}
|
||||||
|
for len(v2Parts) < len(v1Parts) {
|
||||||
|
v2Parts = append(v2Parts, "0")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare each part
|
||||||
|
for i := 0; i < len(v1Parts); i++ {
|
||||||
|
// Convert parts to integers
|
||||||
|
n1, err := strconv.Atoi(v1Parts[i])
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.New("invalid version component ", v1Parts[i], " in ", v1)
|
||||||
|
}
|
||||||
|
n2, err := strconv.Atoi(v2Parts[i])
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.New("invalid version component ", v2Parts[i], " in ", v2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n1 < n2 {
|
||||||
|
return -1, nil // v1 < v2
|
||||||
|
}
|
||||||
|
if n1 > n2 {
|
||||||
|
return 1, nil // v1 > v2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, nil // v1 == v2
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
|
||||||
|
return New(ctx, config.(*Config))
|
||||||
|
}))
|
||||||
|
}
|
@@ -173,6 +173,7 @@ type ClientWorker struct {
|
|||||||
sessionManager *SessionManager
|
sessionManager *SessionManager
|
||||||
link transport.Link
|
link transport.Link
|
||||||
done *done.Instance
|
done *done.Instance
|
||||||
|
timer *time.Ticker
|
||||||
strategy ClientStrategy
|
strategy ClientStrategy
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,6 +188,7 @@ func NewClientWorker(stream transport.Link, s ClientStrategy) (*ClientWorker, er
|
|||||||
sessionManager: NewSessionManager(),
|
sessionManager: NewSessionManager(),
|
||||||
link: stream,
|
link: stream,
|
||||||
done: done.New(),
|
done: done.New(),
|
||||||
|
timer: time.NewTicker(time.Second * 16),
|
||||||
strategy: s,
|
strategy: s,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,9 +211,12 @@ func (m *ClientWorker) Closed() bool {
|
|||||||
return m.done.Done()
|
return m.done.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *ClientWorker) GetTimer() *time.Ticker {
|
||||||
|
return m.timer
|
||||||
|
}
|
||||||
|
|
||||||
func (m *ClientWorker) monitor() {
|
func (m *ClientWorker) monitor() {
|
||||||
timer := time.NewTicker(time.Second * 16)
|
defer m.timer.Stop()
|
||||||
defer timer.Stop()
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -220,7 +225,7 @@ func (m *ClientWorker) monitor() {
|
|||||||
common.Close(m.link.Writer)
|
common.Close(m.link.Writer)
|
||||||
common.Interrupt(m.link.Reader)
|
common.Interrupt(m.link.Reader)
|
||||||
return
|
return
|
||||||
case <-timer.C:
|
case <-m.timer.C:
|
||||||
size := m.sessionManager.Size()
|
size := m.sessionManager.Size()
|
||||||
if size == 0 && m.sessionManager.CloseIfNoSession() {
|
if size == 0 && m.sessionManager.CloseIfNoSession() {
|
||||||
common.Must(m.done.Close())
|
common.Must(m.done.Close())
|
||||||
@@ -276,6 +281,8 @@ func (m *ClientWorker) IsClosing() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsFull returns true if this ClientWorker is unable to accept more connections.
|
||||||
|
// it might be because it is closing, or the number of connections has reached the limit.
|
||||||
func (m *ClientWorker) IsFull() bool {
|
func (m *ClientWorker) IsFull() bool {
|
||||||
if m.IsClosing() || m.Closed() {
|
if m.IsClosing() || m.Closed() {
|
||||||
return true
|
return true
|
||||||
@@ -289,12 +296,12 @@ func (m *ClientWorker) IsFull() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *ClientWorker) Dispatch(ctx context.Context, link *transport.Link) bool {
|
func (m *ClientWorker) Dispatch(ctx context.Context, link *transport.Link) bool {
|
||||||
if m.IsFull() || m.Closed() {
|
if m.IsFull() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
sm := m.sessionManager
|
sm := m.sessionManager
|
||||||
s := sm.Allocate()
|
s := sm.Allocate(&m.strategy)
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@@ -201,11 +201,12 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
|
|||||||
transferType: protocol.TransferTypePacket,
|
transferType: protocol.TransferTypePacket,
|
||||||
XUDP: x,
|
XUDP: x,
|
||||||
}
|
}
|
||||||
go handle(ctx, x.Mux, w.link.Writer)
|
|
||||||
x.Status = Active
|
x.Status = Active
|
||||||
if !w.sessionManager.Add(x.Mux) {
|
if !w.sessionManager.Add(x.Mux) {
|
||||||
x.Mux.Close(false)
|
x.Mux.Close(false)
|
||||||
|
return errors.New("failed to add new session")
|
||||||
}
|
}
|
||||||
|
go handle(ctx, x.Mux, w.link.Writer)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,18 +227,23 @@ func (w *ServerWorker) handleStatusNew(ctx context.Context, meta *FrameMetadata,
|
|||||||
if meta.Target.Network == net.Network_UDP {
|
if meta.Target.Network == net.Network_UDP {
|
||||||
s.transferType = protocol.TransferTypePacket
|
s.transferType = protocol.TransferTypePacket
|
||||||
}
|
}
|
||||||
w.sessionManager.Add(s)
|
if !w.sessionManager.Add(s) {
|
||||||
|
s.Close(false)
|
||||||
|
return errors.New("failed to add new session")
|
||||||
|
}
|
||||||
go handle(ctx, s, w.link.Writer)
|
go handle(ctx, s, w.link.Writer)
|
||||||
if !meta.Option.Has(OptionData) {
|
if !meta.Option.Has(OptionData) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rr := s.NewReader(reader, &meta.Target)
|
rr := s.NewReader(reader, &meta.Target)
|
||||||
if err := buf.Copy(rr, s.output); err != nil {
|
err = buf.Copy(rr, s.output)
|
||||||
buf.Copy(rr, buf.Discard)
|
|
||||||
return s.Close(false)
|
if err != nil && buf.IsWriteError(err) {
|
||||||
|
s.Close(false)
|
||||||
|
return buf.Copy(rr, buf.Discard)
|
||||||
}
|
}
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
func (w *ServerWorker) handleStatusKeep(meta *FrameMetadata, reader *buf.BufferedReader) error {
|
||||||
@@ -304,10 +310,11 @@ func (w *ServerWorker) handleFrame(ctx context.Context, reader *buf.BufferedRead
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *ServerWorker) run(ctx context.Context) {
|
func (w *ServerWorker) run(ctx context.Context) {
|
||||||
input := w.link.Reader
|
reader := &buf.BufferedReader{Reader: w.link.Reader}
|
||||||
reader := &buf.BufferedReader{Reader: input}
|
|
||||||
|
|
||||||
defer w.sessionManager.Close()
|
defer w.sessionManager.Close()
|
||||||
|
defer common.Close(w.link.Writer)
|
||||||
|
defer common.Interrupt(w.link.Reader)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -318,7 +325,6 @@ func (w *ServerWorker) run(ctx context.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Cause(err) != io.EOF {
|
if errors.Cause(err) != io.EOF {
|
||||||
errors.LogInfoInner(ctx, err, "unexpected EOF")
|
errors.LogInfoInner(ctx, err, "unexpected EOF")
|
||||||
common.Interrupt(input)
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -50,11 +50,14 @@ func (m *SessionManager) Count() int {
|
|||||||
return int(m.count)
|
return int(m.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *SessionManager) Allocate() *Session {
|
func (m *SessionManager) Allocate(Strategy *ClientStrategy) *Session {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
if m.closed {
|
MaxConcurrency := int(Strategy.MaxConcurrency)
|
||||||
|
MaxConnection := uint16(Strategy.MaxConnection)
|
||||||
|
|
||||||
|
if m.closed || (MaxConcurrency > 0 && len(m.sessions) >= MaxConcurrency) || (MaxConnection > 0 && m.count >= MaxConnection) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@ import (
|
|||||||
func TestSessionManagerAdd(t *testing.T) {
|
func TestSessionManagerAdd(t *testing.T) {
|
||||||
m := NewSessionManager()
|
m := NewSessionManager()
|
||||||
|
|
||||||
s := m.Allocate()
|
s := m.Allocate(&ClientStrategy{})
|
||||||
if s.ID != 1 {
|
if s.ID != 1 {
|
||||||
t.Error("id: ", s.ID)
|
t.Error("id: ", s.ID)
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@ func TestSessionManagerAdd(t *testing.T) {
|
|||||||
t.Error("size: ", m.Size())
|
t.Error("size: ", m.Size())
|
||||||
}
|
}
|
||||||
|
|
||||||
s = m.Allocate()
|
s = m.Allocate(&ClientStrategy{})
|
||||||
if s.ID != 2 {
|
if s.ID != 2 {
|
||||||
t.Error("id: ", s.ID)
|
t.Error("id: ", s.ID)
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@ func TestSessionManagerAdd(t *testing.T) {
|
|||||||
|
|
||||||
func TestSessionManagerClose(t *testing.T) {
|
func TestSessionManagerClose(t *testing.T) {
|
||||||
m := NewSessionManager()
|
m := NewSessionManager()
|
||||||
s := m.Allocate()
|
s := m.Allocate(&ClientStrategy{})
|
||||||
|
|
||||||
if m.CloseIfNoSession() {
|
if m.CloseIfNoSession() {
|
||||||
t.Error("able to close")
|
t.Error("able to close")
|
||||||
|
@@ -67,9 +67,9 @@ func (t *ActivityTimer) SetTimeout(timeout time.Duration) {
|
|||||||
t.checkTask.Close()
|
t.checkTask.Close()
|
||||||
}
|
}
|
||||||
t.checkTask = checkTask
|
t.checkTask = checkTask
|
||||||
t.Unlock()
|
|
||||||
t.Update()
|
t.Update()
|
||||||
common.Must(checkTask.Start())
|
common.Must(checkTask.Start())
|
||||||
|
t.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer {
|
func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer {
|
||||||
|
112
common/utils/typed_sync_map.go
Normal file
112
common/utils/typed_sync_map.go
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TypedSyncMap is a wrapper of sync.Map that provides type-safe for keys and values.
|
||||||
|
// No need to use type assertions every time, so you can have more time to enjoy other things like GochiUsa
|
||||||
|
// If sync.Map methods returned nil, it will return the zero value of the type V.
|
||||||
|
type TypedSyncMap[K, V any] struct {
|
||||||
|
syncMap *sync.Map
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTypedSyncMap creates a new TypedSyncMap
|
||||||
|
// K is key type, V is value type
|
||||||
|
// It is recommended to use pointer types for V because sync.Map might return nil
|
||||||
|
// If sync.Map methods really returned nil, it will return the zero value of the type V
|
||||||
|
func NewTypedSyncMap[K any, V any]() *TypedSyncMap[K, V] {
|
||||||
|
return &TypedSyncMap[K, V]{
|
||||||
|
syncMap: &sync.Map{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear deletes all the entries, resulting in an empty Map.
|
||||||
|
func (m *TypedSyncMap[K, V]) Clear() {
|
||||||
|
m.syncMap.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndDelete deletes the entry for key if its value is equal to old.
|
||||||
|
// The old value must be of a comparable type.
|
||||||
|
//
|
||||||
|
// If there is no current value for key in the map, CompareAndDelete
|
||||||
|
// returns false (even if the old value is the nil interface value).
|
||||||
|
func (m *TypedSyncMap[K, V]) CompareAndDelete(key K, old V) (deleted bool) {
|
||||||
|
return m.syncMap.CompareAndDelete(key, old)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareAndSwap swaps the old and new values for key
|
||||||
|
// if the value stored in the map is equal to old.
|
||||||
|
// The old value must be of a comparable type.
|
||||||
|
func (m *TypedSyncMap[K, V]) CompareAndSwap(key K, old V, new V) (swapped bool) {
|
||||||
|
return m.syncMap.CompareAndSwap(key, old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete deletes the value for a key.
|
||||||
|
func (m *TypedSyncMap[K, V]) Delete(key K) {
|
||||||
|
m.syncMap.Delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load returns the value stored in the map for a key, or nil if no
|
||||||
|
// value is present.
|
||||||
|
// The ok result indicates whether value was found in the map.
|
||||||
|
func (m *TypedSyncMap[K, V]) Load(key K) (value V, ok bool) {
|
||||||
|
anyValue, ok := m.syncMap.Load(key)
|
||||||
|
// anyValue might be nil
|
||||||
|
if anyValue != nil {
|
||||||
|
value = anyValue.(V)
|
||||||
|
}
|
||||||
|
return value, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadAndDelete deletes the value for a key, returning the previous value if any.
|
||||||
|
// The loaded result reports whether the key was present.
|
||||||
|
func (m *TypedSyncMap[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
|
||||||
|
anyValue, loaded := m.syncMap.LoadAndDelete(key)
|
||||||
|
if anyValue != nil {
|
||||||
|
value = anyValue.(V)
|
||||||
|
}
|
||||||
|
return value, loaded
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadOrStore returns the existing value for the key if present.
|
||||||
|
// Otherwise, it stores and returns the given value.
|
||||||
|
// The loaded result is true if the value was loaded, false if stored.
|
||||||
|
func (m *TypedSyncMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
|
||||||
|
anyActual, loaded := m.syncMap.LoadOrStore(key, value)
|
||||||
|
if anyActual != nil {
|
||||||
|
actual = anyActual.(V)
|
||||||
|
}
|
||||||
|
return actual, loaded
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range calls f sequentially for each key and value present in the map.
|
||||||
|
// If f returns false, range stops the iteration.
|
||||||
|
//
|
||||||
|
// Range does not necessarily correspond to any consistent snapshot of the Map's
|
||||||
|
// contents: no key will be visited more than once, but if the value for any key
|
||||||
|
// is stored or deleted concurrently (including by f), Range may reflect any
|
||||||
|
// mapping for that key from any point during the Range call. Range does not
|
||||||
|
// block other methods on the receiver; even f itself may call any method on m.
|
||||||
|
//
|
||||||
|
// Range may be O(N) with the number of elements in the map even if f returns
|
||||||
|
// false after a constant number of calls.
|
||||||
|
func (m *TypedSyncMap[K, V]) Range(f func(key K, value V) bool) {
|
||||||
|
m.syncMap.Range(func(key, value any) bool {
|
||||||
|
return f(key.(K), value.(V))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store sets the value for a key.
|
||||||
|
func (m *TypedSyncMap[K, V]) Store(key K, value V) {
|
||||||
|
m.syncMap.Store(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap swaps the value for a key and returns the previous value if any. The loaded result reports whether the key was present.
|
||||||
|
func (m *TypedSyncMap[K, V]) Swap(key K, value V) (previous V, loaded bool) {
|
||||||
|
anyPrevious, loaded := m.syncMap.Swap(key, value)
|
||||||
|
if anyPrevious != nil {
|
||||||
|
previous = anyPrevious.(V)
|
||||||
|
}
|
||||||
|
return previous, loaded
|
||||||
|
}
|
@@ -18,8 +18,8 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
Version_x byte = 25
|
Version_x byte = 25
|
||||||
Version_y byte = 6
|
Version_y byte = 8
|
||||||
Version_z byte = 8
|
Version_z byte = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -42,6 +42,24 @@ func (e RCodeError) Error() string {
|
|||||||
return serial.Concat("rcode: ", uint16(e))
|
return serial.Concat("rcode: ", uint16(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (RCodeError) IP() net.IP {
|
||||||
|
panic("Calling IP() on a RCodeError.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (RCodeError) Domain() string {
|
||||||
|
panic("Calling Domain() on a RCodeError.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (RCodeError) Family() net.AddressFamily {
|
||||||
|
panic("Calling Family() on a RCodeError.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e RCodeError) String() string {
|
||||||
|
return e.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ net.Address = (*RCodeError)(nil)
|
||||||
|
|
||||||
func RCodeFromError(err error) uint16 {
|
func RCodeFromError(err error) uint16 {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return 0
|
return 0
|
||||||
|
37
go.mod
37
go.mod
@@ -3,31 +3,30 @@ module github.com/xtls/xray-core
|
|||||||
go 1.24
|
go 1.24
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0
|
|
||||||
github.com/cloudflare/circl v1.6.1
|
github.com/cloudflare/circl v1.6.1
|
||||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344
|
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344
|
||||||
github.com/golang/mock v1.7.0-rc.1
|
github.com/golang/mock v1.7.0-rc.1
|
||||||
github.com/google/go-cmp v0.7.0
|
github.com/google/go-cmp v0.7.0
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
github.com/miekg/dns v1.1.66
|
github.com/miekg/dns v1.1.67
|
||||||
github.com/pelletier/go-toml v1.9.5
|
github.com/pelletier/go-toml v1.9.5
|
||||||
github.com/pires/go-proxyproto v0.8.1
|
github.com/pires/go-proxyproto v0.8.1
|
||||||
github.com/quic-go/quic-go v0.52.0
|
github.com/quic-go/quic-go v0.54.0
|
||||||
github.com/refraction-networking/utls v1.7.3
|
github.com/refraction-networking/utls v1.8.0
|
||||||
github.com/sagernet/sing v0.5.1
|
github.com/sagernet/sing v0.5.1
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7
|
github.com/sagernet/sing-shadowsocks v0.2.7
|
||||||
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771
|
github.com/seiflotfy/cuckoofilter v0.0.0-20240715131351-a2f2c23f1771
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
|
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e
|
||||||
github.com/vishvananda/netlink v1.3.1
|
github.com/vishvananda/netlink v1.3.1
|
||||||
github.com/xtls/reality v0.0.0-20250608132114-50752aec6bfb
|
github.com/xtls/reality v0.0.0-20250725142056-5b52a03d4fb7
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
|
||||||
golang.org/x/crypto v0.39.0
|
golang.org/x/crypto v0.40.0
|
||||||
golang.org/x/net v0.41.0
|
golang.org/x/net v0.42.0
|
||||||
golang.org/x/sync v0.15.0
|
golang.org/x/sync v0.16.0
|
||||||
golang.org/x/sys v0.33.0
|
golang.org/x/sys v0.34.0
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173
|
||||||
google.golang.org/grpc v1.73.0
|
google.golang.org/grpc v1.74.2
|
||||||
google.golang.org/protobuf v1.36.6
|
google.golang.org/protobuf v1.36.6
|
||||||
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5
|
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5
|
||||||
h12.io/socks v1.0.3
|
h12.io/socks v1.0.3
|
||||||
@@ -35,27 +34,25 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
github.com/andybalholm/brotli v1.0.6 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect
|
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
|
||||||
github.com/google/btree v1.1.2 // indirect
|
github.com/google/btree v1.1.2 // indirect
|
||||||
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba // indirect
|
|
||||||
github.com/juju/ratelimit v1.0.2 // indirect
|
github.com/juju/ratelimit v1.0.2 // indirect
|
||||||
github.com/klauspost/compress v1.17.8 // indirect
|
github.com/klauspost/compress v1.17.4 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.19.0 // indirect
|
github.com/kr/pretty v0.3.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/quic-go/qpack v0.5.1 // indirect
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||||
github.com/vishvananda/netns v0.0.5 // indirect
|
github.com/vishvananda/netns v0.0.5 // indirect
|
||||||
go.uber.org/mock v0.5.0 // indirect
|
go.uber.org/mock v0.5.0 // indirect
|
||||||
golang.org/x/mod v0.25.0 // indirect
|
golang.org/x/mod v0.25.0 // indirect
|
||||||
golang.org/x/text v0.26.0 // indirect
|
golang.org/x/text v0.27.0 // indirect
|
||||||
golang.org/x/time v0.7.0 // indirect
|
golang.org/x/time v0.7.0 // indirect
|
||||||
golang.org/x/tools v0.33.0 // indirect
|
golang.org/x/tools v0.34.0 // indirect
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
109
go.sum
109
go.sum
@@ -1,23 +1,19 @@
|
|||||||
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0 h1:Wo41lDOevRJSGpevP+8Pk5bANX7fJacO2w04aqLiC5I=
|
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
|
||||||
github.com/OmarTariq612/goech v0.0.0-20240405204721-8e2e1dafd3a0/go.mod h1:FVGavL/QEBQDcBpr3fAojoK17xX5k9bicBphrOpP7uM=
|
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
|
||||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
|
||||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165 h1:BS21ZUJ/B5X2UVUbczfmdWH7GapPWAhxcMsDnjJTU1E=
|
||||||
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
github.com/dgryski/go-metro v0.0.0-20200812162917-85c65e2d0165/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
||||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=
|
|
||||||
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
|
|
||||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
|
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344 h1:Arcl6UOIS/kgO2nW3A65HN+7CMjSDP/gofXL4CZt1V4=
|
||||||
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
github.com/ghodss/yaml v1.0.1-0.20220118164431-d8423dcdf344/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
|
||||||
github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
|
github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
|
||||||
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
|
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
@@ -26,8 +22,6 @@ github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
|||||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JTNM+zJrgIbnyCKX/wdlyPufP5g=
|
|
||||||
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
@@ -36,32 +30,35 @@ github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364 h1:5XxdakFhqd9dnXoA
|
|||||||
github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c=
|
github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c=
|
||||||
github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
|
github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI=
|
||||||
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
|
github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
|
||||||
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||||
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
|
github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0=
|
||||||
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
|
github.com/miekg/dns v1.1.67/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
|
||||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
|
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=
|
||||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||||
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
|
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
|
||||||
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||||
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA=
|
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
|
||||||
github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
|
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||||
github.com/refraction-networking/utls v1.7.3 h1:L0WRhHY7Oq1T0zkdzVZMR6zWZv+sXbHB9zcuvsAEqCo=
|
github.com/refraction-networking/utls v1.8.0 h1:L38krhiTAyj9EeiQQa2sg+hYb4qwLCqdMcpZrRfbONE=
|
||||||
github.com/refraction-networking/utls v1.7.3/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ=
|
github.com/refraction-networking/utls v1.8.0/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
|
||||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/sagernet/sing v0.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y=
|
github.com/sagernet/sing v0.5.1 h1:mhL/MZVq0TjuvHcpYcFtmSD1BFOxZ/+8ofbNZcg1k1Y=
|
||||||
github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
github.com/sagernet/sing v0.5.1/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak=
|
||||||
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
|
||||||
@@ -78,64 +75,63 @@ github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW
|
|||||||
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
|
||||||
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
|
||||||
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
|
||||||
github.com/xtls/reality v0.0.0-20250608132114-50752aec6bfb h1:X6ziJCMsFF8Ac/0F3W7+UbFdHZTu+r5nZ/smksHVxNQ=
|
github.com/xtls/reality v0.0.0-20250725142056-5b52a03d4fb7 h1:Ript0vN+nSO33+Vj4n0mgNY5M+oOxFQJdrJ1VnwTBO0=
|
||||||
github.com/xtls/reality v0.0.0-20250608132114-50752aec6bfb/go.mod h1:yD47RN65bDLZgyHWMfFDiqlzrq4usDMt/Xzsk6tMbhw=
|
github.com/xtls/reality v0.0.0-20250725142056-5b52a03d4fb7/go.mod h1:XxvnCCgBee4WWE0bc4E+a7wbk8gkJ/rS0vNVNtC5qp0=
|
||||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
|
||||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
|
||||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
|
||||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
|
||||||
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@@ -143,14 +139,15 @@ golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeu
|
|||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 h1:/jFs0duh4rdb8uIfPMv78iAJGcPKDeqAFnaLBropIC4=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173/go.mod h1:tkCQ4FQXmpAgYVh++1cq16/dH4QJtmvpRv19DWGAHSA=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
|
||||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
|
||||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
@@ -30,7 +30,7 @@ func (c *DNSOutboundConfig) Build() (proto.Message, error) {
|
|||||||
switch c.NonIPQuery {
|
switch c.NonIPQuery {
|
||||||
case "":
|
case "":
|
||||||
c.NonIPQuery = "drop"
|
c.NonIPQuery = "drop"
|
||||||
case "drop", "skip":
|
case "drop", "skip", "reject":
|
||||||
default:
|
default:
|
||||||
return nil, errors.New(`unknown "nonIPQuery": `, c.NonIPQuery)
|
return nil, errors.New(`unknown "nonIPQuery": `, c.NonIPQuery)
|
||||||
}
|
}
|
||||||
|
@@ -1,26 +1,35 @@
|
|||||||
package conf
|
package conf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
"github.com/xtls/xray-core/proxy/dokodemo"
|
"github.com/xtls/xray-core/proxy/dokodemo"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DokodemoConfig struct {
|
type DokodemoConfig struct {
|
||||||
Host *Address `json:"address"`
|
Address *Address `json:"address"`
|
||||||
PortValue uint16 `json:"port"`
|
Port uint16 `json:"port"`
|
||||||
NetworkList *NetworkList `json:"network"`
|
PortMap map[string]string `json:"portMap"`
|
||||||
Redirect bool `json:"followRedirect"`
|
Network *NetworkList `json:"network"`
|
||||||
|
FollowRedirect bool `json:"followRedirect"`
|
||||||
UserLevel uint32 `json:"userLevel"`
|
UserLevel uint32 `json:"userLevel"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *DokodemoConfig) Build() (proto.Message, error) {
|
func (v *DokodemoConfig) Build() (proto.Message, error) {
|
||||||
config := new(dokodemo.Config)
|
config := new(dokodemo.Config)
|
||||||
if v.Host != nil {
|
if v.Address != nil {
|
||||||
config.Address = v.Host.Build()
|
config.Address = v.Address.Build()
|
||||||
}
|
}
|
||||||
config.Port = uint32(v.PortValue)
|
config.Port = uint32(v.Port)
|
||||||
config.Networks = v.NetworkList.Build()
|
config.PortMap = v.PortMap
|
||||||
config.FollowRedirect = v.Redirect
|
for _, v := range config.PortMap {
|
||||||
|
if _, _, err := net.SplitHostPort(v); err != nil {
|
||||||
|
return nil, errors.New("invalid portMap: ", v).Base(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.Networks = v.Network.Build()
|
||||||
|
config.FollowRedirect = v.FollowRedirect
|
||||||
config.UserLevel = v.UserLevel
|
config.UserLevel = v.UserLevel
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
@@ -2,6 +2,7 @@ package conf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/app/observatory/burst"
|
"github.com/xtls/xray-core/app/observatory/burst"
|
||||||
"github.com/xtls/xray-core/app/router"
|
"github.com/xtls/xray-core/app/router"
|
||||||
@@ -51,15 +52,23 @@ type healthCheckSettings struct {
|
|||||||
Interval duration.Duration `json:"interval"`
|
Interval duration.Duration `json:"interval"`
|
||||||
SamplingCount int `json:"sampling"`
|
SamplingCount int `json:"sampling"`
|
||||||
Timeout duration.Duration `json:"timeout"`
|
Timeout duration.Duration `json:"timeout"`
|
||||||
|
HttpMethod string `json:"httpMethod"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h healthCheckSettings) Build() (proto.Message, error) {
|
func (h healthCheckSettings) Build() (proto.Message, error) {
|
||||||
|
var httpMethod string
|
||||||
|
if h.HttpMethod == "" {
|
||||||
|
httpMethod = "HEAD"
|
||||||
|
} else {
|
||||||
|
httpMethod = strings.TrimSpace(h.HttpMethod)
|
||||||
|
}
|
||||||
return &burst.HealthPingConfig{
|
return &burst.HealthPingConfig{
|
||||||
Destination: h.Destination,
|
Destination: h.Destination,
|
||||||
Connectivity: h.Connectivity,
|
Connectivity: h.Connectivity,
|
||||||
Interval: int64(h.Interval),
|
Interval: int64(h.Interval),
|
||||||
Timeout: int64(h.Timeout),
|
Timeout: int64(h.Timeout),
|
||||||
SamplingCount: int32(h.SamplingCount),
|
SamplingCount: int32(h.SamplingCount),
|
||||||
|
HttpMethod: httpMethod,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -412,6 +412,10 @@ type TLSConfig struct {
|
|||||||
MasterKeyLog string `json:"masterKeyLog"`
|
MasterKeyLog string `json:"masterKeyLog"`
|
||||||
ServerNameToVerify string `json:"serverNameToVerify"`
|
ServerNameToVerify string `json:"serverNameToVerify"`
|
||||||
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
|
VerifyPeerCertInNames []string `json:"verifyPeerCertInNames"`
|
||||||
|
ECHServerKeys string `json:"echServerKeys"`
|
||||||
|
ECHConfigList string `json:"echConfigList"`
|
||||||
|
ECHForceQuery string `json:"echForceQuery"`
|
||||||
|
ECHSocketSettings *SocketConfig `json:"echSockopt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build implements Buildable.
|
// Build implements Buildable.
|
||||||
@@ -435,7 +439,7 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
if len(config.NextProtocol) > 1 {
|
if len(config.NextProtocol) > 1 {
|
||||||
for _, p := range config.NextProtocol {
|
for _, p := range config.NextProtocol {
|
||||||
if tcp.IsFromMitm(p) {
|
if tls.IsFromMitm(p) {
|
||||||
return nil, errors.New(`only one element is allowed in "alpn" when using "fromMitm" in it`)
|
return nil, errors.New(`only one element is allowed in "alpn" when using "fromMitm" in it`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -483,6 +487,29 @@ func (c *TLSConfig) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
config.VerifyPeerCertInNames = c.VerifyPeerCertInNames
|
config.VerifyPeerCertInNames = c.VerifyPeerCertInNames
|
||||||
|
|
||||||
|
if c.ECHServerKeys != "" {
|
||||||
|
EchPrivateKey, err := base64.StdEncoding.DecodeString(c.ECHServerKeys)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("invalid ECH Config", c.ECHServerKeys)
|
||||||
|
}
|
||||||
|
config.EchServerKeys = EchPrivateKey
|
||||||
|
}
|
||||||
|
switch c.ECHForceQuery {
|
||||||
|
case "none", "half", "full", "":
|
||||||
|
config.EchForceQuery = c.ECHForceQuery
|
||||||
|
default:
|
||||||
|
return nil, errors.New(`invalid "echForceQuery": `, c.ECHForceQuery)
|
||||||
|
}
|
||||||
|
config.EchForceQuery = c.ECHForceQuery
|
||||||
|
config.EchConfigList = c.ECHConfigList
|
||||||
|
if c.ECHSocketSettings != nil {
|
||||||
|
ss, err := c.ECHSocketSettings.Build()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("Failed to build ech sockopt.").Base(err)
|
||||||
|
}
|
||||||
|
config.EchSocketSettings = ss
|
||||||
|
}
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,6 +532,7 @@ type REALITYConfig struct {
|
|||||||
MaxClientVer string `json:"maxClientVer"`
|
MaxClientVer string `json:"maxClientVer"`
|
||||||
MaxTimeDiff uint64 `json:"maxTimeDiff"`
|
MaxTimeDiff uint64 `json:"maxTimeDiff"`
|
||||||
ShortIds []string `json:"shortIds"`
|
ShortIds []string `json:"shortIds"`
|
||||||
|
Mldsa65Seed string `json:"mldsa65Seed"`
|
||||||
|
|
||||||
LimitFallbackUpload LimitFallback `json:"limitFallbackUpload"`
|
LimitFallbackUpload LimitFallback `json:"limitFallbackUpload"`
|
||||||
LimitFallbackDownload LimitFallback `json:"limitFallbackDownload"`
|
LimitFallbackDownload LimitFallback `json:"limitFallbackDownload"`
|
||||||
@@ -514,6 +542,7 @@ type REALITYConfig struct {
|
|||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
PublicKey string `json:"publicKey"`
|
PublicKey string `json:"publicKey"`
|
||||||
ShortId string `json:"shortId"`
|
ShortId string `json:"shortId"`
|
||||||
|
Mldsa65Verify string `json:"mldsa65Verify"`
|
||||||
SpiderX string `json:"spiderX"`
|
SpiderX string `json:"spiderX"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,7 +573,7 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if _, err = strconv.Atoi(s); err == nil {
|
if _, err = strconv.Atoi(s); err == nil {
|
||||||
s = "127.0.0.1:" + s
|
s = "localhost:" + s
|
||||||
}
|
}
|
||||||
if _, _, err = net.SplitHostPort(s); err == nil {
|
if _, _, err = net.SplitHostPort(s); err == nil {
|
||||||
c.Type = "tcp"
|
c.Type = "tcp"
|
||||||
@@ -610,6 +639,15 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
|
|||||||
config.ServerNames = c.ServerNames
|
config.ServerNames = c.ServerNames
|
||||||
config.MaxTimeDiff = c.MaxTimeDiff
|
config.MaxTimeDiff = c.MaxTimeDiff
|
||||||
|
|
||||||
|
if c.Mldsa65Seed != "" {
|
||||||
|
if c.Mldsa65Seed == c.PrivateKey {
|
||||||
|
return nil, errors.New(`"mldsa65Seed" and "privateKey" can not be the same value: `, c.Mldsa65Seed)
|
||||||
|
}
|
||||||
|
if config.Mldsa65Seed, err = base64.RawURLEncoding.DecodeString(c.Mldsa65Seed); err != nil || len(config.Mldsa65Seed) != 32 {
|
||||||
|
return nil, errors.New(`invalid "mldsa65Seed": `, c.Mldsa65Seed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
config.LimitFallbackUpload = new(reality.LimitFallback)
|
config.LimitFallbackUpload = new(reality.LimitFallback)
|
||||||
config.LimitFallbackUpload.AfterBytes = c.LimitFallbackUpload.AfterBytes
|
config.LimitFallbackUpload.AfterBytes = c.LimitFallbackUpload.AfterBytes
|
||||||
config.LimitFallbackUpload.BytesPerSec = c.LimitFallbackUpload.BytesPerSec
|
config.LimitFallbackUpload.BytesPerSec = c.LimitFallbackUpload.BytesPerSec
|
||||||
@@ -645,6 +683,11 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
|
|||||||
if _, err = hex.Decode(config.ShortId, []byte(c.ShortId)); err != nil {
|
if _, err = hex.Decode(config.ShortId, []byte(c.ShortId)); err != nil {
|
||||||
return nil, errors.New(`invalid "shortId": `, c.ShortId)
|
return nil, errors.New(`invalid "shortId": `, c.ShortId)
|
||||||
}
|
}
|
||||||
|
if c.Mldsa65Verify != "" {
|
||||||
|
if config.Mldsa65Verify, err = base64.RawURLEncoding.DecodeString(c.Mldsa65Verify); err != nil || len(config.Mldsa65Verify) != 1952 {
|
||||||
|
return nil, errors.New(`invalid "mldsa65Verify": `, c.Mldsa65Verify)
|
||||||
|
}
|
||||||
|
}
|
||||||
if c.SpiderX == "" {
|
if c.SpiderX == "" {
|
||||||
c.SpiderX = "/"
|
c.SpiderX = "/"
|
||||||
}
|
}
|
||||||
|
@@ -155,7 +155,7 @@ func (c *TrojanServerConfig) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if _, err := strconv.Atoi(fb.Dest); err == nil {
|
if _, err := strconv.Atoi(fb.Dest); err == nil {
|
||||||
fb.Dest = "127.0.0.1:" + fb.Dest
|
fb.Dest = "localhost:" + fb.Dest
|
||||||
}
|
}
|
||||||
if _, _, err := net.SplitHostPort(fb.Dest); err == nil {
|
if _, _, err := net.SplitHostPort(fb.Dest); err == nil {
|
||||||
fb.Type = "tcp"
|
fb.Type = "tcp"
|
||||||
|
22
infra/conf/version.go
Normal file
22
infra/conf/version.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package conf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/xtls/xray-core/app/version"
|
||||||
|
"github.com/xtls/xray-core/core"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VersionConfig struct {
|
||||||
|
MinVersion string `json:"min"`
|
||||||
|
MaxVersion string `json:"max"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *VersionConfig) Build() (*version.Config, error) {
|
||||||
|
coreVersion := strconv.Itoa(int(core.Version_x)) + "." + strconv.Itoa(int(core.Version_y)) + "." + strconv.Itoa(int(core.Version_z))
|
||||||
|
|
||||||
|
return &version.Config{
|
||||||
|
CoreVersion: coreVersion,
|
||||||
|
MinVersion: c.MinVersion,
|
||||||
|
MaxVersion: c.MaxVersion,
|
||||||
|
}, nil
|
||||||
|
}
|
@@ -111,7 +111,7 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if _, err := strconv.Atoi(fb.Dest); err == nil {
|
if _, err := strconv.Atoi(fb.Dest); err == nil {
|
||||||
fb.Dest = "127.0.0.1:" + fb.Dest
|
fb.Dest = "localhost:" + fb.Dest
|
||||||
}
|
}
|
||||||
if _, _, err := net.SplitHostPort(fb.Dest); err == nil {
|
if _, _, err := net.SplitHostPort(fb.Dest); err == nil {
|
||||||
fb.Type = "tcp"
|
fb.Type = "tcp"
|
||||||
|
@@ -110,7 +110,7 @@ func TestVLessInbound(t *testing.T) {
|
|||||||
Alpn: "",
|
Alpn: "",
|
||||||
Path: "",
|
Path: "",
|
||||||
Type: "tcp",
|
Type: "tcp",
|
||||||
Dest: "127.0.0.1:80",
|
Dest: "localhost:80",
|
||||||
Xver: 0,
|
Xver: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -21,6 +21,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
inboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
inboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
||||||
|
"tunnel": func() interface{} { return new(DokodemoConfig) },
|
||||||
"dokodemo-door": func() interface{} { return new(DokodemoConfig) },
|
"dokodemo-door": func() interface{} { return new(DokodemoConfig) },
|
||||||
"http": func() interface{} { return new(HTTPServerConfig) },
|
"http": func() interface{} { return new(HTTPServerConfig) },
|
||||||
"shadowsocks": func() interface{} { return new(ShadowsocksServerConfig) },
|
"shadowsocks": func() interface{} { return new(ShadowsocksServerConfig) },
|
||||||
@@ -33,8 +34,10 @@ var (
|
|||||||
}, "protocol", "settings")
|
}, "protocol", "settings")
|
||||||
|
|
||||||
outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
outboundConfigLoader = NewJSONConfigLoader(ConfigCreatorCache{
|
||||||
|
"block": func() interface{} { return new(BlackholeConfig) },
|
||||||
"blackhole": func() interface{} { return new(BlackholeConfig) },
|
"blackhole": func() interface{} { return new(BlackholeConfig) },
|
||||||
"loopback": func() interface{} { return new(LoopbackConfig) },
|
"loopback": func() interface{} { return new(LoopbackConfig) },
|
||||||
|
"direct": func() interface{} { return new(FreedomConfig) },
|
||||||
"freedom": func() interface{} { return new(FreedomConfig) },
|
"freedom": func() interface{} { return new(FreedomConfig) },
|
||||||
"http": func() interface{} { return new(HTTPClientConfig) },
|
"http": func() interface{} { return new(HTTPClientConfig) },
|
||||||
"shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) },
|
"shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) },
|
||||||
@@ -242,7 +245,7 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
|
|||||||
return nil, errors.New("failed to load inbound detour config for protocol ", c.Protocol).Base(err)
|
return nil, errors.New("failed to load inbound detour config for protocol ", c.Protocol).Base(err)
|
||||||
}
|
}
|
||||||
if dokodemoConfig, ok := rawConfig.(*DokodemoConfig); ok {
|
if dokodemoConfig, ok := rawConfig.(*DokodemoConfig); ok {
|
||||||
receiverSettings.ReceiveOriginalDestination = dokodemoConfig.Redirect
|
receiverSettings.ReceiveOriginalDestination = dokodemoConfig.FollowRedirect
|
||||||
}
|
}
|
||||||
ts, err := rawConfig.(Buildable).Build()
|
ts, err := rawConfig.(Buildable).Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -380,6 +383,7 @@ type Config struct {
|
|||||||
FakeDNS *FakeDNSConfig `json:"fakeDns"`
|
FakeDNS *FakeDNSConfig `json:"fakeDns"`
|
||||||
Observatory *ObservatoryConfig `json:"observatory"`
|
Observatory *ObservatoryConfig `json:"observatory"`
|
||||||
BurstObservatory *BurstObservatoryConfig `json:"burstObservatory"`
|
BurstObservatory *BurstObservatoryConfig `json:"burstObservatory"`
|
||||||
|
Version *VersionConfig `json:"version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) findInboundTag(tag string) int {
|
func (c *Config) findInboundTag(tag string) int {
|
||||||
@@ -448,6 +452,10 @@ func (c *Config) Override(o *Config, fn string) {
|
|||||||
c.BurstObservatory = o.BurstObservatory
|
c.BurstObservatory = o.BurstObservatory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if o.Version != nil {
|
||||||
|
c.Version = o.Version
|
||||||
|
}
|
||||||
|
|
||||||
// update the Inbound in slice if the only one in override config has same tag
|
// update the Inbound in slice if the only one in override config has same tag
|
||||||
if len(o.InboundConfigs) > 0 {
|
if len(o.InboundConfigs) > 0 {
|
||||||
for i := range o.InboundConfigs {
|
for i := range o.InboundConfigs {
|
||||||
@@ -588,6 +596,14 @@ func (c *Config) Build() (*core.Config, error) {
|
|||||||
config.App = append(config.App, serial.ToTypedMessage(r))
|
config.App = append(config.App, serial.ToTypedMessage(r))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.Version != nil {
|
||||||
|
r, err := c.Version.Build()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("failed to build version configuration").Base(err)
|
||||||
|
}
|
||||||
|
config.App = append(config.App, serial.ToTypedMessage(r))
|
||||||
|
}
|
||||||
|
|
||||||
var inbounds []InboundDetourConfig
|
var inbounds []InboundDetourConfig
|
||||||
|
|
||||||
if len(c.InboundConfigs) > 0 {
|
if len(c.InboundConfigs) > 0 {
|
||||||
|
@@ -23,6 +23,8 @@ var CmdAPI = &base.Command{
|
|||||||
cmdRemoveOutbounds,
|
cmdRemoveOutbounds,
|
||||||
cmdListInbounds,
|
cmdListInbounds,
|
||||||
cmdListOutbounds,
|
cmdListOutbounds,
|
||||||
|
cmdAddInboundUsers,
|
||||||
|
cmdRemoveInboundUsers,
|
||||||
cmdInboundUser,
|
cmdInboundUser,
|
||||||
cmdInboundUserCount,
|
cmdInboundUserCount,
|
||||||
cmdAddRules,
|
cmdAddRules,
|
||||||
|
144
main/commands/all/api/inbound_user_add.go
Normal file
144
main/commands/all/api/inbound_user_add.go
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common/protocol"
|
||||||
|
|
||||||
|
handlerService "github.com/xtls/xray-core/app/proxyman/command"
|
||||||
|
cserial "github.com/xtls/xray-core/common/serial"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/core"
|
||||||
|
"github.com/xtls/xray-core/infra/conf"
|
||||||
|
"github.com/xtls/xray-core/infra/conf/serial"
|
||||||
|
"github.com/xtls/xray-core/proxy/shadowsocks"
|
||||||
|
"github.com/xtls/xray-core/proxy/shadowsocks_2022"
|
||||||
|
"github.com/xtls/xray-core/proxy/trojan"
|
||||||
|
vlessin "github.com/xtls/xray-core/proxy/vless/inbound"
|
||||||
|
vmessin "github.com/xtls/xray-core/proxy/vmess/inbound"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/main/commands/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdAddInboundUsers = &base.Command{
|
||||||
|
CustomFlags: true,
|
||||||
|
UsageLine: "{{.Exec}} api adu [--server=127.0.0.1:8080] <c1.json> [c2.json]...",
|
||||||
|
Short: "Add users to inbounds",
|
||||||
|
Long: `
|
||||||
|
Add users to inbounds.
|
||||||
|
Arguments:
|
||||||
|
-s, -server
|
||||||
|
The API server address. Default 127.0.0.1:8080
|
||||||
|
-t, -timeout
|
||||||
|
Timeout seconds to call API. Default 3
|
||||||
|
Example:
|
||||||
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 c1.json c2.json
|
||||||
|
`,
|
||||||
|
Run: executeAddInboundUsers,
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeAddInboundUsers(cmd *base.Command, args []string) {
|
||||||
|
setSharedFlags(cmd)
|
||||||
|
cmd.Flag.Parse(args)
|
||||||
|
unnamedArgs := cmd.Flag.Args()
|
||||||
|
inbs := extractInboundsConfig(unnamedArgs)
|
||||||
|
|
||||||
|
conn, ctx, close := dialAPIServer()
|
||||||
|
defer close()
|
||||||
|
client := handlerService.NewHandlerServiceClient(conn)
|
||||||
|
|
||||||
|
success := 0
|
||||||
|
for _, inb := range inbs {
|
||||||
|
success += executeInboundUserAction(ctx, client, inb, addInboundUserAction)
|
||||||
|
}
|
||||||
|
fmt.Println("Added", success, "user(s) in total.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func addInboundUserAction(ctx context.Context, client handlerService.HandlerServiceClient, tag string, user *protocol.User) error {
|
||||||
|
fmt.Println("add user:", user.Email)
|
||||||
|
_, err := client.AlterInbound(ctx, &handlerService.AlterInboundRequest{
|
||||||
|
Tag: tag,
|
||||||
|
Operation: cserial.ToTypedMessage(
|
||||||
|
&handlerService.AddUserOperation{
|
||||||
|
User: user,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractInboundUsers(inb *core.InboundHandlerConfig) []*protocol.User {
|
||||||
|
if inb == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
inst, err := inb.ProxySettings.GetInstance()
|
||||||
|
if err != nil || inst == nil {
|
||||||
|
fmt.Println("failed to get inbound instance:", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch ty := inst.(type) {
|
||||||
|
case *vmessin.Config:
|
||||||
|
return ty.User
|
||||||
|
case *vlessin.Config:
|
||||||
|
return ty.Clients
|
||||||
|
case *trojan.ServerConfig:
|
||||||
|
return ty.Users
|
||||||
|
case *shadowsocks.ServerConfig:
|
||||||
|
return ty.Users
|
||||||
|
case *shadowsocks_2022.MultiUserServerConfig:
|
||||||
|
return ty.Users
|
||||||
|
default:
|
||||||
|
fmt.Println("unsupported inbound type")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractInboundsConfig(unnamedArgs []string) []conf.InboundDetourConfig {
|
||||||
|
ins := make([]conf.InboundDetourConfig, 0)
|
||||||
|
for _, arg := range unnamedArgs {
|
||||||
|
r, err := loadArg(arg)
|
||||||
|
if err != nil {
|
||||||
|
base.Fatalf("failed to load %s: %s", arg, err)
|
||||||
|
}
|
||||||
|
conf, err := serial.DecodeJSONConfig(r)
|
||||||
|
if err != nil {
|
||||||
|
base.Fatalf("failed to decode %s: %s", arg, err)
|
||||||
|
}
|
||||||
|
ins = append(ins, conf.InboundConfigs...)
|
||||||
|
}
|
||||||
|
return ins
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeInboundUserAction(ctx context.Context, client handlerService.HandlerServiceClient, inb conf.InboundDetourConfig, action func(ctx context.Context, client handlerService.HandlerServiceClient, tag string, user *protocol.User) error) int {
|
||||||
|
success := 0
|
||||||
|
|
||||||
|
tag := inb.Tag
|
||||||
|
if len(tag) < 1 {
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("processing inbound:", tag)
|
||||||
|
built, err := inb.Build()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("failed to build config:", err)
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
|
||||||
|
users := extractInboundUsers(built)
|
||||||
|
if users == nil {
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range users {
|
||||||
|
if len(user.Email) < 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := action(ctx, client, inb.Tag, user); err == nil {
|
||||||
|
fmt.Println("result: ok")
|
||||||
|
success += 1
|
||||||
|
} else {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success
|
||||||
|
}
|
62
main/commands/all/api/inbound_user_remove.go
Normal file
62
main/commands/all/api/inbound_user_remove.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
handlerService "github.com/xtls/xray-core/app/proxyman/command"
|
||||||
|
cserial "github.com/xtls/xray-core/common/serial"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/main/commands/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdRemoveInboundUsers = &base.Command{
|
||||||
|
CustomFlags: true,
|
||||||
|
UsageLine: "{{.Exec}} api rmu [--server=127.0.0.1:8080] -tag=tag <email1> [email2]...",
|
||||||
|
Short: "Remove users from inbounds",
|
||||||
|
Long: `
|
||||||
|
Remove users from inbounds.
|
||||||
|
Arguments:
|
||||||
|
-s, -server
|
||||||
|
The API server address. Default 127.0.0.1:8080
|
||||||
|
-t, -timeout
|
||||||
|
Timeout seconds to call API. Default 3
|
||||||
|
-tag
|
||||||
|
Inbound tag
|
||||||
|
Example:
|
||||||
|
{{.Exec}} {{.LongName}} --server=127.0.0.1:8080 -tag="vless-in" "xray@love.com" ...
|
||||||
|
`,
|
||||||
|
Run: executeRemoveUsers,
|
||||||
|
}
|
||||||
|
|
||||||
|
func executeRemoveUsers(cmd *base.Command, args []string) {
|
||||||
|
setSharedFlags(cmd)
|
||||||
|
var tag string
|
||||||
|
cmd.Flag.StringVar(&tag, "tag", "", "")
|
||||||
|
cmd.Flag.Parse(args)
|
||||||
|
emails := cmd.Flag.Args()
|
||||||
|
if len(tag) < 1 {
|
||||||
|
base.Fatalf("inbound tag not specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, ctx, close := dialAPIServer()
|
||||||
|
defer close()
|
||||||
|
client := handlerService.NewHandlerServiceClient(conn)
|
||||||
|
|
||||||
|
success := 0
|
||||||
|
for _, email := range emails {
|
||||||
|
fmt.Println("remove user:", email)
|
||||||
|
_, err := client.AlterInbound(ctx, &handlerService.AlterInboundRequest{
|
||||||
|
Tag: tag,
|
||||||
|
Operation: cserial.ToTypedMessage(
|
||||||
|
&handlerService.RemoveUserOperation{
|
||||||
|
Email: email,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
success += 1
|
||||||
|
} else {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Println("Removed", success, "user(s) in total.")
|
||||||
|
}
|
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
var cmdListInbounds = &base.Command{
|
var cmdListInbounds = &base.Command{
|
||||||
CustomFlags: true,
|
CustomFlags: true,
|
||||||
UsageLine: "{{.Exec}} api lsi [--server=127.0.0.1:8080]",
|
UsageLine: "{{.Exec}} api lsi [--server=127.0.0.1:8080] [--isOnlyTags=true]",
|
||||||
Short: "List inbounds",
|
Short: "List inbounds",
|
||||||
Long: `
|
Long: `
|
||||||
List inbounds in Xray.
|
List inbounds in Xray.
|
||||||
@@ -29,14 +29,17 @@ Example:
|
|||||||
|
|
||||||
func executeListInbounds(cmd *base.Command, args []string) {
|
func executeListInbounds(cmd *base.Command, args []string) {
|
||||||
setSharedFlags(cmd)
|
setSharedFlags(cmd)
|
||||||
|
var isOnlyTagsStr string
|
||||||
|
cmd.Flag.StringVar(&isOnlyTagsStr, "isOnlyTags", "", "")
|
||||||
cmd.Flag.Parse(args)
|
cmd.Flag.Parse(args)
|
||||||
|
isOnlyTags := isOnlyTagsStr == "true"
|
||||||
|
|
||||||
conn, ctx, close := dialAPIServer()
|
conn, ctx, close := dialAPIServer()
|
||||||
defer close()
|
defer close()
|
||||||
|
|
||||||
client := handlerService.NewHandlerServiceClient(conn)
|
client := handlerService.NewHandlerServiceClient(conn)
|
||||||
|
|
||||||
resp, err := client.ListInbounds(ctx, &handlerService.ListInboundsRequest{})
|
resp, err := client.ListInbounds(ctx, &handlerService.ListInboundsRequest{IsOnlyTags: isOnlyTags})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
base.Fatalf("failed to list inbounds: %s", err)
|
base.Fatalf("failed to list inbounds: %s", err)
|
||||||
}
|
}
|
||||||
|
@@ -16,5 +16,6 @@ func init() {
|
|||||||
cmdUUID,
|
cmdUUID,
|
||||||
cmdX25519,
|
cmdX25519,
|
||||||
cmdWG,
|
cmdWG,
|
||||||
|
cmdMLDSA65,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
42
main/commands/all/mldsa65.go
Normal file
42
main/commands/all/mldsa65.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package all
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/cloudflare/circl/sign/mldsa/mldsa65"
|
||||||
|
"github.com/xtls/xray-core/main/commands/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdMLDSA65 = &base.Command{
|
||||||
|
UsageLine: `{{.Exec}} mldsa65 [-i "seed (base64.RawURLEncoding)"]`,
|
||||||
|
Short: `Generate key pair for ML-DSA-65 post-quantum signature`,
|
||||||
|
Long: `
|
||||||
|
Generate key pair for ML-DSA-65 post-quantum signature.
|
||||||
|
|
||||||
|
Random: {{.Exec}} mldsa65
|
||||||
|
|
||||||
|
From seed: {{.Exec}} mldsa65 -i "seed (base64.RawURLEncoding)"
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cmdMLDSA65.Run = executeMLDSA65 // break init loop
|
||||||
|
}
|
||||||
|
|
||||||
|
var input_seed = cmdMLDSA65.Flag.String("i", "", "")
|
||||||
|
|
||||||
|
func executeMLDSA65(cmd *base.Command, args []string) {
|
||||||
|
var seed [32]byte
|
||||||
|
if len(*input_seed) > 0 {
|
||||||
|
s, _ := base64.RawURLEncoding.DecodeString(*input_seed)
|
||||||
|
seed = [32]byte(s)
|
||||||
|
} else {
|
||||||
|
rand.Read(seed[:])
|
||||||
|
}
|
||||||
|
pub, _ := mldsa65.NewKeyFromSeed(&seed)
|
||||||
|
fmt.Printf("Seed: %v\nVerify: %v",
|
||||||
|
base64.RawURLEncoding.EncodeToString(seed[:]),
|
||||||
|
base64.RawURLEncoding.EncodeToString(pub.Bytes()))
|
||||||
|
}
|
@@ -1,25 +1,26 @@
|
|||||||
package tls
|
package tls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/base64"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/OmarTariq612/goech"
|
"github.com/xtls/reality/hpke"
|
||||||
"github.com/cloudflare/circl/hpke"
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
"github.com/xtls/xray-core/main/commands/base"
|
"github.com/xtls/xray-core/main/commands/base"
|
||||||
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
|
"golang.org/x/crypto/cryptobyte"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmdECH = &base.Command{
|
var cmdECH = &base.Command{
|
||||||
UsageLine: `{{.Exec}} tls ech [--serverName (string)] [--json]`,
|
UsageLine: `{{.Exec}} tls ech [--serverName (string)] [--pem] [-i "ECHServerKeys (base64.StdEncoding)"]`,
|
||||||
Short: `Generate TLS-ECH certificates`,
|
Short: `Generate TLS-ECH certificates`,
|
||||||
Long: `
|
Long: `
|
||||||
Generate TLS-ECH certificates.
|
Generate TLS-ECH certificates.
|
||||||
|
|
||||||
Set serverName to your custom string: {{.Exec}} tls ech --serverName (string)
|
Set serverName to your custom string: {{.Exec}} tls ech --serverName (string)
|
||||||
Generate into json format: {{.Exec}} tls ech --json
|
Generate into pem format: {{.Exec}} tls ech --pem
|
||||||
|
Restore ECHConfigs from ECHServerKeys: {{.Exec}} tls ech -i "ECHServerKeys (base64.StdEncoding)"
|
||||||
`, // Enable PQ signature schemes: {{.Exec}} tls ech --pq-signature-schemes-enabled
|
`, // Enable PQ signature schemes: {{.Exec}} tls ech --pq-signature-schemes-enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,43 +28,66 @@ func init() {
|
|||||||
cmdECH.Run = executeECH
|
cmdECH.Run = executeECH
|
||||||
}
|
}
|
||||||
|
|
||||||
var input_pqSignatureSchemesEnabled = cmdECH.Flag.Bool("pqSignatureSchemesEnabled", false, "")
|
var input_echServerKeys = cmdECH.Flag.String("i", "", "ECHServerKeys (base64.StdEncoding)")
|
||||||
|
|
||||||
|
// var input_pqSignatureSchemesEnabled = cmdECH.Flag.Bool("pqSignatureSchemesEnabled", false, "")
|
||||||
var input_serverName = cmdECH.Flag.String("serverName", "cloudflare-ech.com", "")
|
var input_serverName = cmdECH.Flag.String("serverName", "cloudflare-ech.com", "")
|
||||||
var input_json = cmdECH.Flag.Bool("json", false, "True == turn on json output")
|
var input_pem = cmdECH.Flag.Bool("pem", false, "True == turn on pem output")
|
||||||
|
|
||||||
func executeECH(cmd *base.Command, args []string) {
|
func executeECH(cmd *base.Command, args []string) {
|
||||||
var kem hpke.KEM
|
var kem uint16
|
||||||
|
|
||||||
if *input_pqSignatureSchemesEnabled {
|
// if *input_pqSignatureSchemesEnabled {
|
||||||
kem = hpke.KEM_X25519_KYBER768_DRAFT00
|
// kem = 0x30 // hpke.KEM_X25519_KYBER768_DRAFT00
|
||||||
} else {
|
// } else {
|
||||||
kem = hpke.KEM_X25519_HKDF_SHA256
|
kem = hpke.DHKEM_X25519_HKDF_SHA256
|
||||||
}
|
// }
|
||||||
|
|
||||||
echKeySet, err := goech.GenerateECHKeySet(0, *input_serverName, kem)
|
echConfig, priv, err := tls.GenerateECHKeySet(0, *input_serverName, kem)
|
||||||
common.Must(err)
|
common.Must(err)
|
||||||
|
|
||||||
configBuffer, _ := echKeySet.ECHConfig.MarshalBinary()
|
var configBuffer, keyBuffer []byte
|
||||||
keyBuffer, _ := echKeySet.MarshalBinary()
|
if *input_echServerKeys == "" {
|
||||||
|
configBytes, _ := tls.MarshalBinary(echConfig)
|
||||||
|
var b cryptobyte.Builder
|
||||||
|
b.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) {
|
||||||
|
child.AddBytes(configBytes)
|
||||||
|
})
|
||||||
|
configBuffer, _ = b.Bytes()
|
||||||
|
var b2 cryptobyte.Builder
|
||||||
|
b2.AddUint16(uint16(len(priv)))
|
||||||
|
b2.AddBytes(priv)
|
||||||
|
b2.AddUint16(uint16(len(configBytes)))
|
||||||
|
b2.AddBytes(configBytes)
|
||||||
|
keyBuffer, _ = b2.Bytes()
|
||||||
|
} else {
|
||||||
|
keySetsByte, err := base64.StdEncoding.DecodeString(*input_echServerKeys)
|
||||||
|
if err != nil {
|
||||||
|
os.Stdout.WriteString("Failed to decode ECHServerKeys: " + err.Error() + "\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
keyBuffer = keySetsByte
|
||||||
|
KeySets, err := tls.ConvertToGoECHKeys(keySetsByte)
|
||||||
|
if err != nil {
|
||||||
|
os.Stdout.WriteString("Failed to decode ECHServerKeys: " + err.Error() + "\n")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var b cryptobyte.Builder
|
||||||
|
for _, keySet := range KeySets {
|
||||||
|
b.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) {
|
||||||
|
child.AddBytes(keySet.Config)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
configBuffer, _ = b.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
if *input_pem {
|
||||||
configPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH CONFIGS", Bytes: configBuffer}))
|
configPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH CONFIGS", Bytes: configBuffer}))
|
||||||
keyPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH KEYS", Bytes: keyBuffer}))
|
keyPEM := string(pem.EncodeToMemory(&pem.Block{Type: "ECH KEYS", Bytes: keyBuffer}))
|
||||||
if *input_json {
|
|
||||||
jECHConfigs := map[string]interface{}{
|
|
||||||
"configs": strings.Split(strings.TrimSpace(string(configPEM)), "\n"),
|
|
||||||
}
|
|
||||||
jECHKey := map[string]interface{}{
|
|
||||||
"key": strings.Split(strings.TrimSpace(string(keyPEM)), "\n"),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, i := range []map[string]interface{}{jECHConfigs, jECHKey} {
|
|
||||||
content, err := json.MarshalIndent(i, "", " ")
|
|
||||||
common.Must(err)
|
|
||||||
os.Stdout.Write(content)
|
|
||||||
os.Stdout.WriteString("\n")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
os.Stdout.WriteString(configPEM)
|
os.Stdout.WriteString(configPEM)
|
||||||
os.Stdout.WriteString(keyPEM)
|
os.Stdout.WriteString(keyPEM)
|
||||||
|
} else {
|
||||||
|
os.Stdout.WriteString("ECH config list: \n" + base64.StdEncoding.EncodeToString(configBuffer) + "\n")
|
||||||
|
os.Stdout.WriteString("ECH server keys: \n" + base64.StdEncoding.EncodeToString(keyBuffer) + "\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,9 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/main/commands/base"
|
"github.com/xtls/xray-core/main/commands/base"
|
||||||
. "github.com/xtls/xray-core/transport/internet/tls"
|
. "github.com/xtls/xray-core/transport/internet/tls"
|
||||||
@@ -36,8 +39,15 @@ func executePing(cmd *base.Command, args []string) {
|
|||||||
base.Fatalf("domain not specified")
|
base.Fatalf("domain not specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
domain := cmdPing.Flag.Arg(0)
|
domainWithPort := cmdPing.Flag.Arg(0)
|
||||||
fmt.Println("Tls ping: ", domain)
|
fmt.Println("TLS ping: ", domainWithPort)
|
||||||
|
TargetPort := 443
|
||||||
|
domain, port, err := net.SplitHostPort(domainWithPort)
|
||||||
|
if err != nil {
|
||||||
|
domain = domainWithPort
|
||||||
|
} else {
|
||||||
|
TargetPort, _ = strconv.Atoi(port)
|
||||||
|
}
|
||||||
|
|
||||||
var ip net.IP
|
var ip net.IP
|
||||||
if len(*pingIPStr) > 0 {
|
if len(*pingIPStr) > 0 {
|
||||||
@@ -53,19 +63,19 @@ func executePing(cmd *base.Command, args []string) {
|
|||||||
}
|
}
|
||||||
ip = v.IP
|
ip = v.IP
|
||||||
}
|
}
|
||||||
fmt.Println("Using IP: ", ip.String())
|
fmt.Println("Using IP: ", ip.String()+":"+strconv.Itoa(TargetPort))
|
||||||
|
|
||||||
fmt.Println("-------------------")
|
fmt.Println("-------------------")
|
||||||
fmt.Println("Pinging without SNI")
|
fmt.Println("Pinging without SNI")
|
||||||
{
|
{
|
||||||
tcpConn, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: ip, Port: 443})
|
tcpConn, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: ip, Port: TargetPort})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
base.Fatalf("Failed to dial tcp: %s", err)
|
base.Fatalf("Failed to dial tcp: %s", err)
|
||||||
}
|
}
|
||||||
tlsConn := gotls.Client(tcpConn, &gotls.Config{
|
tlsConn := gotls.Client(tcpConn, &gotls.Config{
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
NextProtos: []string{"http/1.1"},
|
NextProtos: []string{"h2", "http/1.1"},
|
||||||
MaxVersion: gotls.VersionTLS12,
|
MaxVersion: gotls.VersionTLS13,
|
||||||
MinVersion: gotls.VersionTLS12,
|
MinVersion: gotls.VersionTLS12,
|
||||||
// Do not release tool before v5's refactor
|
// Do not release tool before v5's refactor
|
||||||
// VerifyPeerCertificate: showCert(),
|
// VerifyPeerCertificate: showCert(),
|
||||||
@@ -75,6 +85,7 @@ func executePing(cmd *base.Command, args []string) {
|
|||||||
fmt.Println("Handshake failure: ", err)
|
fmt.Println("Handshake failure: ", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Handshake succeeded")
|
fmt.Println("Handshake succeeded")
|
||||||
|
printTLSConnDetail(tlsConn)
|
||||||
printCertificates(tlsConn.ConnectionState().PeerCertificates)
|
printCertificates(tlsConn.ConnectionState().PeerCertificates)
|
||||||
}
|
}
|
||||||
tlsConn.Close()
|
tlsConn.Close()
|
||||||
@@ -89,31 +100,58 @@ func executePing(cmd *base.Command, args []string) {
|
|||||||
}
|
}
|
||||||
tlsConn := gotls.Client(tcpConn, &gotls.Config{
|
tlsConn := gotls.Client(tcpConn, &gotls.Config{
|
||||||
ServerName: domain,
|
ServerName: domain,
|
||||||
NextProtos: []string{"http/1.1"},
|
NextProtos: []string{"h2", "http/1.1"},
|
||||||
MaxVersion: gotls.VersionTLS12,
|
MaxVersion: gotls.VersionTLS13,
|
||||||
MinVersion: gotls.VersionTLS12,
|
MinVersion: gotls.VersionTLS12,
|
||||||
// Do not release tool before v5's refactor
|
// Do not release tool before v5's refactor
|
||||||
// VerifyPeerCertificate: showCert(),
|
// VerifyPeerCertificate: showCert(),
|
||||||
})
|
})
|
||||||
err = tlsConn.Handshake()
|
err = tlsConn.Handshake()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("handshake failure: ", err)
|
fmt.Println("Handshake failure: ", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("handshake succeeded")
|
fmt.Println("Handshake succeeded")
|
||||||
|
printTLSConnDetail(tlsConn)
|
||||||
printCertificates(tlsConn.ConnectionState().PeerCertificates)
|
printCertificates(tlsConn.ConnectionState().PeerCertificates)
|
||||||
}
|
}
|
||||||
tlsConn.Close()
|
tlsConn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Tls ping finished")
|
fmt.Println("-------------------")
|
||||||
|
fmt.Println("TLS ping finished")
|
||||||
}
|
}
|
||||||
|
|
||||||
func printCertificates(certs []*x509.Certificate) {
|
func printCertificates(certs []*x509.Certificate) {
|
||||||
|
var leaf *x509.Certificate
|
||||||
|
var length int
|
||||||
for _, cert := range certs {
|
for _, cert := range certs {
|
||||||
if len(cert.DNSNames) == 0 {
|
length += len(cert.Raw)
|
||||||
continue
|
if len(cert.DNSNames) != 0 {
|
||||||
|
leaf = cert
|
||||||
}
|
}
|
||||||
fmt.Println("Allowed domains: ", cert.DNSNames)
|
}
|
||||||
|
fmt.Println("Certificate chain's total length: ", length, "(certs count: "+strconv.Itoa(len(certs))+")")
|
||||||
|
if leaf != nil {
|
||||||
|
fmt.Println("Cert's signature algorithm: ", leaf.SignatureAlgorithm.String())
|
||||||
|
fmt.Println("Cert's publicKey algorithm: ", leaf.PublicKeyAlgorithm.String())
|
||||||
|
fmt.Println("Cert's allowed domains: ", leaf.DNSNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printTLSConnDetail(tlsConn *gotls.Conn) {
|
||||||
|
var tlsVersion string
|
||||||
|
if tlsConn.ConnectionState().Version == gotls.VersionTLS13 {
|
||||||
|
tlsVersion = "TLS 1.3"
|
||||||
|
} else if tlsConn.ConnectionState().Version == gotls.VersionTLS12 {
|
||||||
|
tlsVersion = "TLS 1.2"
|
||||||
|
}
|
||||||
|
fmt.Println("TLS Version: ", tlsVersion)
|
||||||
|
curveID := *(*gotls.CurveID)(unsafe.Pointer(reflect.ValueOf(tlsConn).Elem().FieldByName("curveID").UnsafeAddr()))
|
||||||
|
if curveID != 0 {
|
||||||
|
PostQuantum := (curveID == gotls.X25519MLKEM768)
|
||||||
|
fmt.Println("TLS Post-Quantum key exchange: ", PostQuantum, "("+curveID.String()+")")
|
||||||
|
} else {
|
||||||
|
fmt.Println("TLS Post-Quantum key exchange: false (RSA Exchange)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -187,6 +187,9 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
|
|||||||
if len(h.blockTypes) > 0 {
|
if len(h.blockTypes) > 0 {
|
||||||
for _, blocktype := range h.blockTypes {
|
for _, blocktype := range h.blockTypes {
|
||||||
if blocktype == int32(qType) {
|
if blocktype == int32(qType) {
|
||||||
|
if h.nonIPQuery == "reject" {
|
||||||
|
go h.rejectNonIPQuery(id, qType, domain, writer)
|
||||||
|
}
|
||||||
errors.LogInfo(ctx, "blocked type ", qType, " query for domain ", domain)
|
errors.LogInfo(ctx, "blocked type ", qType, " query for domain ", domain)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -199,6 +202,11 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet.
|
|||||||
b.Release()
|
b.Release()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if h.nonIPQuery == "reject" {
|
||||||
|
go h.rejectNonIPQuery(id, qType, domain, writer)
|
||||||
|
b.Release()
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := connWriter.WriteMessage(b); err != nil {
|
if err := connWriter.WriteMessage(b); err != nil {
|
||||||
@@ -317,6 +325,43 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) rejectNonIPQuery(id uint16, qType dnsmessage.Type, domain string, writer dns_proto.MessageWriter) {
|
||||||
|
b := buf.New()
|
||||||
|
rawBytes := b.Extend(buf.Size)
|
||||||
|
builder := dnsmessage.NewBuilder(rawBytes[:0], dnsmessage.Header{
|
||||||
|
ID: id,
|
||||||
|
RCode: dnsmessage.RCodeRefused,
|
||||||
|
RecursionAvailable: true,
|
||||||
|
RecursionDesired: true,
|
||||||
|
Response: true,
|
||||||
|
Authoritative: true,
|
||||||
|
})
|
||||||
|
builder.EnableCompression()
|
||||||
|
common.Must(builder.StartQuestions())
|
||||||
|
err := builder.Question(dnsmessage.Question{
|
||||||
|
Name: dnsmessage.MustNewName(domain),
|
||||||
|
Class: dnsmessage.ClassINET,
|
||||||
|
Type: qType,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
errors.LogInfo(context.Background(), "unexpected domain ", domain, " when building reject message: ", err)
|
||||||
|
b.Release()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msgBytes, err := builder.Finish()
|
||||||
|
if err != nil {
|
||||||
|
errors.LogInfoInner(context.Background(), err, "pack reject message")
|
||||||
|
b.Release()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.Resize(0, int32(len(msgBytes)))
|
||||||
|
|
||||||
|
if err := writer.WriteMessage(b); err != nil {
|
||||||
|
errors.LogInfoInner(context.Background(), err, "write reject answer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type outboundConn struct {
|
type outboundConn struct {
|
||||||
access sync.Mutex
|
access sync.Mutex
|
||||||
dialer func() (stat.Connection, error)
|
dialer func() (stat.Connection, error)
|
||||||
|
@@ -28,6 +28,7 @@ type Config struct {
|
|||||||
|
|
||||||
Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
Address *net.IPOrDomain `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
|
||||||
Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"`
|
Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"`
|
||||||
|
PortMap map[string]string `protobuf:"bytes,3,rep,name=port_map,json=portMap,proto3" json:"port_map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||||
// List of networks that the Dokodemo accepts.
|
// List of networks that the Dokodemo accepts.
|
||||||
Networks []net.Network `protobuf:"varint,7,rep,packed,name=networks,proto3,enum=xray.common.net.Network" json:"networks,omitempty"`
|
Networks []net.Network `protobuf:"varint,7,rep,packed,name=networks,proto3,enum=xray.common.net.Network" json:"networks,omitempty"`
|
||||||
FollowRedirect bool `protobuf:"varint,5,opt,name=follow_redirect,json=followRedirect,proto3" json:"follow_redirect,omitempty"`
|
FollowRedirect bool `protobuf:"varint,5,opt,name=follow_redirect,json=followRedirect,proto3" json:"follow_redirect,omitempty"`
|
||||||
@@ -78,6 +79,13 @@ func (x *Config) GetPort() uint32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetPortMap() map[string]string {
|
||||||
|
if x != nil {
|
||||||
|
return x.PortMap
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (x *Config) GetNetworks() []net.Network {
|
func (x *Config) GetNetworks() []net.Network {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Networks
|
return x.Networks
|
||||||
@@ -108,26 +116,34 @@ var file_proxy_dokodemo_config_proto_rawDesc = []byte{
|
|||||||
0x6d, 0x6f, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x61,
|
0x6d, 0x6f, 0x1a, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x61,
|
||||||
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x63, 0x6f,
|
0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x63, 0x6f,
|
||||||
0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
|
0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b,
|
||||||
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd1, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd2, 0x02, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69,
|
||||||
0x67, 0x12, 0x35, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01,
|
0x67, 0x12, 0x35, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01,
|
||||||
0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
|
||||||
0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52,
|
0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52,
|
||||||
0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74,
|
0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74,
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x34, 0x0a, 0x08,
|
0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x43, 0x0a, 0x08,
|
||||||
0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x18,
|
0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28,
|
||||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74,
|
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6f, 0x6b, 0x6f,
|
||||||
0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72,
|
0x64, 0x65, 0x6d, 0x6f, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x50, 0x6f, 0x72, 0x74,
|
||||||
0x6b, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x72, 0x65, 0x64,
|
0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x70, 0x6f, 0x72, 0x74, 0x4d, 0x61,
|
||||||
0x69, 0x72, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x66, 0x6f, 0x6c,
|
0x70, 0x12, 0x34, 0x0a, 0x08, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x18, 0x07, 0x20,
|
||||||
0x6c, 0x6f, 0x77, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x75,
|
0x03, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f,
|
||||||
0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x52, 0x08, 0x6e,
|
||||||
0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x42, 0x5b, 0x0a, 0x17, 0x63, 0x6f,
|
0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x66, 0x6f, 0x6c, 0x6c, 0x6f,
|
||||||
0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6f, 0x6b,
|
0x77, 0x5f, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08,
|
||||||
0x6f, 0x64, 0x65, 0x6d, 0x6f, 0x50, 0x01, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
0x52, 0x0e, 0x66, 0x6f, 0x6c, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
|
||||||
0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f,
|
0x12, 0x1d, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x06,
|
||||||
0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x64, 0x6f, 0x6b, 0x6f, 0x64, 0x65, 0x6d,
|
0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x75, 0x73, 0x65, 0x72, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x1a,
|
||||||
0x6f, 0xaa, 0x02, 0x13, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x44,
|
0x3a, 0x0a, 0x0c, 0x50, 0x6f, 0x72, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
|
||||||
0x6f, 0x6b, 0x6f, 0x64, 0x65, 0x6d, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
|
||||||
|
0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
|
||||||
|
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x5b, 0x0a, 0x17, 0x63,
|
||||||
|
0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x64, 0x6f,
|
||||||
|
0x6b, 0x6f, 0x64, 0x65, 0x6d, 0x6f, 0x50, 0x01, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
|
||||||
|
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63,
|
||||||
|
0x6f, 0x72, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x64, 0x6f, 0x6b, 0x6f, 0x64, 0x65,
|
||||||
|
0x6d, 0x6f, 0xaa, 0x02, 0x13, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e,
|
||||||
|
0x44, 0x6f, 0x6b, 0x6f, 0x64, 0x65, 0x6d, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -142,20 +158,22 @@ func file_proxy_dokodemo_config_proto_rawDescGZIP() []byte {
|
|||||||
return file_proxy_dokodemo_config_proto_rawDescData
|
return file_proxy_dokodemo_config_proto_rawDescData
|
||||||
}
|
}
|
||||||
|
|
||||||
var file_proxy_dokodemo_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
var file_proxy_dokodemo_config_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
var file_proxy_dokodemo_config_proto_goTypes = []any{
|
var file_proxy_dokodemo_config_proto_goTypes = []any{
|
||||||
(*Config)(nil), // 0: xray.proxy.dokodemo.Config
|
(*Config)(nil), // 0: xray.proxy.dokodemo.Config
|
||||||
(*net.IPOrDomain)(nil), // 1: xray.common.net.IPOrDomain
|
nil, // 1: xray.proxy.dokodemo.Config.PortMapEntry
|
||||||
(net.Network)(0), // 2: xray.common.net.Network
|
(*net.IPOrDomain)(nil), // 2: xray.common.net.IPOrDomain
|
||||||
|
(net.Network)(0), // 3: xray.common.net.Network
|
||||||
}
|
}
|
||||||
var file_proxy_dokodemo_config_proto_depIdxs = []int32{
|
var file_proxy_dokodemo_config_proto_depIdxs = []int32{
|
||||||
1, // 0: xray.proxy.dokodemo.Config.address:type_name -> xray.common.net.IPOrDomain
|
2, // 0: xray.proxy.dokodemo.Config.address:type_name -> xray.common.net.IPOrDomain
|
||||||
2, // 1: xray.proxy.dokodemo.Config.networks:type_name -> xray.common.net.Network
|
1, // 1: xray.proxy.dokodemo.Config.port_map:type_name -> xray.proxy.dokodemo.Config.PortMapEntry
|
||||||
2, // [2:2] is the sub-list for method output_type
|
3, // 2: xray.proxy.dokodemo.Config.networks:type_name -> xray.common.net.Network
|
||||||
2, // [2:2] is the sub-list for method input_type
|
3, // [3:3] is the sub-list for method output_type
|
||||||
2, // [2:2] is the sub-list for extension type_name
|
3, // [3:3] is the sub-list for method input_type
|
||||||
2, // [2:2] is the sub-list for extension extendee
|
3, // [3:3] is the sub-list for extension type_name
|
||||||
0, // [0:2] is the sub-list for field type_name
|
3, // [3:3] is the sub-list for extension extendee
|
||||||
|
0, // [0:3] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_proxy_dokodemo_config_proto_init() }
|
func init() { file_proxy_dokodemo_config_proto_init() }
|
||||||
@@ -169,7 +187,7 @@ func file_proxy_dokodemo_config_proto_init() {
|
|||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_proxy_dokodemo_config_proto_rawDesc,
|
RawDescriptor: file_proxy_dokodemo_config_proto_rawDesc,
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 1,
|
NumMessages: 2,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
|
@@ -13,6 +13,8 @@ message Config {
|
|||||||
xray.common.net.IPOrDomain address = 1;
|
xray.common.net.IPOrDomain address = 1;
|
||||||
uint32 port = 2;
|
uint32 port = 2;
|
||||||
|
|
||||||
|
map<string, string> port_map = 3;
|
||||||
|
|
||||||
// List of networks that the Dokodemo accepts.
|
// List of networks that the Dokodemo accepts.
|
||||||
repeated xray.common.net.Network networks = 7;
|
repeated xray.common.net.Network networks = 7;
|
||||||
|
|
||||||
|
@@ -3,6 +3,8 @@ package dokodemo
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/xtls/xray-core/common"
|
"github.com/xtls/xray-core/common"
|
||||||
@@ -36,6 +38,7 @@ type DokodemoDoor struct {
|
|||||||
config *Config
|
config *Config
|
||||||
address net.Address
|
address net.Address
|
||||||
port net.Port
|
port net.Port
|
||||||
|
portMap map[string]string
|
||||||
sockopt *session.Sockopt
|
sockopt *session.Sockopt
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,6 +50,7 @@ func (d *DokodemoDoor) Init(config *Config, pm policy.Manager, sockopt *session.
|
|||||||
d.config = config
|
d.config = config
|
||||||
d.address = config.GetPredefinedAddress()
|
d.address = config.GetPredefinedAddress()
|
||||||
d.port = net.Port(config.Port)
|
d.port = net.Port(config.Port)
|
||||||
|
d.portMap = config.PortMap
|
||||||
d.policyManager = pm
|
d.policyManager = pm
|
||||||
d.sockopt = sockopt
|
d.sockopt = sockopt
|
||||||
|
|
||||||
@@ -73,6 +77,33 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn st
|
|||||||
Port: d.port,
|
Port: d.port,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !d.config.FollowRedirect {
|
||||||
|
host, port, err := net.SplitHostPort(conn.LocalAddr().String())
|
||||||
|
if dest.Address == nil {
|
||||||
|
if err != nil {
|
||||||
|
dest.Address = net.DomainAddress("localhost")
|
||||||
|
} else {
|
||||||
|
if strings.Contains(host, ".") {
|
||||||
|
dest.Address = net.LocalHostIP
|
||||||
|
} else {
|
||||||
|
dest.Address = net.LocalHostIPv6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dest.Port == 0 {
|
||||||
|
dest.Port = net.Port(common.Must2(strconv.Atoi(port)).(int))
|
||||||
|
}
|
||||||
|
if d.portMap != nil && d.portMap[port] != "" {
|
||||||
|
h, p, _ := net.SplitHostPort(d.portMap[port])
|
||||||
|
if len(h) > 0 {
|
||||||
|
dest.Address = net.ParseAddress(h)
|
||||||
|
}
|
||||||
|
if len(p) > 0 {
|
||||||
|
dest.Port = net.Port(common.Must2(strconv.Atoi(p)).(int))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
destinationOverridden := false
|
destinationOverridden := false
|
||||||
if d.config.FollowRedirect {
|
if d.config.FollowRedirect {
|
||||||
outbounds := session.OutboundsFromContext(ctx)
|
outbounds := session.OutboundsFromContext(ctx)
|
||||||
|
@@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/xtls/xray-core/common/session"
|
"github.com/xtls/xray-core/common/session"
|
||||||
"github.com/xtls/xray-core/common/signal"
|
"github.com/xtls/xray-core/common/signal"
|
||||||
"github.com/xtls/xray-core/common/task"
|
"github.com/xtls/xray-core/common/task"
|
||||||
|
"github.com/xtls/xray-core/common/utils"
|
||||||
"github.com/xtls/xray-core/core"
|
"github.com/xtls/xray-core/core"
|
||||||
"github.com/xtls/xray-core/features/dns"
|
"github.com/xtls/xray-core/features/dns"
|
||||||
"github.com/xtls/xray-core/features/policy"
|
"github.com/xtls/xray-core/features/policy"
|
||||||
@@ -202,7 +203,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
writer = buf.NewWriter(conn)
|
writer = buf.NewWriter(conn)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
writer = NewPacketWriter(conn, h, ctx, UDPOverride)
|
writer = NewPacketWriter(conn, h, ctx, UDPOverride, destination)
|
||||||
if h.config.Noises != nil {
|
if h.config.Noises != nil {
|
||||||
errors.LogDebug(ctx, "NOISE", h.config.Noises)
|
errors.LogDebug(ctx, "NOISE", h.config.Noises)
|
||||||
writer = &NoisePacketWriter{
|
writer = &NoisePacketWriter{
|
||||||
@@ -238,7 +239,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
|
|||||||
if destination.Network == net.Network_TCP {
|
if destination.Network == net.Network_TCP {
|
||||||
reader = buf.NewReader(conn)
|
reader = buf.NewReader(conn)
|
||||||
} else {
|
} else {
|
||||||
reader = NewPacketReader(conn, UDPOverride)
|
reader = NewPacketReader(conn, UDPOverride, destination)
|
||||||
}
|
}
|
||||||
if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil {
|
if err := buf.Copy(reader, output, buf.UpdateActivity(timer)); err != nil {
|
||||||
return errors.New("failed to process response").Base(err)
|
return errors.New("failed to process response").Base(err)
|
||||||
@@ -273,7 +274,7 @@ func isTLSConn(conn stat.Connection) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPacketReader(conn net.Conn, UDPOverride net.Destination) buf.Reader {
|
func NewPacketReader(conn net.Conn, UDPOverride net.Destination, DialDest net.Destination) buf.Reader {
|
||||||
iConn := conn
|
iConn := conn
|
||||||
statConn, ok := iConn.(*stat.CounterConnection)
|
statConn, ok := iConn.(*stat.CounterConnection)
|
||||||
if ok {
|
if ok {
|
||||||
@@ -283,10 +284,19 @@ func NewPacketReader(conn net.Conn, UDPOverride net.Destination) buf.Reader {
|
|||||||
if statConn != nil {
|
if statConn != nil {
|
||||||
counter = statConn.ReadCounter
|
counter = statConn.ReadCounter
|
||||||
}
|
}
|
||||||
if c, ok := iConn.(*internet.PacketConnWrapper); ok && UDPOverride.Address == nil && UDPOverride.Port == 0 {
|
if c, ok := iConn.(*internet.PacketConnWrapper); ok {
|
||||||
|
isOverridden := false
|
||||||
|
if UDPOverride.Address != nil || UDPOverride.Port != 0 {
|
||||||
|
isOverridden = true
|
||||||
|
}
|
||||||
|
changedAddress, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
|
||||||
|
|
||||||
return &PacketReader{
|
return &PacketReader{
|
||||||
PacketConnWrapper: c,
|
PacketConnWrapper: c,
|
||||||
Counter: counter,
|
Counter: counter,
|
||||||
|
IsOverridden: isOverridden,
|
||||||
|
InitUnchangedAddr: DialDest.Address,
|
||||||
|
InitChangedAddr: net.ParseAddress(changedAddress),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &buf.PacketReader{Reader: conn}
|
return &buf.PacketReader{Reader: conn}
|
||||||
@@ -295,6 +305,9 @@ func NewPacketReader(conn net.Conn, UDPOverride net.Destination) buf.Reader {
|
|||||||
type PacketReader struct {
|
type PacketReader struct {
|
||||||
*internet.PacketConnWrapper
|
*internet.PacketConnWrapper
|
||||||
stats.Counter
|
stats.Counter
|
||||||
|
IsOverridden bool
|
||||||
|
InitUnchangedAddr net.Address
|
||||||
|
InitChangedAddr net.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
||||||
@@ -306,18 +319,27 @@ func (r *PacketReader) ReadMultiBuffer() (buf.MultiBuffer, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
b.Resize(0, int32(n))
|
b.Resize(0, int32(n))
|
||||||
|
// if udp dest addr is changed, we are unable to get the correct src addr
|
||||||
|
// so we don't attach src info to udp packet, break cone behavior, assuming the dial dest is the expected scr addr
|
||||||
|
if !r.IsOverridden {
|
||||||
|
address := net.IPAddress(d.(*net.UDPAddr).IP)
|
||||||
|
if r.InitChangedAddr == address {
|
||||||
|
address = r.InitUnchangedAddr
|
||||||
|
}
|
||||||
b.UDP = &net.Destination{
|
b.UDP = &net.Destination{
|
||||||
Address: net.IPAddress(d.(*net.UDPAddr).IP),
|
Address: address,
|
||||||
Port: net.Port(d.(*net.UDPAddr).Port),
|
Port: net.Port(d.(*net.UDPAddr).Port),
|
||||||
Network: net.Network_UDP,
|
Network: net.Network_UDP,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if r.Counter != nil {
|
if r.Counter != nil {
|
||||||
r.Counter.Add(int64(n))
|
r.Counter.Add(int64(n))
|
||||||
}
|
}
|
||||||
return buf.MultiBuffer{b}, nil
|
return buf.MultiBuffer{b}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPacketWriter(conn net.Conn, h *Handler, ctx context.Context, UDPOverride net.Destination) buf.Writer {
|
// DialDest means the dial target used in the dialer when creating conn
|
||||||
|
func NewPacketWriter(conn net.Conn, h *Handler, ctx context.Context, UDPOverride net.Destination, DialDest net.Destination) buf.Writer {
|
||||||
iConn := conn
|
iConn := conn
|
||||||
statConn, ok := iConn.(*stat.CounterConnection)
|
statConn, ok := iConn.(*stat.CounterConnection)
|
||||||
if ok {
|
if ok {
|
||||||
@@ -328,12 +350,20 @@ func NewPacketWriter(conn net.Conn, h *Handler, ctx context.Context, UDPOverride
|
|||||||
counter = statConn.WriteCounter
|
counter = statConn.WriteCounter
|
||||||
}
|
}
|
||||||
if c, ok := iConn.(*internet.PacketConnWrapper); ok {
|
if c, ok := iConn.(*internet.PacketConnWrapper); ok {
|
||||||
|
// If DialDest is a domain, it will be resolved in dialer
|
||||||
|
// check this behavior and add it to map
|
||||||
|
resolvedUDPAddr := utils.NewTypedSyncMap[string, net.Address]()
|
||||||
|
if DialDest.Address.Family().IsDomain() {
|
||||||
|
RemoteAddress, _, _ := net.SplitHostPort(conn.RemoteAddr().String())
|
||||||
|
resolvedUDPAddr.Store(DialDest.Address.String(), net.ParseAddress(RemoteAddress))
|
||||||
|
}
|
||||||
return &PacketWriter{
|
return &PacketWriter{
|
||||||
PacketConnWrapper: c,
|
PacketConnWrapper: c,
|
||||||
Counter: counter,
|
Counter: counter,
|
||||||
Handler: h,
|
Handler: h,
|
||||||
Context: ctx,
|
Context: ctx,
|
||||||
UDPOverride: UDPOverride,
|
UDPOverride: UDPOverride,
|
||||||
|
resolvedUDPAddr: resolvedUDPAddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -346,6 +376,12 @@ type PacketWriter struct {
|
|||||||
*Handler
|
*Handler
|
||||||
context.Context
|
context.Context
|
||||||
UDPOverride net.Destination
|
UDPOverride net.Destination
|
||||||
|
|
||||||
|
// Dest of udp packets might be a domain, we will resolve them to IP
|
||||||
|
// But resolver will return a random one if the domain has many IPs
|
||||||
|
// Resulting in these packets being sent to many different IPs randomly
|
||||||
|
// So, cache and keep the resolve result
|
||||||
|
resolvedUDPAddr *utils.TypedSyncMap[string, net.Address]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
||||||
@@ -364,10 +400,34 @@ func (w *PacketWriter) WriteMultiBuffer(mb buf.MultiBuffer) error {
|
|||||||
if w.UDPOverride.Port != 0 {
|
if w.UDPOverride.Port != 0 {
|
||||||
b.UDP.Port = w.UDPOverride.Port
|
b.UDP.Port = w.UDPOverride.Port
|
||||||
}
|
}
|
||||||
if w.Handler.config.hasStrategy() && b.UDP.Address.Family().IsDomain() {
|
if b.UDP.Address.Family().IsDomain() {
|
||||||
ip := w.Handler.resolveIP(w.Context, b.UDP.Address.Domain(), nil)
|
if ip, ok := w.resolvedUDPAddr.Load(b.UDP.Address.Domain()); ok {
|
||||||
if ip != nil {
|
|
||||||
b.UDP.Address = ip
|
b.UDP.Address = ip
|
||||||
|
} else {
|
||||||
|
ShouldUseSystemResolver := true
|
||||||
|
if w.Handler.config.hasStrategy() {
|
||||||
|
ip = w.Handler.resolveIP(w.Context, b.UDP.Address.Domain(), nil)
|
||||||
|
if ip != nil {
|
||||||
|
ShouldUseSystemResolver = false
|
||||||
|
}
|
||||||
|
// drop packet if resolve failed when forceIP
|
||||||
|
if ip == nil && w.Handler.config.forceIP() {
|
||||||
|
b.Release()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ShouldUseSystemResolver {
|
||||||
|
udpAddr, err := net.ResolveUDPAddr("udp", b.UDP.NetAddr())
|
||||||
|
if err != nil {
|
||||||
|
b.Release()
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
ip = net.IPAddress(udpAddr.IP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ip != nil {
|
||||||
|
b.UDP.Address, _ = w.resolvedUDPAddr.LoadOrStore(b.UDP.Address.Domain(), ip)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
destAddr, _ := net.ResolveUDPAddr("udp", b.UDP.NetAddr())
|
destAddr, _ := net.ResolveUDPAddr("udp", b.UDP.NetAddr())
|
||||||
|
@@ -128,6 +128,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis
|
|||||||
|
|
||||||
conn.Write(data.Bytes())
|
conn.Write(data.Bytes())
|
||||||
})
|
})
|
||||||
|
defer udpServer.RemoveRay()
|
||||||
|
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
var dest *net.Destination
|
var dest *net.Destination
|
||||||
|
@@ -245,13 +245,15 @@ func (s *Server) handleUDPPayload(ctx context.Context, conn stat.Connection, dis
|
|||||||
udpMessage, err := EncodeUDPPacket(request, payload.Bytes())
|
udpMessage, err := EncodeUDPPacket(request, payload.Bytes())
|
||||||
payload.Release()
|
payload.Release()
|
||||||
|
|
||||||
defer udpMessage.Release()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors.LogWarningInner(ctx, err, "failed to write UDP response")
|
errors.LogWarningInner(ctx, err, "failed to write UDP response")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
defer udpMessage.Release()
|
||||||
|
|
||||||
conn.Write(udpMessage.Bytes())
|
conn.Write(udpMessage.Bytes())
|
||||||
})
|
})
|
||||||
|
defer udpServer.RemoveRay()
|
||||||
|
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
if inbound != nil && inbound.Source.IsValid() {
|
if inbound != nil && inbound.Source.IsValid() {
|
||||||
|
@@ -259,6 +259,7 @@ func (s *Server) handleUDPPayload(ctx context.Context, clientReader *PacketReade
|
|||||||
errors.LogWarningInner(ctx, err, "failed to write response")
|
errors.LogWarningInner(ctx, err, "failed to write response")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
defer udpServer.RemoveRay()
|
||||||
|
|
||||||
inbound := session.InboundFromContext(ctx)
|
inbound := session.InboundFromContext(ctx)
|
||||||
user := inbound.User
|
user := inbound.User
|
||||||
|
@@ -53,6 +53,7 @@ func (v *Validator) Get(hash string) *protocol.MemoryUser {
|
|||||||
|
|
||||||
// Get a trojan user with hashed key, nil if user doesn't exist.
|
// Get a trojan user with hashed key, nil if user doesn't exist.
|
||||||
func (v *Validator) GetByEmail(email string) *protocol.MemoryUser {
|
func (v *Validator) GetByEmail(email string) *protocol.MemoryUser {
|
||||||
|
email = strings.ToLower(email)
|
||||||
u, _ := v.email.Load(email)
|
u, _ := v.email.Load(email)
|
||||||
if u != nil {
|
if u != nil {
|
||||||
return u.(*protocol.MemoryUser)
|
return u.(*protocol.MemoryUser)
|
||||||
|
@@ -206,7 +206,10 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
|
|||||||
|
|
||||||
first := buf.FromBytes(make([]byte, buf.Size))
|
first := buf.FromBytes(make([]byte, buf.Size))
|
||||||
first.Clear()
|
first.Clear()
|
||||||
firstLen, _ := first.ReadFrom(connection)
|
firstLen, errR := first.ReadFrom(connection)
|
||||||
|
if errR != nil {
|
||||||
|
return errR
|
||||||
|
}
|
||||||
errors.LogInfo(ctx, "firstLen = ", firstLen)
|
errors.LogInfo(ctx, "firstLen = ", firstLen)
|
||||||
|
|
||||||
reader := &buf.BufferedReader{
|
reader := &buf.BufferedReader{
|
||||||
|
@@ -63,6 +63,7 @@ func (v *MemoryValidator) Get(id uuid.UUID) *protocol.MemoryUser {
|
|||||||
|
|
||||||
// Get a VLESS user with email, nil if user doesn't exist.
|
// Get a VLESS user with email, nil if user doesn't exist.
|
||||||
func (v *MemoryValidator) GetByEmail(email string) *protocol.MemoryUser {
|
func (v *MemoryValidator) GetByEmail(email string) *protocol.MemoryUser {
|
||||||
|
email = strings.ToLower(email)
|
||||||
u, _ := v.email.Load(email)
|
u, _ := v.email.Load(email)
|
||||||
if u != nil {
|
if u != nil {
|
||||||
return u.(*protocol.MemoryUser)
|
return u.(*protocol.MemoryUser)
|
||||||
|
@@ -7,6 +7,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/cloudflare/circl/sign/mldsa/mldsa65"
|
||||||
"github.com/xtls/reality"
|
"github.com/xtls/reality"
|
||||||
"github.com/xtls/xray-core/common/errors"
|
"github.com/xtls/xray-core/common/errors"
|
||||||
"github.com/xtls/xray-core/transport/internet"
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
@@ -32,6 +33,10 @@ func (c *Config) GetREALITYConfig() *reality.Config {
|
|||||||
|
|
||||||
KeyLogWriter: KeyLogWriterFromConfig(c),
|
KeyLogWriter: KeyLogWriterFromConfig(c),
|
||||||
}
|
}
|
||||||
|
if c.Mldsa65Seed != nil {
|
||||||
|
_, key := mldsa65.NewKeyFromSeed((*[32]byte)(c.Mldsa65Seed))
|
||||||
|
config.Mldsa65Key = key.Bytes()
|
||||||
|
}
|
||||||
if c.LimitFallbackUpload != nil {
|
if c.LimitFallbackUpload != nil {
|
||||||
config.LimitFallbackUpload.AfterBytes = c.LimitFallbackUpload.AfterBytes
|
config.LimitFallbackUpload.AfterBytes = c.LimitFallbackUpload.AfterBytes
|
||||||
config.LimitFallbackUpload.BytesPerSec = c.LimitFallbackUpload.BytesPerSec
|
config.LimitFallbackUpload.BytesPerSec = c.LimitFallbackUpload.BytesPerSec
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// protoc-gen-go v1.35.1
|
// protoc-gen-go v1.35.1
|
||||||
// protoc v5.29.4
|
// protoc v5.28.2
|
||||||
// source: transport/internet/reality/config.proto
|
// source: transport/internet/reality/config.proto
|
||||||
|
|
||||||
package reality
|
package reality
|
||||||
@@ -35,15 +35,17 @@ type Config struct {
|
|||||||
MaxClientVer []byte `protobuf:"bytes,8,opt,name=max_client_ver,json=maxClientVer,proto3" json:"max_client_ver,omitempty"`
|
MaxClientVer []byte `protobuf:"bytes,8,opt,name=max_client_ver,json=maxClientVer,proto3" json:"max_client_ver,omitempty"`
|
||||||
MaxTimeDiff uint64 `protobuf:"varint,9,opt,name=max_time_diff,json=maxTimeDiff,proto3" json:"max_time_diff,omitempty"`
|
MaxTimeDiff uint64 `protobuf:"varint,9,opt,name=max_time_diff,json=maxTimeDiff,proto3" json:"max_time_diff,omitempty"`
|
||||||
ShortIds [][]byte `protobuf:"bytes,10,rep,name=short_ids,json=shortIds,proto3" json:"short_ids,omitempty"`
|
ShortIds [][]byte `protobuf:"bytes,10,rep,name=short_ids,json=shortIds,proto3" json:"short_ids,omitempty"`
|
||||||
|
Mldsa65Seed []byte `protobuf:"bytes,11,opt,name=mldsa65_seed,json=mldsa65Seed,proto3" json:"mldsa65_seed,omitempty"`
|
||||||
|
LimitFallbackUpload *LimitFallback `protobuf:"bytes,12,opt,name=limit_fallback_upload,json=limitFallbackUpload,proto3" json:"limit_fallback_upload,omitempty"`
|
||||||
|
LimitFallbackDownload *LimitFallback `protobuf:"bytes,13,opt,name=limit_fallback_download,json=limitFallbackDownload,proto3" json:"limit_fallback_download,omitempty"`
|
||||||
Fingerprint string `protobuf:"bytes,21,opt,name=Fingerprint,proto3" json:"Fingerprint,omitempty"`
|
Fingerprint string `protobuf:"bytes,21,opt,name=Fingerprint,proto3" json:"Fingerprint,omitempty"`
|
||||||
ServerName string `protobuf:"bytes,22,opt,name=server_name,json=serverName,proto3" json:"server_name,omitempty"`
|
ServerName string `protobuf:"bytes,22,opt,name=server_name,json=serverName,proto3" json:"server_name,omitempty"`
|
||||||
PublicKey []byte `protobuf:"bytes,23,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
|
PublicKey []byte `protobuf:"bytes,23,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
|
||||||
ShortId []byte `protobuf:"bytes,24,opt,name=short_id,json=shortId,proto3" json:"short_id,omitempty"`
|
ShortId []byte `protobuf:"bytes,24,opt,name=short_id,json=shortId,proto3" json:"short_id,omitempty"`
|
||||||
SpiderX string `protobuf:"bytes,25,opt,name=spider_x,json=spiderX,proto3" json:"spider_x,omitempty"`
|
Mldsa65Verify []byte `protobuf:"bytes,25,opt,name=mldsa65_verify,json=mldsa65Verify,proto3" json:"mldsa65_verify,omitempty"`
|
||||||
SpiderY []int64 `protobuf:"varint,26,rep,packed,name=spider_y,json=spiderY,proto3" json:"spider_y,omitempty"`
|
SpiderX string `protobuf:"bytes,26,opt,name=spider_x,json=spiderX,proto3" json:"spider_x,omitempty"`
|
||||||
MasterKeyLog string `protobuf:"bytes,27,opt,name=master_key_log,json=masterKeyLog,proto3" json:"master_key_log,omitempty"`
|
SpiderY []int64 `protobuf:"varint,27,rep,packed,name=spider_y,json=spiderY,proto3" json:"spider_y,omitempty"`
|
||||||
LimitFallbackUpload *LimitFallback `protobuf:"bytes,28,opt,name=limit_fallback_upload,json=limitFallbackUpload,proto3" json:"limit_fallback_upload,omitempty"`
|
MasterKeyLog string `protobuf:"bytes,31,opt,name=master_key_log,json=masterKeyLog,proto3" json:"master_key_log,omitempty"`
|
||||||
LimitFallbackDownload *LimitFallback `protobuf:"bytes,29,opt,name=limit_fallback_download,json=limitFallbackDownload,proto3" json:"limit_fallback_download,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
@@ -146,6 +148,27 @@ func (x *Config) GetShortIds() [][]byte {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetMldsa65Seed() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.Mldsa65Seed
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetLimitFallbackUpload() *LimitFallback {
|
||||||
|
if x != nil {
|
||||||
|
return x.LimitFallbackUpload
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetLimitFallbackDownload() *LimitFallback {
|
||||||
|
if x != nil {
|
||||||
|
return x.LimitFallbackDownload
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (x *Config) GetFingerprint() string {
|
func (x *Config) GetFingerprint() string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Fingerprint
|
return x.Fingerprint
|
||||||
@@ -174,6 +197,13 @@ func (x *Config) GetShortId() []byte {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetMldsa65Verify() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.Mldsa65Verify
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (x *Config) GetSpiderX() string {
|
func (x *Config) GetSpiderX() string {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.SpiderX
|
return x.SpiderX
|
||||||
@@ -195,20 +225,6 @@ func (x *Config) GetMasterKeyLog() string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) GetLimitFallbackUpload() *LimitFallback {
|
|
||||||
if x != nil {
|
|
||||||
return x.LimitFallbackUpload
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *Config) GetLimitFallbackDownload() *LimitFallback {
|
|
||||||
if x != nil {
|
|
||||||
return x.LimitFallbackDownload
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type LimitFallback struct {
|
type LimitFallback struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
@@ -277,7 +293,7 @@ var file_transport_internet_reality_config_proto_rawDesc = []byte{
|
|||||||
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2f, 0x63, 0x6f, 0x6e,
|
0x72, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2f, 0x63, 0x6f, 0x6e,
|
||||||
0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1f, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||||
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||||
0x65, 0x74, 0x2e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0xce, 0x05, 0x0a, 0x06, 0x43,
|
0x65, 0x74, 0x2e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0x98, 0x06, 0x0a, 0x06, 0x43,
|
||||||
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x18, 0x01, 0x20,
|
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x18, 0x01, 0x20,
|
||||||
0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73,
|
0x01, 0x28, 0x08, 0x52, 0x04, 0x73, 0x68, 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73,
|
||||||
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a,
|
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a,
|
||||||
@@ -296,50 +312,55 @@ var file_transport_internet_reality_config_proto_rawDesc = []byte{
|
|||||||
0x65, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61,
|
0x65, 0x5f, 0x64, 0x69, 0x66, 0x66, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61,
|
||||||
0x78, 0x54, 0x69, 0x6d, 0x65, 0x44, 0x69, 0x66, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x6f,
|
0x78, 0x54, 0x69, 0x6d, 0x65, 0x44, 0x69, 0x66, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x68, 0x6f,
|
||||||
0x72, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x68,
|
0x72, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x68,
|
||||||
0x6f, 0x72, 0x74, 0x49, 0x64, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72,
|
0x6f, 0x72, 0x74, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x6c, 0x64, 0x73, 0x61, 0x36,
|
||||||
0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x46, 0x69, 0x6e,
|
0x35, 0x5f, 0x73, 0x65, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x6d, 0x6c,
|
||||||
0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76,
|
0x64, 0x73, 0x61, 0x36, 0x35, 0x53, 0x65, 0x65, 0x64, 0x12, 0x62, 0x0a, 0x15, 0x6c, 0x69, 0x6d,
|
||||||
0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73,
|
0x69, 0x74, 0x5f, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x75, 0x70, 0x6c, 0x6f,
|
||||||
0x65, 0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62,
|
0x61, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e,
|
||||||
0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70,
|
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||||
0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x68, 0x6f, 0x72,
|
0x65, 0x74, 0x2e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74,
|
||||||
0x74, 0x5f, 0x69, 0x64, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x68, 0x6f, 0x72,
|
0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x13, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x46,
|
||||||
0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x78, 0x18,
|
0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x66, 0x0a,
|
||||||
0x19, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x58, 0x12, 0x19,
|
0x17, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f,
|
||||||
0x0a, 0x08, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x79, 0x18, 0x1a, 0x20, 0x03, 0x28, 0x03,
|
0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e,
|
||||||
0x52, 0x07, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x59, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x73,
|
|
||||||
0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x1b, 0x20, 0x01, 0x28,
|
|
||||||
0x09, 0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x12,
|
|
||||||
0x62, 0x0a, 0x15, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63,
|
|
||||||
0x6b, 0x5f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x1c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e,
|
|
||||||
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
|
0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
|
||||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79,
|
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79,
|
||||||
0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x13,
|
0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x52, 0x15,
|
||||||
0x6c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x55, 0x70, 0x6c,
|
0x6c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x44, 0x6f, 0x77,
|
||||||
0x6f, 0x61, 0x64, 0x12, 0x66, 0x0a, 0x17, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x66, 0x61, 0x6c,
|
0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70,
|
||||||
0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x1d,
|
0x72, 0x69, 0x6e, 0x74, 0x18, 0x15, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x46, 0x69, 0x6e, 0x67,
|
||||||
0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
|
0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65,
|
||||||
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x72,
|
0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65,
|
||||||
0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x61, 0x6c, 0x6c,
|
0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c,
|
||||||
0x62, 0x61, 0x63, 0x6b, 0x52, 0x15, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x61, 0x6c, 0x6c, 0x62,
|
0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75,
|
||||||
0x61, 0x63, 0x6b, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x83, 0x01, 0x0a, 0x0d,
|
0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x68, 0x6f, 0x72, 0x74,
|
||||||
0x4c, 0x69, 0x6d, 0x69, 0x74, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x1f, 0x0a,
|
0x5f, 0x69, 0x64, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x73, 0x68, 0x6f, 0x72, 0x74,
|
||||||
0x0b, 0x61, 0x66, 0x74, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01,
|
0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x6c, 0x64, 0x73, 0x61, 0x36, 0x35, 0x5f, 0x76, 0x65,
|
||||||
0x28, 0x04, 0x52, 0x0a, 0x61, 0x66, 0x74, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x22,
|
0x72, 0x69, 0x66, 0x79, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x6d, 0x6c, 0x64, 0x73,
|
||||||
0x0a, 0x0d, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x18,
|
0x61, 0x36, 0x35, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x69,
|
||||||
0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x62, 0x79, 0x74, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53,
|
0x64, 0x65, 0x72, 0x5f, 0x78, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x69,
|
||||||
0x65, 0x63, 0x12, 0x2d, 0x0a, 0x13, 0x62, 0x75, 0x72, 0x73, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65,
|
0x64, 0x65, 0x72, 0x58, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x79,
|
||||||
0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52,
|
0x18, 0x1b, 0x20, 0x03, 0x28, 0x03, 0x52, 0x07, 0x73, 0x70, 0x69, 0x64, 0x65, 0x72, 0x59, 0x12,
|
||||||
0x10, 0x62, 0x75, 0x72, 0x73, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53, 0x65,
|
0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f,
|
||||||
0x63, 0x42, 0x7f, 0x0a, 0x23, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72,
|
0x67, 0x18, 0x1f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b,
|
||||||
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
0x65, 0x79, 0x4c, 0x6f, 0x67, 0x22, 0x83, 0x01, 0x0a, 0x0d, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x46,
|
||||||
0x2e, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x50, 0x01, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68,
|
0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x66, 0x74, 0x65, 0x72,
|
||||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
|
0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x61, 0x66,
|
||||||
0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f,
|
0x74, 0x65, 0x72, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x62, 0x79, 0x74, 0x65,
|
||||||
0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79,
|
0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52,
|
||||||
0xaa, 0x02, 0x1f, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
0x0b, 0x62, 0x79, 0x74, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53, 0x65, 0x63, 0x12, 0x2d, 0x0a, 0x13,
|
||||||
0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x52, 0x65, 0x61, 0x6c, 0x69,
|
0x62, 0x75, 0x72, 0x73, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x70, 0x65, 0x72, 0x5f,
|
||||||
0x74, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x73, 0x65, 0x63, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x62, 0x75, 0x72, 0x73, 0x74,
|
||||||
|
0x42, 0x79, 0x74, 0x65, 0x73, 0x50, 0x65, 0x72, 0x53, 0x65, 0x63, 0x42, 0x7f, 0x0a, 0x23, 0x63,
|
||||||
|
0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
||||||
|
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x72, 0x65, 0x61, 0x6c, 0x69,
|
||||||
|
0x74, 0x79, 0x50, 0x01, 0x5a, 0x34, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
|
0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f,
|
||||||
|
0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
|
||||||
|
0x65, 0x74, 0x2f, 0x72, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0xaa, 0x02, 0x1f, 0x58, 0x72, 0x61,
|
||||||
|
0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65,
|
||||||
|
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x52, 0x65, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x62, 0x06, 0x70, 0x72,
|
||||||
|
0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@@ -18,16 +18,19 @@ message Config {
|
|||||||
uint64 max_time_diff = 9;
|
uint64 max_time_diff = 9;
|
||||||
repeated bytes short_ids = 10;
|
repeated bytes short_ids = 10;
|
||||||
|
|
||||||
|
bytes mldsa65_seed = 11;
|
||||||
|
LimitFallback limit_fallback_upload = 12;
|
||||||
|
LimitFallback limit_fallback_download = 13;
|
||||||
|
|
||||||
string Fingerprint = 21;
|
string Fingerprint = 21;
|
||||||
string server_name = 22;
|
string server_name = 22;
|
||||||
bytes public_key = 23;
|
bytes public_key = 23;
|
||||||
bytes short_id = 24;
|
bytes short_id = 24;
|
||||||
string spider_x = 25;
|
bytes mldsa65_verify = 25;
|
||||||
repeated int64 spider_y = 26;
|
string spider_x = 26;
|
||||||
string master_key_log = 27;
|
repeated int64 spider_y = 27;
|
||||||
|
|
||||||
LimitFallback limit_fallback_upload = 28;
|
string master_key_log = 31;
|
||||||
LimitFallback limit_fallback_download = 29;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message LimitFallback {
|
message LimitFallback {
|
||||||
|
@@ -23,6 +23,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/cloudflare/circl/sign/mldsa/mldsa65"
|
||||||
utls "github.com/refraction-networking/utls"
|
utls "github.com/refraction-networking/utls"
|
||||||
"github.com/xtls/reality"
|
"github.com/xtls/reality"
|
||||||
"github.com/xtls/xray-core/common/crypto"
|
"github.com/xtls/xray-core/common/crypto"
|
||||||
@@ -56,6 +57,7 @@ func Server(c net.Conn, config *reality.Config) (net.Conn, error) {
|
|||||||
|
|
||||||
type UConn struct {
|
type UConn struct {
|
||||||
*utls.UConn
|
*utls.UConn
|
||||||
|
Config *Config
|
||||||
ServerName string
|
ServerName string
|
||||||
AuthKey []byte
|
AuthKey []byte
|
||||||
Verified bool
|
Verified bool
|
||||||
@@ -73,16 +75,34 @@ func (c *UConn) HandshakeAddress() net.Address {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *UConn) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
func (c *UConn) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
|
||||||
|
if c.Config.Show {
|
||||||
|
localAddr := c.LocalAddr().String()
|
||||||
|
curveID := *(*utls.CurveID)(unsafe.Pointer(reflect.ValueOf(c).Elem().FieldByName("curveID").UnsafeAddr()))
|
||||||
|
fmt.Printf("REALITY localAddr: %v\tis using X25519MLKEM768 for TLS' communication: %v\n", localAddr, curveID == utls.X25519MLKEM768)
|
||||||
|
fmt.Printf("REALITY localAddr: %v\tis using ML-DSA-65 for cert's extra verification: %v\n", localAddr, len(c.Config.Mldsa65Verify) > 0)
|
||||||
|
}
|
||||||
p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates")
|
p, _ := reflect.TypeOf(c.Conn).Elem().FieldByName("peerCertificates")
|
||||||
certs := *(*([]*x509.Certificate))(unsafe.Pointer(uintptr(unsafe.Pointer(c.Conn)) + p.Offset))
|
certs := *(*([]*x509.Certificate))(unsafe.Pointer(uintptr(unsafe.Pointer(c.Conn)) + p.Offset))
|
||||||
if pub, ok := certs[0].PublicKey.(ed25519.PublicKey); ok {
|
if pub, ok := certs[0].PublicKey.(ed25519.PublicKey); ok {
|
||||||
h := hmac.New(sha512.New, c.AuthKey)
|
h := hmac.New(sha512.New, c.AuthKey)
|
||||||
h.Write(pub)
|
h.Write(pub)
|
||||||
if bytes.Equal(h.Sum(nil), certs[0].Signature) {
|
if bytes.Equal(h.Sum(nil), certs[0].Signature) {
|
||||||
|
if len(c.Config.Mldsa65Verify) > 0 {
|
||||||
|
if len(certs[0].Extensions) > 0 {
|
||||||
|
h.Write(c.HandshakeState.Hello.Raw)
|
||||||
|
h.Write(c.HandshakeState.ServerHello.Raw)
|
||||||
|
verify, _ := mldsa65.Scheme().UnmarshalBinaryPublicKey(c.Config.Mldsa65Verify)
|
||||||
|
if mldsa65.Verify(verify.(*mldsa65.PublicKey), h.Sum(nil), nil, certs[0].Extensions[0].Value) {
|
||||||
c.Verified = true
|
c.Verified = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
c.Verified = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
opts := x509.VerifyOptions{
|
opts := x509.VerifyOptions{
|
||||||
DNSName: c.ServerName,
|
DNSName: c.ServerName,
|
||||||
Intermediates: x509.NewCertPool(),
|
Intermediates: x509.NewCertPool(),
|
||||||
@@ -98,7 +118,9 @@ func (c *UConn) VerifyPeerCertificate(rawCerts [][]byte, verifiedChains [][]*x50
|
|||||||
|
|
||||||
func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destination) (net.Conn, error) {
|
func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destination) (net.Conn, error) {
|
||||||
localAddr := c.LocalAddr().String()
|
localAddr := c.LocalAddr().String()
|
||||||
uConn := &UConn{}
|
uConn := &UConn{
|
||||||
|
Config: config,
|
||||||
|
}
|
||||||
utlsConfig := &utls.Config{
|
utlsConfig := &utls.Config{
|
||||||
VerifyPeerCertificate: uConn.VerifyPeerCertificate,
|
VerifyPeerCertificate: uConn.VerifyPeerCertificate,
|
||||||
ServerName: config.ServerName,
|
ServerName: config.ServerName,
|
||||||
@@ -127,16 +149,20 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
|
|||||||
binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix()))
|
binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix()))
|
||||||
copy(hello.SessionId[8:], config.ShortId)
|
copy(hello.SessionId[8:], config.ShortId)
|
||||||
if config.Show {
|
if config.Show {
|
||||||
errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\thello.SessionId[:16]: %v\n", localAddr, hello.SessionId[:16]))
|
fmt.Printf("REALITY localAddr: %v\thello.SessionId[:16]: %v\n", localAddr, hello.SessionId[:16])
|
||||||
}
|
}
|
||||||
publicKey, err := ecdh.X25519().NewPublicKey(config.PublicKey)
|
publicKey, err := ecdh.X25519().NewPublicKey(config.PublicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("REALITY: publicKey == nil")
|
return nil, errors.New("REALITY: publicKey == nil")
|
||||||
}
|
}
|
||||||
if uConn.HandshakeState.State13.KeyShareKeys.Ecdhe == nil {
|
ecdhe := uConn.HandshakeState.State13.KeyShareKeys.Ecdhe
|
||||||
|
if ecdhe == nil {
|
||||||
|
ecdhe = uConn.HandshakeState.State13.KeyShareKeys.MlkemEcdhe
|
||||||
|
}
|
||||||
|
if ecdhe == nil {
|
||||||
return nil, errors.New("Current fingerprint ", uConn.ClientHelloID.Client, uConn.ClientHelloID.Version, " does not support TLS 1.3, REALITY handshake cannot establish.")
|
return nil, errors.New("Current fingerprint ", uConn.ClientHelloID.Client, uConn.ClientHelloID.Version, " does not support TLS 1.3, REALITY handshake cannot establish.")
|
||||||
}
|
}
|
||||||
uConn.AuthKey, _ = uConn.HandshakeState.State13.KeyShareKeys.Ecdhe.ECDH(publicKey)
|
uConn.AuthKey, _ = ecdhe.ECDH(publicKey)
|
||||||
if uConn.AuthKey == nil {
|
if uConn.AuthKey == nil {
|
||||||
return nil, errors.New("REALITY: SharedKey == nil")
|
return nil, errors.New("REALITY: SharedKey == nil")
|
||||||
}
|
}
|
||||||
@@ -146,7 +172,7 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
|
|||||||
block, _ := aes.NewCipher(uConn.AuthKey)
|
block, _ := aes.NewCipher(uConn.AuthKey)
|
||||||
aead, _ := cipher.NewGCM(block)
|
aead, _ := cipher.NewGCM(block)
|
||||||
if config.Show {
|
if config.Show {
|
||||||
errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\tuConn.AuthKey[:16]: %v\tAEAD: %T\n", localAddr, uConn.AuthKey[:16], aead))
|
fmt.Printf("REALITY localAddr: %v\tuConn.AuthKey[:16]: %v\tAEAD: %T\n", localAddr, uConn.AuthKey[:16], aead)
|
||||||
}
|
}
|
||||||
aead.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw)
|
aead.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw)
|
||||||
copy(hello.Raw[39:], hello.SessionId)
|
copy(hello.Raw[39:], hello.SessionId)
|
||||||
@@ -155,14 +181,14 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if config.Show {
|
if config.Show {
|
||||||
errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\tuConn.Verified: %v\n", localAddr, uConn.Verified))
|
fmt.Printf("REALITY localAddr: %v\tuConn.Verified: %v\n", localAddr, uConn.Verified)
|
||||||
}
|
}
|
||||||
if !uConn.Verified {
|
if !uConn.Verified {
|
||||||
go func() {
|
go func() {
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Transport: &http2.Transport{
|
Transport: &http2.Transport{
|
||||||
DialTLSContext: func(ctx context.Context, network, addr string, cfg *gotls.Config) (net.Conn, error) {
|
DialTLSContext: func(ctx context.Context, network, addr string, cfg *gotls.Config) (net.Conn, error) {
|
||||||
errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\tDialTLSContext\n", localAddr))
|
fmt.Printf("REALITY localAddr: %v\tDialTLSContext\n", localAddr)
|
||||||
return uConn, nil
|
return uConn, nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -199,7 +225,7 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
|
|||||||
}
|
}
|
||||||
req.Header.Set("User-Agent", fingerprint.Client) // TODO: User-Agent map
|
req.Header.Set("User-Agent", fingerprint.Client) // TODO: User-Agent map
|
||||||
if first && config.Show {
|
if first && config.Show {
|
||||||
errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\treq.UserAgent(): %v\n", localAddr, req.UserAgent()))
|
fmt.Printf("REALITY localAddr: %v\treq.UserAgent(): %v\n", localAddr, req.UserAgent())
|
||||||
}
|
}
|
||||||
times := 1
|
times := 1
|
||||||
if !first {
|
if !first {
|
||||||
@@ -227,9 +253,9 @@ func UClient(c net.Conn, config *Config, ctx context.Context, dest net.Destinati
|
|||||||
}
|
}
|
||||||
req.URL.Path = getPathLocked(paths)
|
req.URL.Path = getPathLocked(paths)
|
||||||
if config.Show {
|
if config.Show {
|
||||||
errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\treq.Referer(): %v\n", localAddr, req.Referer()))
|
fmt.Printf("REALITY localAddr: %v\treq.Referer(): %v\n", localAddr, req.Referer())
|
||||||
errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\tlen(body): %v\n", localAddr, len(body)))
|
fmt.Printf("REALITY localAddr: %v\tlen(body): %v\n", localAddr, len(body))
|
||||||
errors.LogInfo(ctx, fmt.Sprintf("REALITY localAddr: %v\tlen(paths): %v\n", localAddr, len(paths)))
|
fmt.Printf("REALITY localAddr: %v\tlen(paths): %v\n", localAddr, len(paths))
|
||||||
}
|
}
|
||||||
maps.Unlock()
|
maps.Unlock()
|
||||||
if !first {
|
if !first {
|
||||||
|
@@ -64,26 +64,6 @@ func applyOutboundSocketOptions(network string, address string, fd uintptr, conf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.TcpKeepAliveInterval > 0 || config.TcpKeepAliveIdle > 0 {
|
|
||||||
if config.TcpKeepAliveInterval > 0 {
|
|
||||||
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, int(config.TcpKeepAliveInterval)); err != nil {
|
|
||||||
return errors.New("failed to set TCP_KEEPINTVL", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if config.TcpKeepAliveIdle > 0 {
|
|
||||||
if err := syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, int(config.TcpKeepAliveIdle)); err != nil {
|
|
||||||
return errors.New("failed to set TCP_KEEPIDLE", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 1); err != nil {
|
|
||||||
return errors.New("failed to set SO_KEEPALIVE", err)
|
|
||||||
}
|
|
||||||
} else if config.TcpKeepAliveInterval < 0 || config.TcpKeepAliveIdle < 0 {
|
|
||||||
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, 0); err != nil {
|
|
||||||
return errors.New("failed to unset SO_KEEPALIVE", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.TcpCongestion != "" {
|
if config.TcpCongestion != "" {
|
||||||
if err := syscall.SetsockoptString(int(fd), syscall.SOL_TCP, syscall.TCP_CONGESTION, config.TcpCongestion); err != nil {
|
if err := syscall.SetsockoptString(int(fd), syscall.SOL_TCP, syscall.TCP_CONGESTION, config.TcpCongestion); err != nil {
|
||||||
return errors.New("failed to set TCP_CONGESTION", err)
|
return errors.New("failed to set TCP_CONGESTION", err)
|
||||||
|
@@ -161,7 +161,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea
|
|||||||
transport = &http3.Transport{
|
transport = &http3.Transport{
|
||||||
QUICConfig: quicConfig,
|
QUICConfig: quicConfig,
|
||||||
TLSClientConfig: gotlsConfig,
|
TLSClientConfig: gotlsConfig,
|
||||||
Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
|
Dial: func(ctx context.Context, addr string, tlsCfg *gotls.Config, cfg *quic.Config) (*quic.Conn, error) {
|
||||||
conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
|
conn, err := internet.DialSystem(ctx, dest, streamSettings.SocketSettings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@@ -3,6 +3,7 @@ package internet
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
gonet "net"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -87,14 +88,34 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne
|
|||||||
Dest: destAddr,
|
Dest: destAddr,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
goStdKeepAlive := time.Duration(0)
|
// Chrome defaults
|
||||||
if sockopt != nil && (sockopt.TcpKeepAliveInterval != 0 || sockopt.TcpKeepAliveIdle != 0) {
|
keepAliveConfig := gonet.KeepAliveConfig{
|
||||||
goStdKeepAlive = time.Duration(-1)
|
Enable: true,
|
||||||
|
Idle: 45 * time.Second,
|
||||||
|
Interval: 45 * time.Second,
|
||||||
|
Count: -1,
|
||||||
|
}
|
||||||
|
keepAlive := time.Duration(0)
|
||||||
|
if sockopt != nil {
|
||||||
|
if sockopt.TcpKeepAliveIdle*sockopt.TcpKeepAliveInterval < 0 {
|
||||||
|
return nil, errors.New("invalid TcpKeepAliveIdle or TcpKeepAliveInterval value: ", sockopt.TcpKeepAliveIdle, " ", sockopt.TcpKeepAliveInterval)
|
||||||
|
}
|
||||||
|
if sockopt.TcpKeepAliveIdle < 0 || sockopt.TcpKeepAliveInterval < 0 {
|
||||||
|
keepAlive = -1
|
||||||
|
keepAliveConfig.Enable = false
|
||||||
|
}
|
||||||
|
if sockopt.TcpKeepAliveIdle > 0 {
|
||||||
|
keepAliveConfig.Idle = time.Duration(sockopt.TcpKeepAliveIdle) * time.Second
|
||||||
|
}
|
||||||
|
if sockopt.TcpKeepAliveInterval > 0 {
|
||||||
|
keepAliveConfig.Interval = time.Duration(sockopt.TcpKeepAliveInterval) * time.Second
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dialer := &net.Dialer{
|
dialer := &net.Dialer{
|
||||||
Timeout: time.Second * 16,
|
Timeout: time.Second * 16,
|
||||||
LocalAddr: resolveSrcAddr(dest.Network, src),
|
LocalAddr: resolveSrcAddr(dest.Network, src),
|
||||||
KeepAlive: goStdKeepAlive,
|
KeepAlive: keepAlive,
|
||||||
|
KeepAliveConfig: keepAliveConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
if sockopt != nil || len(d.controllers) > 0 {
|
if sockopt != nil || len(d.controllers) > 0 {
|
||||||
|
@@ -2,6 +2,7 @@ package internet
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
gonet "net"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -88,9 +89,25 @@ func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *S
|
|||||||
network = addr.Network()
|
network = addr.Network()
|
||||||
address = addr.String()
|
address = addr.String()
|
||||||
lc.Control = getControlFunc(ctx, sockopt, dl.controllers)
|
lc.Control = getControlFunc(ctx, sockopt, dl.controllers)
|
||||||
|
// default disable keepalive
|
||||||
|
lc.KeepAlive = -1
|
||||||
if sockopt != nil {
|
if sockopt != nil {
|
||||||
if sockopt.TcpKeepAliveInterval != 0 || sockopt.TcpKeepAliveIdle != 0 {
|
if sockopt.TcpKeepAliveIdle*sockopt.TcpKeepAliveInterval < 0 {
|
||||||
lc.KeepAlive = time.Duration(-1)
|
return nil, errors.New("invalid TcpKeepAliveIdle or TcpKeepAliveInterval value: ", sockopt.TcpKeepAliveIdle, " ", sockopt.TcpKeepAliveInterval)
|
||||||
|
}
|
||||||
|
lc.KeepAliveConfig = gonet.KeepAliveConfig{
|
||||||
|
Enable: false,
|
||||||
|
Idle: -1,
|
||||||
|
Interval: -1,
|
||||||
|
Count: -1,
|
||||||
|
}
|
||||||
|
if sockopt.TcpKeepAliveIdle > 0 {
|
||||||
|
lc.KeepAliveConfig.Enable = true
|
||||||
|
lc.KeepAliveConfig.Idle = time.Duration(sockopt.TcpKeepAliveIdle) * time.Second
|
||||||
|
}
|
||||||
|
if sockopt.TcpKeepAliveInterval > 0 {
|
||||||
|
lc.KeepAliveConfig.Enable = true
|
||||||
|
lc.KeepAliveConfig.Interval = time.Duration(sockopt.TcpKeepAliveInterval) * time.Second
|
||||||
}
|
}
|
||||||
if sockopt.TcpMptcp {
|
if sockopt.TcpMptcp {
|
||||||
lc.SetMultipathTCP(true)
|
lc.SetMultipathTCP(true)
|
||||||
|
@@ -2,6 +2,7 @@ package tcp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
gotls "crypto/tls"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -15,10 +16,6 @@ import (
|
|||||||
"github.com/xtls/xray-core/transport/internet/tls"
|
"github.com/xtls/xray-core/transport/internet/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
func IsFromMitm(str string) bool {
|
|
||||||
return strings.ToLower(str) == "frommitm"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial dials a new TCP connection to the given destination.
|
// Dial dials a new TCP connection to the given destination.
|
||||||
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
|
func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.MemoryStreamConfig) (stat.Connection, error) {
|
||||||
errors.LogInfo(ctx, "dialing TCP to ", dest)
|
errors.LogInfo(ctx, "dialing TCP to ", dest)
|
||||||
@@ -30,14 +27,17 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|||||||
if config := tls.ConfigFromStreamSettings(streamSettings); config != nil {
|
if config := tls.ConfigFromStreamSettings(streamSettings); config != nil {
|
||||||
mitmServerName := session.MitmServerNameFromContext(ctx)
|
mitmServerName := session.MitmServerNameFromContext(ctx)
|
||||||
mitmAlpn11 := session.MitmAlpn11FromContext(ctx)
|
mitmAlpn11 := session.MitmAlpn11FromContext(ctx)
|
||||||
tlsConfig := config.GetTLSConfig(tls.WithDestination(dest))
|
var tlsConfig *gotls.Config
|
||||||
if IsFromMitm(tlsConfig.ServerName) {
|
if tls.IsFromMitm(config.ServerName) {
|
||||||
tlsConfig.ServerName = mitmServerName
|
tlsConfig = config.GetTLSConfig(tls.WithOverrideName(mitmServerName))
|
||||||
|
} else {
|
||||||
|
tlsConfig = config.GetTLSConfig(tls.WithDestination(dest))
|
||||||
}
|
}
|
||||||
|
|
||||||
isFromMitmVerify := false
|
isFromMitmVerify := false
|
||||||
if r, ok := tlsConfig.Rand.(*tls.RandCarrier); ok && len(r.VerifyPeerCertInNames) > 0 {
|
if r, ok := tlsConfig.Rand.(*tls.RandCarrier); ok && len(r.VerifyPeerCertInNames) > 0 {
|
||||||
for i, name := range r.VerifyPeerCertInNames {
|
for i, name := range r.VerifyPeerCertInNames {
|
||||||
if IsFromMitm(name) {
|
if tls.IsFromMitm(name) {
|
||||||
isFromMitmVerify = true
|
isFromMitmVerify = true
|
||||||
r.VerifyPeerCertInNames[0], r.VerifyPeerCertInNames[i] = r.VerifyPeerCertInNames[i], r.VerifyPeerCertInNames[0]
|
r.VerifyPeerCertInNames[0], r.VerifyPeerCertInNames[i] = r.VerifyPeerCertInNames[i], r.VerifyPeerCertInNames[0]
|
||||||
r.VerifyPeerCertInNames = r.VerifyPeerCertInNames[1:]
|
r.VerifyPeerCertInNames = r.VerifyPeerCertInNames[1:]
|
||||||
@@ -56,7 +56,7 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isFromMitmAlpn := len(tlsConfig.NextProtos) == 1 && IsFromMitm(tlsConfig.NextProtos[0])
|
isFromMitmAlpn := len(tlsConfig.NextProtos) == 1 && tls.IsFromMitm(tlsConfig.NextProtos[0])
|
||||||
if isFromMitmAlpn {
|
if isFromMitmAlpn {
|
||||||
if mitmAlpn11 {
|
if mitmAlpn11 {
|
||||||
tlsConfig.NextProtos[0] = "http/1.1"
|
tlsConfig.NextProtos[0] = "http/1.1"
|
||||||
|
@@ -42,6 +42,9 @@ func ListenTCP(ctx context.Context, address net.Address, port net.Port, streamSe
|
|||||||
var listener net.Listener
|
var listener net.Listener
|
||||||
var err error
|
var err error
|
||||||
if port == net.Port(0) { // unix
|
if port == net.Port(0) { // unix
|
||||||
|
if !address.Family().IsDomain() {
|
||||||
|
return nil, errors.New("invalid unix listen: ", address).AtError()
|
||||||
|
}
|
||||||
listener, err = internet.ListenSystem(ctx, &net.UnixAddr{
|
listener, err = internet.ListenSystem(ctx, &net.UnixAddr{
|
||||||
Name: address.Domain(),
|
Name: address.Domain(),
|
||||||
Net: "unix",
|
Net: "unix",
|
||||||
|
@@ -275,6 +275,9 @@ func getNewGetCertificateFunc(certs []*tls.Certificate, rejectUnknownSNI bool) f
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) parseServerName() string {
|
func (c *Config) parseServerName() string {
|
||||||
|
if IsFromMitm(c.ServerName) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
return c.ServerName
|
return c.ServerName
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,6 +447,16 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
|||||||
config.KeyLogWriter = writer
|
config.KeyLogWriter = writer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(c.EchConfigList) > 0 || len(c.EchServerKeys) > 0 {
|
||||||
|
err := ApplyECH(c, config)
|
||||||
|
if err != nil {
|
||||||
|
if c.EchForceQuery == "full" {
|
||||||
|
errors.LogError(context.Background(), err)
|
||||||
|
} else {
|
||||||
|
errors.LogInfo(context.Background(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
@@ -463,6 +476,12 @@ func WithDestination(dest net.Destination) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithOverrideName(serverName string) Option {
|
||||||
|
return func(config *tls.Config) {
|
||||||
|
config.ServerName = serverName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithNextProto sets the ALPN values in TLS config.
|
// WithNextProto sets the ALPN values in TLS config.
|
||||||
func WithNextProto(protocol ...string) Option {
|
func WithNextProto(protocol ...string) Option {
|
||||||
return func(config *tls.Config) {
|
return func(config *tls.Config) {
|
||||||
@@ -490,7 +509,7 @@ func ParseCurveName(curveNames []string) []tls.CurveID {
|
|||||||
"curvep384": tls.CurveP384,
|
"curvep384": tls.CurveP384,
|
||||||
"curvep521": tls.CurveP521,
|
"curvep521": tls.CurveP521,
|
||||||
"x25519": tls.X25519,
|
"x25519": tls.X25519,
|
||||||
"x25519kyber768draft00": 0x6399,
|
"x25519mlkem768": tls.X25519MLKEM768,
|
||||||
}
|
}
|
||||||
|
|
||||||
var curveIDs []tls.CurveID
|
var curveIDs []tls.CurveID
|
||||||
@@ -503,3 +522,7 @@ func ParseCurveName(curveNames []string) []tls.CurveID {
|
|||||||
}
|
}
|
||||||
return curveIDs
|
return curveIDs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsFromMitm(str string) bool {
|
||||||
|
return strings.ToLower(str) == "frommitm"
|
||||||
|
}
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
package tls
|
package tls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
internet "github.com/xtls/xray-core/transport/internet"
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
@@ -217,6 +218,10 @@ type Config struct {
|
|||||||
// @Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried.
|
// @Document After allow_insecure (automatically), if the server's cert can't be verified by any of these names, pinned_peer_certificate_chain_sha256 will be tried.
|
||||||
// @Critical
|
// @Critical
|
||||||
VerifyPeerCertInNames []string `protobuf:"bytes,17,rep,name=verify_peer_cert_in_names,json=verifyPeerCertInNames,proto3" json:"verify_peer_cert_in_names,omitempty"`
|
VerifyPeerCertInNames []string `protobuf:"bytes,17,rep,name=verify_peer_cert_in_names,json=verifyPeerCertInNames,proto3" json:"verify_peer_cert_in_names,omitempty"`
|
||||||
|
EchServerKeys []byte `protobuf:"bytes,18,opt,name=ech_server_keys,json=echServerKeys,proto3" json:"ech_server_keys,omitempty"`
|
||||||
|
EchConfigList string `protobuf:"bytes,19,opt,name=ech_config_list,json=echConfigList,proto3" json:"ech_config_list,omitempty"`
|
||||||
|
EchForceQuery string `protobuf:"bytes,20,opt,name=ech_force_query,json=echForceQuery,proto3" json:"ech_force_query,omitempty"`
|
||||||
|
EchSocketSettings *internet.SocketConfig `protobuf:"bytes,21,opt,name=ech_socket_settings,json=echSocketSettings,proto3" json:"ech_socket_settings,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Config) Reset() {
|
func (x *Config) Reset() {
|
||||||
@@ -361,6 +366,34 @@ func (x *Config) GetVerifyPeerCertInNames() []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetEchServerKeys() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.EchServerKeys
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetEchConfigList() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.EchConfigList
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetEchForceQuery() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.EchForceQuery
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *Config) GetEchSocketSettings() *internet.SocketConfig {
|
||||||
|
if x != nil {
|
||||||
|
return x.EchSocketSettings
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
|
var File_transport_internet_tls_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
var file_transport_internet_tls_config_proto_rawDesc = []byte{
|
||||||
@@ -368,81 +401,96 @@ 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,
|
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,
|
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,
|
0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74,
|
||||||
0x6c, 0x73, 0x22, 0x83, 0x03, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
|
0x6c, 0x73, 0x1a, 0x1f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e,
|
||||||
0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
|
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72,
|
||||||
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
|
0x6f, 0x74, 0x6f, 0x22, 0x83, 0x03, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
|
||||||
0x63, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x61, 0x74, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
|
||||||
0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x18,
|
0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66,
|
||||||
0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
|
0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01,
|
||||||
0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e,
|
0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65,
|
||||||
0x74, 0x6c, 0x73, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x2e,
|
0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2e, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72,
|
||||||
0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x12, 0x23, 0x0a, 0x0d,
|
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
|
||||||
0x6f, 0x63, 0x73, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20,
|
0x2e, 0x74, 0x6c, 0x73, 0x2e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
|
||||||
0x01, 0x28, 0x04, 0x52, 0x0c, 0x6f, 0x63, 0x73, 0x70, 0x53, 0x74, 0x61, 0x70, 0x6c, 0x69, 0x6e,
|
0x2e, 0x55, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x75, 0x73, 0x61, 0x67, 0x65, 0x12, 0x23, 0x0a,
|
||||||
0x67, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
|
0x0d, 0x6f, 0x63, 0x73, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x18, 0x04,
|
||||||
0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x65, 0x72,
|
0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6f, 0x63, 0x73, 0x70, 0x53, 0x74, 0x61, 0x70, 0x6c, 0x69,
|
||||||
0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08,
|
0x6e, 0x67, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
|
||||||
0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x65,
|
||||||
0x6b, 0x65, 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x28, 0x0a, 0x10, 0x4f, 0x6e, 0x65, 0x5f, 0x74,
|
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a,
|
||||||
0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28,
|
0x08, 0x6b, 0x65, 0x79, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x08, 0x52, 0x0e, 0x4f, 0x6e, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x69, 0x6e,
|
0x07, 0x6b, 0x65, 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x28, 0x0a, 0x10, 0x4f, 0x6e, 0x65, 0x5f,
|
||||||
0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e,
|
0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, 0x6f, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01,
|
||||||
0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x68, 0x61,
|
0x28, 0x08, 0x52, 0x0e, 0x4f, 0x6e, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x69,
|
||||||
0x69, 0x6e, 0x22, 0x44, 0x0a, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x45,
|
0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69,
|
||||||
0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14, 0x0a,
|
0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x68,
|
||||||
0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49, 0x46,
|
0x61, 0x69, 0x6e, 0x22, 0x44, 0x0a, 0x05, 0x55, 0x73, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x0c,
|
||||||
0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59,
|
0x45, 0x4e, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x4d, 0x45, 0x4e, 0x54, 0x10, 0x00, 0x12, 0x14,
|
||||||
0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0x9a, 0x06, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
|
0x0a, 0x10, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54, 0x59, 0x5f, 0x56, 0x45, 0x52, 0x49,
|
||||||
0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e, 0x73,
|
0x46, 0x59, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x41, 0x55, 0x54, 0x48, 0x4f, 0x52, 0x49, 0x54,
|
||||||
0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x6c,
|
0x59, 0x5f, 0x49, 0x53, 0x53, 0x55, 0x45, 0x10, 0x02, 0x22, 0xe9, 0x07, 0x0a, 0x06, 0x43, 0x6f,
|
||||||
0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63, 0x65,
|
0x6e, 0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x6e,
|
||||||
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c,
|
||||||
0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
|
0x6c, 0x6f, 0x77, 0x49, 0x6e, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x4a, 0x0a, 0x0b, 0x63,
|
||||||
0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x2e, 0x43, 0x65,
|
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
|
||||||
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74, 0x69,
|
0x32, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
|
||||||
0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
|
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x2e, 0x43,
|
||||||
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65, 0x72,
|
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x52, 0x0b, 0x63, 0x65, 0x72, 0x74,
|
||||||
0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x5f,
|
0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76, 0x65,
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c,
|
0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x65,
|
||||||
0x6e, 0x65, 0x78, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3a, 0x0a, 0x19,
|
0x72, 0x76, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6e, 0x65, 0x78, 0x74,
|
||||||
0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x72,
|
0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52,
|
||||||
0x65, 0x73, 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52,
|
0x0c, 0x6e, 0x65, 0x78, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x3a, 0x0a,
|
||||||
0x17, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65,
|
0x19, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
|
||||||
0x73, 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73, 0x61,
|
0x72, 0x65, 0x73, 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08,
|
||||||
0x62, 0x6c, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18,
|
0x52, 0x17, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52,
|
||||||
0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x79,
|
0x65, 0x73, 0x75, 0x6d, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x69, 0x73,
|
||||||
0x73, 0x74, 0x65, 0x6d, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e, 0x5f,
|
0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x72, 0x6f, 0x6f, 0x74,
|
||||||
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d,
|
0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x53,
|
||||||
0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x78,
|
0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x69, 0x6e,
|
||||||
0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
|
0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
|
||||||
0x6d, 0x61, 0x78, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x69,
|
0x6d, 0x69, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61,
|
||||||
0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28,
|
0x78, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x09, 0x52, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73, 0x12,
|
0x0a, 0x6d, 0x61, 0x78, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x63,
|
||||||
0x20, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x0b,
|
0x69, 0x70, 0x68, 0x65, 0x72, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01,
|
||||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e,
|
0x28, 0x09, 0x52, 0x0c, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x53, 0x75, 0x69, 0x74, 0x65, 0x73,
|
||||||
0x74, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x6e, 0x6b, 0x6e,
|
0x12, 0x20, 0x0a, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18,
|
||||||
0x6f, 0x77, 0x6e, 0x5f, 0x73, 0x6e, 0x69, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x72,
|
0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69,
|
||||||
0x65, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53, 0x6e, 0x69, 0x12,
|
0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x6e, 0x6b,
|
||||||
0x4e, 0x0a, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63,
|
0x6e, 0x6f, 0x77, 0x6e, 0x5f, 0x73, 0x6e, 0x69, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10,
|
||||||
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e,
|
0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x53, 0x6e, 0x69,
|
||||||
0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x20, 0x70,
|
0x12, 0x4e, 0x0a, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f,
|
||||||
0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
|
0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x69,
|
||||||
0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12,
|
0x6e, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x20,
|
||||||
0x57, 0x0a, 0x29, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63,
|
0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
|
||||||
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69,
|
0x69, 0x63, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36,
|
||||||
0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0e, 0x20, 0x03,
|
0x12, 0x57, 0x0a, 0x29, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f,
|
||||||
0x28, 0x0c, 0x52, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65,
|
0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c,
|
||||||
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b,
|
0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x0e, 0x20,
|
||||||
0x65, 0x79, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x73, 0x74,
|
0x03, 0x28, 0x0c, 0x52, 0x24, 0x70, 0x69, 0x6e, 0x6e, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x43,
|
||||||
0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09,
|
0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63,
|
||||||
0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x12, 0x2b,
|
0x4b, 0x65, 0x79, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x61, 0x73,
|
||||||
0x0a, 0x11, 0x63, 0x75, 0x72, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e,
|
0x74, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x0f, 0x20, 0x01, 0x28,
|
||||||
0x63, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, 0x76, 0x65,
|
0x09, 0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4c, 0x6f, 0x67, 0x12,
|
||||||
0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x19, 0x76,
|
0x2b, 0x0a, 0x11, 0x63, 0x75, 0x72, 0x76, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65,
|
||||||
0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x5f,
|
0x6e, 0x63, 0x65, 0x73, 0x18, 0x10, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x75, 0x72, 0x76,
|
||||||
0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15,
|
0x65, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x38, 0x0a, 0x19,
|
||||||
0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x49, 0x6e,
|
0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74,
|
||||||
0x4e, 0x61, 0x6d, 0x65, 0x73, 0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61,
|
0x5f, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x09, 0x52,
|
||||||
|
0x15, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x49,
|
||||||
|
0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x63, 0x68, 0x5f, 0x73, 0x65,
|
||||||
|
0x72, 0x76, 0x65, 0x72, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0c, 0x52,
|
||||||
|
0x0d, 0x65, 0x63, 0x68, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x26,
|
||||||
|
0x0a, 0x0f, 0x65, 0x63, 0x68, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x6c, 0x69, 0x73,
|
||||||
|
0x74, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x63, 0x68, 0x43, 0x6f, 0x6e, 0x66,
|
||||||
|
0x69, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x65, 0x63, 0x68, 0x5f, 0x66, 0x6f,
|
||||||
|
0x72, 0x63, 0x65, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
|
0x0d, 0x65, 0x63, 0x68, 0x46, 0x6f, 0x72, 0x63, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x55,
|
||||||
|
0x0a, 0x13, 0x65, 0x63, 0x68, 0x5f, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x5f, 0x73, 0x65, 0x74,
|
||||||
|
0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x78, 0x72,
|
||||||
|
0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74,
|
||||||
|
0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
|
||||||
|
0x69, 0x67, 0x52, 0x11, 0x65, 0x63, 0x68, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74,
|
||||||
|
0x74, 0x69, 0x6e, 0x67, 0x73, 0x42, 0x73, 0x0a, 0x1f, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61,
|
||||||
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
|
0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
|
||||||
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68,
|
0x72, 0x6e, 0x65, 0x74, 0x2e, 0x74, 0x6c, 0x73, 0x50, 0x01, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68,
|
||||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
|
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79,
|
||||||
@@ -471,15 +519,17 @@ var file_transport_internet_tls_config_proto_goTypes = []any{
|
|||||||
(Certificate_Usage)(0), // 0: xray.transport.internet.tls.Certificate.Usage
|
(Certificate_Usage)(0), // 0: xray.transport.internet.tls.Certificate.Usage
|
||||||
(*Certificate)(nil), // 1: xray.transport.internet.tls.Certificate
|
(*Certificate)(nil), // 1: xray.transport.internet.tls.Certificate
|
||||||
(*Config)(nil), // 2: xray.transport.internet.tls.Config
|
(*Config)(nil), // 2: xray.transport.internet.tls.Config
|
||||||
|
(*internet.SocketConfig)(nil), // 3: xray.transport.internet.SocketConfig
|
||||||
}
|
}
|
||||||
var file_transport_internet_tls_config_proto_depIdxs = []int32{
|
var file_transport_internet_tls_config_proto_depIdxs = []int32{
|
||||||
0, // 0: xray.transport.internet.tls.Certificate.usage:type_name -> xray.transport.internet.tls.Certificate.Usage
|
0, // 0: xray.transport.internet.tls.Certificate.usage:type_name -> xray.transport.internet.tls.Certificate.Usage
|
||||||
1, // 1: xray.transport.internet.tls.Config.certificate:type_name -> xray.transport.internet.tls.Certificate
|
1, // 1: xray.transport.internet.tls.Config.certificate:type_name -> xray.transport.internet.tls.Certificate
|
||||||
2, // [2:2] is the sub-list for method output_type
|
3, // 2: xray.transport.internet.tls.Config.ech_socket_settings:type_name -> xray.transport.internet.SocketConfig
|
||||||
2, // [2:2] is the sub-list for method input_type
|
3, // [3:3] is the sub-list for method output_type
|
||||||
2, // [2:2] is the sub-list for extension type_name
|
3, // [3:3] is the sub-list for method input_type
|
||||||
2, // [2:2] is the sub-list for extension extendee
|
3, // [3:3] is the sub-list for extension type_name
|
||||||
0, // [0:2] is the sub-list for field type_name
|
3, // [3:3] is the sub-list for extension extendee
|
||||||
|
0, // [0:3] is the sub-list for field type_name
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { file_transport_internet_tls_config_proto_init() }
|
func init() { file_transport_internet_tls_config_proto_init() }
|
||||||
|
@@ -6,6 +6,8 @@ option go_package = "github.com/xtls/xray-core/transport/internet/tls";
|
|||||||
option java_package = "com.xray.transport.internet.tls";
|
option java_package = "com.xray.transport.internet.tls";
|
||||||
option java_multiple_files = true;
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
import "transport/internet/config.proto";
|
||||||
|
|
||||||
message Certificate {
|
message Certificate {
|
||||||
// TLS certificate in x509 format.
|
// TLS certificate in x509 format.
|
||||||
bytes certificate = 1;
|
bytes certificate = 1;
|
||||||
@@ -91,4 +93,12 @@ message Config {
|
|||||||
@Critical
|
@Critical
|
||||||
*/
|
*/
|
||||||
repeated string verify_peer_cert_in_names = 17;
|
repeated string verify_peer_cert_in_names = 17;
|
||||||
|
|
||||||
|
bytes ech_server_keys = 18;
|
||||||
|
|
||||||
|
string ech_config_list = 19;
|
||||||
|
|
||||||
|
string ech_force_query = 20;
|
||||||
|
|
||||||
|
SocketConfig ech_socket_settings = 21;
|
||||||
}
|
}
|
||||||
|
430
transport/internet/tls/ech.go
Normal file
430
transport/internet/tls/ech.go
Normal file
@@ -0,0 +1,430 @@
|
|||||||
|
package tls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/ecdh"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
utls "github.com/refraction-networking/utls"
|
||||||
|
"github.com/xtls/xray-core/common/crypto"
|
||||||
|
dns2 "github.com/xtls/xray-core/features/dns"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
"github.com/xtls/reality"
|
||||||
|
"github.com/xtls/reality/hpke"
|
||||||
|
"github.com/xtls/xray-core/common/errors"
|
||||||
|
"github.com/xtls/xray-core/common/net"
|
||||||
|
"github.com/xtls/xray-core/common/utils"
|
||||||
|
"github.com/xtls/xray-core/transport/internet"
|
||||||
|
"golang.org/x/crypto/cryptobyte"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ApplyECH(c *Config, config *tls.Config) error {
|
||||||
|
var ECHConfig []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
var nameToQuery string
|
||||||
|
if net.ParseAddress(config.ServerName).Family().IsDomain() {
|
||||||
|
nameToQuery = config.ServerName
|
||||||
|
}
|
||||||
|
var DNSServer string
|
||||||
|
|
||||||
|
// for server
|
||||||
|
if len(c.EchServerKeys) != 0 {
|
||||||
|
KeySets, err := ConvertToGoECHKeys(c.EchServerKeys)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("Failed to unmarshal ECHKeySetList: ", err)
|
||||||
|
}
|
||||||
|
config.EncryptedClientHelloKeys = KeySets
|
||||||
|
}
|
||||||
|
|
||||||
|
// for client
|
||||||
|
if len(c.EchConfigList) != 0 {
|
||||||
|
ECHForceQuery := c.EchForceQuery
|
||||||
|
switch ECHForceQuery {
|
||||||
|
case "none", "half", "full":
|
||||||
|
case "":
|
||||||
|
ECHForceQuery = "none" // default to none
|
||||||
|
default:
|
||||||
|
panic("Invalid ECHForceQuery: " + c.EchForceQuery)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
// if failed to get ECHConfig, use an invalid one to make connection fail
|
||||||
|
if err != nil || len(ECHConfig) == 0 {
|
||||||
|
if ECHForceQuery == "full" {
|
||||||
|
ECHConfig = []byte{1, 1, 4, 5, 1, 4}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.EncryptedClientHelloConfigList = ECHConfig
|
||||||
|
}()
|
||||||
|
// direct base64 config
|
||||||
|
if strings.Contains(c.EchConfigList, "://") {
|
||||||
|
// query config from dns
|
||||||
|
parts := strings.Split(c.EchConfigList, "+")
|
||||||
|
if len(parts) == 2 {
|
||||||
|
// parse ECH DNS server in format of "example.com+https://1.1.1.1/dns-query"
|
||||||
|
nameToQuery = parts[0]
|
||||||
|
DNSServer = parts[1]
|
||||||
|
} else if len(parts) == 1 {
|
||||||
|
// normal format
|
||||||
|
DNSServer = parts[0]
|
||||||
|
} else {
|
||||||
|
return errors.New("Invalid ECH DNS server format: ", c.EchConfigList)
|
||||||
|
}
|
||||||
|
if nameToQuery == "" {
|
||||||
|
return errors.New("Using DNS for ECH Config needs serverName or use Server format example.com+https://1.1.1.1/dns-query")
|
||||||
|
}
|
||||||
|
ECHConfig, err = QueryRecord(nameToQuery, DNSServer, c.EchForceQuery, c.EchSocketSettings)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("Failed to query ECH DNS record for domain: ", nameToQuery, " at server: ", DNSServer).Base(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ECHConfig, err = base64.StdEncoding.DecodeString(c.EchConfigList)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("Failed to unmarshal ECHConfigList: ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ECHConfigCache struct {
|
||||||
|
configRecord atomic.Pointer[echConfigRecord]
|
||||||
|
// updateLock is not for preventing concurrent read/write, but for preventing concurrent update
|
||||||
|
UpdateLock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
type echConfigRecord struct {
|
||||||
|
config []byte
|
||||||
|
expire time.Time
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// The keys for both maps must be generated by ECHCacheKey().
|
||||||
|
GlobalECHConfigCache = utils.NewTypedSyncMap[string, *ECHConfigCache]()
|
||||||
|
clientForECHDOH = utils.NewTypedSyncMap[string, *http.Client]()
|
||||||
|
)
|
||||||
|
|
||||||
|
// sockopt can be nil if not specified.
|
||||||
|
// if for clientForECHDOH, domain can be empty.
|
||||||
|
func ECHCacheKey(server, domain string, sockopt *internet.SocketConfig) string {
|
||||||
|
return server + "|" + domain + "|" + fmt.Sprintf("%p", sockopt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates the ECH config for given domain and server.
|
||||||
|
// this method is concurrent safe, only one update request will be sent, others get the cache.
|
||||||
|
// if isLockedUpdate is true, it will not try to acquire the lock.
|
||||||
|
func (c *ECHConfigCache) Update(domain string, server string, isLockedUpdate bool, forceQuery string, sockopt *internet.SocketConfig) ([]byte, error) {
|
||||||
|
if !isLockedUpdate {
|
||||||
|
c.UpdateLock.Lock()
|
||||||
|
defer c.UpdateLock.Unlock()
|
||||||
|
}
|
||||||
|
// Double check cache after acquiring lock
|
||||||
|
configRecord := c.configRecord.Load()
|
||||||
|
if configRecord.expire.After(time.Now()) && configRecord.err == nil {
|
||||||
|
errors.LogDebug(context.Background(), "Cache hit for domain after double check: ", domain)
|
||||||
|
return configRecord.config, configRecord.err
|
||||||
|
}
|
||||||
|
// Query ECH config from DNS server
|
||||||
|
errors.LogDebug(context.Background(), "Trying to query ECH config for domain: ", domain, " with ECH server: ", server)
|
||||||
|
echConfig, ttl, err := dnsQuery(server, domain, sockopt)
|
||||||
|
// if in "full", directly return
|
||||||
|
if err != nil && forceQuery == "full" {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ttl == 0 {
|
||||||
|
ttl = dns2.DefaultTTL
|
||||||
|
}
|
||||||
|
configRecord = &echConfigRecord{
|
||||||
|
config: echConfig,
|
||||||
|
expire: time.Now().Add(time.Duration(ttl) * time.Second),
|
||||||
|
err: err,
|
||||||
|
}
|
||||||
|
c.configRecord.Store(configRecord)
|
||||||
|
return configRecord.config, configRecord.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryRecord returns the ECH config for given domain.
|
||||||
|
// If the record is not in cache or expired, it will query the DNS server and update the cache.
|
||||||
|
func QueryRecord(domain string, server string, forceQuery string, sockopt *internet.SocketConfig) ([]byte, error) {
|
||||||
|
GlobalECHConfigCacheKey := ECHCacheKey(server, domain, sockopt)
|
||||||
|
echConfigCache, ok := GlobalECHConfigCache.Load(GlobalECHConfigCacheKey)
|
||||||
|
if !ok {
|
||||||
|
echConfigCache = &ECHConfigCache{}
|
||||||
|
echConfigCache.configRecord.Store(&echConfigRecord{})
|
||||||
|
echConfigCache, _ = GlobalECHConfigCache.LoadOrStore(GlobalECHConfigCacheKey, echConfigCache)
|
||||||
|
}
|
||||||
|
configRecord := echConfigCache.configRecord.Load()
|
||||||
|
if configRecord.expire.After(time.Now()) && (configRecord.err == nil || forceQuery == "none") {
|
||||||
|
errors.LogDebug(context.Background(), "Cache hit for domain: ", domain)
|
||||||
|
return configRecord.config, configRecord.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If expire is zero value, it means we are in initial state, wait for the query to finish
|
||||||
|
// otherwise return old value immediately and update in a goroutine
|
||||||
|
// but if the cache is too old, wait for update
|
||||||
|
if configRecord.expire == (time.Time{}) || configRecord.expire.Add(time.Hour*6).Before(time.Now()) {
|
||||||
|
return echConfigCache.Update(domain, server, false, forceQuery, sockopt)
|
||||||
|
} else {
|
||||||
|
// If someone already acquired the lock, it means it is updating, do not start another update goroutine
|
||||||
|
if echConfigCache.UpdateLock.TryLock() {
|
||||||
|
go func() {
|
||||||
|
defer echConfigCache.UpdateLock.Unlock()
|
||||||
|
echConfigCache.Update(domain, server, true, forceQuery, sockopt)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
return configRecord.config, configRecord.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dnsQuery is the real func for sending type65 query for given domain to given DNS server.
|
||||||
|
// return ECH config, TTL and error
|
||||||
|
func dnsQuery(server string, domain string, sockopt *internet.SocketConfig) ([]byte, uint32, error) {
|
||||||
|
m := new(dns.Msg)
|
||||||
|
var dnsResolve []byte
|
||||||
|
m.SetQuestion(dns.Fqdn(domain), dns.TypeHTTPS)
|
||||||
|
// for DOH server
|
||||||
|
if strings.HasPrefix(server, "https://") || strings.HasPrefix(server, "h2c://") {
|
||||||
|
h2c := strings.HasPrefix(server, "h2c://")
|
||||||
|
m.SetEdns0(4096, false) // 4096 is the buffer size, false means no DNSSEC
|
||||||
|
padding := &dns.EDNS0_PADDING{Padding: make([]byte, int(crypto.RandBetween(100, 300)))}
|
||||||
|
if opt := m.IsEdns0(); opt != nil {
|
||||||
|
opt.Option = append(opt.Option, padding)
|
||||||
|
}
|
||||||
|
// always 0 in DOH
|
||||||
|
m.Id = 0
|
||||||
|
msg, err := m.Pack()
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
var client *http.Client
|
||||||
|
serverKey := ECHCacheKey(server, "", sockopt)
|
||||||
|
if client, _ = clientForECHDOH.Load(serverKey); client == nil {
|
||||||
|
// All traffic sent by core should via xray's internet.DialSystem
|
||||||
|
// This involves the behavior of some Android VPN GUI clients
|
||||||
|
tr := &http2.Transport{
|
||||||
|
IdleConnTimeout: net.ConnIdleTimeout,
|
||||||
|
ReadIdleTimeout: net.ChromeH2KeepAlivePeriod,
|
||||||
|
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
|
||||||
|
dest, err := net.ParseDestination(network + ":" + addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var conn net.Conn
|
||||||
|
|
||||||
|
conn, err = internet.DialSystem(ctx, dest, sockopt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !h2c {
|
||||||
|
u, err := url.Parse(server)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
conn = utls.UClient(conn, &utls.Config{ServerName: u.Hostname()}, utls.HelloChrome_Auto)
|
||||||
|
if err := conn.(*utls.UConn).HandshakeContext(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
c := &http.Client{
|
||||||
|
Timeout: 5 * time.Second,
|
||||||
|
Transport: tr,
|
||||||
|
}
|
||||||
|
client, _ = clientForECHDOH.LoadOrStore(serverKey, c)
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest("POST", server, bytes.NewReader(msg))
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Accept", "application/dns-message")
|
||||||
|
req.Header.Set("Content-Type", "application/dns-message")
|
||||||
|
req.Header.Set("X-Padding", strings.Repeat("X", int(crypto.RandBetween(100, 1000))))
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, 0, errors.New("query failed with response code:", resp.StatusCode)
|
||||||
|
}
|
||||||
|
dnsResolve = respBody
|
||||||
|
} else if strings.HasPrefix(server, "udp://") { // for classic udp dns server
|
||||||
|
udpServerAddr := server[len("udp://"):]
|
||||||
|
// default port 53 if not specified
|
||||||
|
if !strings.Contains(udpServerAddr, ":") {
|
||||||
|
udpServerAddr = udpServerAddr + ":53"
|
||||||
|
}
|
||||||
|
dest, err := net.ParseDestination("udp" + ":" + udpServerAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, errors.New("failed to parse udp dns server ", udpServerAddr, " for ECH: ", err)
|
||||||
|
}
|
||||||
|
dnsTimeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
// use xray's internet.DialSystem as mentioned above
|
||||||
|
conn, err := internet.DialSystem(dnsTimeoutCtx, dest, sockopt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
err := conn.Close()
|
||||||
|
if err != nil {
|
||||||
|
errors.LogDebug(context.Background(), "Failed to close connection: ", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
msg, err := m.Pack()
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
conn.Write(msg)
|
||||||
|
udpResponse := make([]byte, 512)
|
||||||
|
conn.SetReadDeadline(time.Now().Add(5 * time.Second))
|
||||||
|
_, err = conn.Read(udpResponse)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
dnsResolve = udpResponse
|
||||||
|
}
|
||||||
|
respMsg := new(dns.Msg)
|
||||||
|
err := respMsg.Unpack(dnsResolve)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, errors.New("failed to unpack dns response for ECH: ", err)
|
||||||
|
}
|
||||||
|
if len(respMsg.Answer) > 0 {
|
||||||
|
for _, answer := range respMsg.Answer {
|
||||||
|
if https, ok := answer.(*dns.HTTPS); ok && https.Hdr.Name == dns.Fqdn(domain) {
|
||||||
|
for _, v := range https.Value {
|
||||||
|
if echConfig, ok := v.(*dns.SVCBECHConfig); ok {
|
||||||
|
errors.LogDebug(context.Background(), "Get ECH config:", echConfig.String(), " TTL:", respMsg.Answer[0].Header().Ttl)
|
||||||
|
return echConfig.ECH, answer.Header().Ttl, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// empty is valid, means no ECH config found
|
||||||
|
return nil, dns2.DefaultTTL, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// reference github.com/OmarTariq612/goech
|
||||||
|
func MarshalBinary(ech reality.EchConfig) ([]byte, error) {
|
||||||
|
var b cryptobyte.Builder
|
||||||
|
b.AddUint16(ech.Version)
|
||||||
|
b.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) {
|
||||||
|
child.AddUint8(ech.ConfigID)
|
||||||
|
child.AddUint16(ech.KemID)
|
||||||
|
child.AddUint16(uint16(len(ech.PublicKey)))
|
||||||
|
child.AddBytes(ech.PublicKey)
|
||||||
|
child.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) {
|
||||||
|
for _, cipherSuite := range ech.SymmetricCipherSuite {
|
||||||
|
child.AddUint16(cipherSuite.KDFID)
|
||||||
|
child.AddUint16(cipherSuite.AEADID)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
child.AddUint8(ech.MaxNameLength)
|
||||||
|
child.AddUint8(uint8(len(ech.PublicName)))
|
||||||
|
child.AddBytes(ech.PublicName)
|
||||||
|
child.AddUint16LengthPrefixed(func(child *cryptobyte.Builder) {
|
||||||
|
for _, extention := range ech.Extensions {
|
||||||
|
child.AddUint16(extention.Type)
|
||||||
|
child.AddBytes(extention.Data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return b.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrInvalidLen = errors.New("goech: invalid length")
|
||||||
|
|
||||||
|
func ConvertToGoECHKeys(data []byte) ([]tls.EncryptedClientHelloKey, error) {
|
||||||
|
var keys []tls.EncryptedClientHelloKey
|
||||||
|
s := cryptobyte.String(data)
|
||||||
|
for !s.Empty() {
|
||||||
|
if len(s) < 2 {
|
||||||
|
return keys, ErrInvalidLen
|
||||||
|
}
|
||||||
|
keyLength := int(binary.BigEndian.Uint16(s[:2]))
|
||||||
|
if len(s) < keyLength+4 {
|
||||||
|
return keys, ErrInvalidLen
|
||||||
|
}
|
||||||
|
configLength := int(binary.BigEndian.Uint16(s[keyLength+2 : keyLength+4]))
|
||||||
|
if len(s) < 2+keyLength+2+configLength {
|
||||||
|
return keys, ErrInvalidLen
|
||||||
|
}
|
||||||
|
child := cryptobyte.String(s[:2+keyLength+2+configLength])
|
||||||
|
var (
|
||||||
|
sk, config cryptobyte.String
|
||||||
|
)
|
||||||
|
if !child.ReadUint16LengthPrefixed(&sk) || !child.ReadUint16LengthPrefixed(&config) || !child.Empty() {
|
||||||
|
return keys, ErrInvalidLen
|
||||||
|
}
|
||||||
|
if !s.Skip(2 + keyLength + 2 + configLength) {
|
||||||
|
return keys, ErrInvalidLen
|
||||||
|
}
|
||||||
|
keys = append(keys, tls.EncryptedClientHelloKey{
|
||||||
|
Config: config,
|
||||||
|
PrivateKey: sk,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return keys, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const ExtensionEncryptedClientHello = 0xfe0d
|
||||||
|
const KDF_HKDF_SHA384 = 0x0002
|
||||||
|
const KDF_HKDF_SHA512 = 0x0003
|
||||||
|
|
||||||
|
func GenerateECHKeySet(configID uint8, domain string, kem uint16) (reality.EchConfig, []byte, error) {
|
||||||
|
config := reality.EchConfig{
|
||||||
|
Version: ExtensionEncryptedClientHello,
|
||||||
|
ConfigID: configID,
|
||||||
|
PublicName: []byte(domain),
|
||||||
|
KemID: kem,
|
||||||
|
SymmetricCipherSuite: []reality.EchCipher{
|
||||||
|
{KDFID: hpke.KDF_HKDF_SHA256, AEADID: hpke.AEAD_AES_128_GCM},
|
||||||
|
{KDFID: hpke.KDF_HKDF_SHA256, AEADID: hpke.AEAD_AES_256_GCM},
|
||||||
|
{KDFID: hpke.KDF_HKDF_SHA256, AEADID: hpke.AEAD_ChaCha20Poly1305},
|
||||||
|
{KDFID: KDF_HKDF_SHA384, AEADID: hpke.AEAD_AES_128_GCM},
|
||||||
|
{KDFID: KDF_HKDF_SHA384, AEADID: hpke.AEAD_AES_256_GCM},
|
||||||
|
{KDFID: KDF_HKDF_SHA384, AEADID: hpke.AEAD_ChaCha20Poly1305},
|
||||||
|
{KDFID: KDF_HKDF_SHA512, AEADID: hpke.AEAD_AES_128_GCM},
|
||||||
|
{KDFID: KDF_HKDF_SHA512, AEADID: hpke.AEAD_AES_256_GCM},
|
||||||
|
{KDFID: KDF_HKDF_SHA512, AEADID: hpke.AEAD_ChaCha20Poly1305},
|
||||||
|
},
|
||||||
|
MaxNameLength: 0,
|
||||||
|
Extensions: nil,
|
||||||
|
}
|
||||||
|
// if kem == hpke.DHKEM_X25519_HKDF_SHA256 {
|
||||||
|
curve := ecdh.X25519()
|
||||||
|
priv := make([]byte, 32) //x25519
|
||||||
|
_, err := io.ReadFull(rand.Reader, priv)
|
||||||
|
if err != nil {
|
||||||
|
return config, nil, err
|
||||||
|
}
|
||||||
|
privKey, _ := curve.NewPrivateKey(priv)
|
||||||
|
config.PublicKey = privKey.PublicKey().Bytes()
|
||||||
|
return config, priv, nil
|
||||||
|
// }
|
||||||
|
// TODO: add mlkem768 (former kyber768 draft00). The golang mlkem private key is 64 bytes seed?
|
||||||
|
}
|
79
transport/internet/tls/ech_test.go
Normal file
79
transport/internet/tls/ech_test.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package tls
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/xtls/xray-core/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestECHDial(t *testing.T) {
|
||||||
|
config := &Config{
|
||||||
|
ServerName: "cloudflare.com",
|
||||||
|
EchConfigList: "encryptedsni.com+udp://1.1.1.1",
|
||||||
|
}
|
||||||
|
// test concurrent Dial(to test cache problem)
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
for range 10 {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
TLSConfig := config.GetTLSConfig()
|
||||||
|
TLSConfig.NextProtos = []string{"http/1.1"}
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSClientConfig: TLSConfig,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resp, err := client.Get("https://cloudflare.com/cdn-cgi/trace")
|
||||||
|
common.Must(err)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := io.ReadAll(resp.Body)
|
||||||
|
common.Must(err)
|
||||||
|
if !strings.Contains(string(body), "sni=encrypted") {
|
||||||
|
t.Error("ECH Dial success but SNI is not encrypted")
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
// check cache
|
||||||
|
echConfigCache, ok := GlobalECHConfigCache.Load(ECHCacheKey("udp://1.1.1.1", "encryptedsni.com", nil))
|
||||||
|
if !ok {
|
||||||
|
t.Error("ECH config cache not found")
|
||||||
|
|
||||||
|
}
|
||||||
|
ok = echConfigCache.UpdateLock.TryLock()
|
||||||
|
if !ok {
|
||||||
|
t.Error("ECH config cache dead lock detected")
|
||||||
|
}
|
||||||
|
echConfigCache.UpdateLock.Unlock()
|
||||||
|
configRecord := echConfigCache.configRecord.Load()
|
||||||
|
if configRecord == nil {
|
||||||
|
t.Error("ECH config record not found in cache")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestECHDialFail(t *testing.T) {
|
||||||
|
config := &Config{
|
||||||
|
ServerName: "cloudflare.com",
|
||||||
|
EchConfigList: "udp://127.0.0.1",
|
||||||
|
EchForceQuery: "half",
|
||||||
|
}
|
||||||
|
config.GetTLSConfig()
|
||||||
|
// check cache
|
||||||
|
echConfigCache, ok := GlobalECHConfigCache.Load(ECHCacheKey("udp://127.0.0.1", "cloudflare.com", nil))
|
||||||
|
if !ok {
|
||||||
|
t.Error("ECH config cache not found")
|
||||||
|
}
|
||||||
|
configRecord := echConfigCache.configRecord.Load()
|
||||||
|
if configRecord == nil {
|
||||||
|
t.Error("ECH config record not found in cache")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if configRecord.err == nil {
|
||||||
|
t.Error("unexpected nil error in ECH config record")
|
||||||
|
}
|
||||||
|
}
|
@@ -134,6 +134,7 @@ func copyConfig(c *tls.Config) *utls.Config {
|
|||||||
InsecureSkipVerify: c.InsecureSkipVerify,
|
InsecureSkipVerify: c.InsecureSkipVerify,
|
||||||
VerifyPeerCertificate: c.VerifyPeerCertificate,
|
VerifyPeerCertificate: c.VerifyPeerCertificate,
|
||||||
KeyLogWriter: c.KeyLogWriter,
|
KeyLogWriter: c.KeyLogWriter,
|
||||||
|
EncryptedClientHelloConfigList: c.EncryptedClientHelloConfigList,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -44,6 +44,10 @@ func NewDispatcher(dispatcher routing.Dispatcher, callback ResponseCallback) *Di
|
|||||||
func (v *Dispatcher) RemoveRay() {
|
func (v *Dispatcher) RemoveRay() {
|
||||||
v.Lock()
|
v.Lock()
|
||||||
defer v.Unlock()
|
defer v.Unlock()
|
||||||
|
v.removeRay()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Dispatcher) removeRay() {
|
||||||
if v.conn != nil {
|
if v.conn != nil {
|
||||||
common.Interrupt(v.conn.link.Reader)
|
common.Interrupt(v.conn.link.Reader)
|
||||||
common.Close(v.conn.link.Writer)
|
common.Close(v.conn.link.Writer)
|
||||||
@@ -62,9 +66,15 @@ func (v *Dispatcher) getInboundRay(ctx context.Context, dest net.Destination) (*
|
|||||||
errors.LogInfo(ctx, "establishing new connection for ", dest)
|
errors.LogInfo(ctx, "establishing new connection for ", dest)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
entry := &connEntry{}
|
||||||
removeRay := func() {
|
removeRay := func() {
|
||||||
|
v.Lock()
|
||||||
|
defer v.Unlock()
|
||||||
|
// sometimes the entry is already removed by others, don't close again
|
||||||
|
if entry == v.conn {
|
||||||
cancel()
|
cancel()
|
||||||
v.RemoveRay()
|
v.removeRay()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
timer := signal.CancelAfterInactivity(ctx, removeRay, time.Minute)
|
timer := signal.CancelAfterInactivity(ctx, removeRay, time.Minute)
|
||||||
|
|
||||||
@@ -73,7 +83,7 @@ func (v *Dispatcher) getInboundRay(ctx context.Context, dest net.Destination) (*
|
|||||||
return nil, errors.New("failed to dispatch request to ", dest).Base(err)
|
return nil, errors.New("failed to dispatch request to ", dest).Base(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
entry := &connEntry{
|
*entry = connEntry{
|
||||||
link: link,
|
link: link,
|
||||||
timer: timer,
|
timer: timer,
|
||||||
cancel: removeRay,
|
cancel: removeRay,
|
||||||
|
@@ -40,6 +40,14 @@ func ListenUDP(ctx context.Context, address net.Address, port net.Port, streamSe
|
|||||||
opt(hub)
|
opt(hub)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if address.Family().IsDomain() && address.Domain() == "localhost" {
|
||||||
|
address = net.LocalHostIP
|
||||||
|
}
|
||||||
|
|
||||||
|
if address.Family().IsDomain() {
|
||||||
|
return nil, errors.New("domain address is not allowed for listening: ", address.Domain())
|
||||||
|
}
|
||||||
|
|
||||||
var sockopt *internet.SocketConfig
|
var sockopt *internet.SocketConfig
|
||||||
if streamSettings != nil {
|
if streamSettings != nil {
|
||||||
sockopt = streamSettings.SocketSettings
|
sockopt = streamSettings.SocketSettings
|
||||||
|
Reference in New Issue
Block a user