diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index dc7759cc..9ec5b9d9 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,36 +1,74 @@ -name: Build docker image +name: Build and Push Docker Image on: 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: - build-image: + build-and-push: + if: (github.event.action != 'published') || (github.event.action == 'published' && github.event.release.prerelease == true) runs-on: ubuntu-latest permissions: + contents: read packages: write + 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 - id: meta - uses: docker/metadata-action@v5 - with: - images: ghcr.io/${{ github.repository_owner }}/xray-core - flavor: latest=auto - tags: | - type=semver,pattern={{version}} + - name: Validate and extract tag + run: | + SOURCE_TAG="${{ github.event.inputs.tag }}" + if [[ -z "$SOURCE_TAG" ]]; then + SOURCE_TAG="${{ github.ref_name }}" + fi - - name: Docker metadata (unsupported architectures) - id: metausa - uses: docker/metadata-action@v5 - with: - images: ghcr.io/${{ github.repository_owner }}/xray-core - flavor: | - latest=auto - suffix=-usa,onlatest=true - tags: | - type=semver,pattern={{version}} + if [[ -z "$SOURCE_TAG" ]]; then + echo "Error: Could not determine a valid tag source. Input tag and context tag (github.ref_name) are both empty." + exit 1 + fi + + if [[ "$SOURCE_TAG" =~ ^v[0-9]+\.[0-9] ]]; then + IMAGE_TAG="${SOURCE_TAG#v}" + else + IMAGE_TAG="$SOURCE_TAG" + 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 uses: docker/login-action@v3 @@ -39,13 +77,12 @@ jobs: username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build and push + - name: Build Docker image (main architectures) + id: build_main_arches uses: docker/build-push-action@v6 with: context: . + file: .github/docker/Dockerfile platforms: | linux/amd64 linux/arm/v7 @@ -53,39 +90,41 @@ jobs: linux/ppc64le linux/s390x provenance: false - file: .github/docker/Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} + outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true - - name: Build and push (unsupported architectures) + - name: Build Docker image (additional architectures) + id: build_additional_arches uses: docker/build-push-action@v6 with: context: . + file: .github/docker/Dockerfile.usa platforms: | linux/386 linux/arm/v6 linux/riscv64 linux/loong64 provenance: false - file: .github/docker/Dockerfile.usa - push: true - tags: ${{ steps.metausa.outputs.tags }} + outputs: type=image,name=${{ env.FULL_IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=true - - name: Merge Multi-Arch Manifests + - name: Create manifest list and push 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 - TAGS=($(echo "${{ steps.meta.outputs.tags }}" | tr '\n' ' ')) + if [[ "${{ env.LATEST }}" == "true" ]]; then + echo "Adding 'latest' tag to manifest: '${{ env.FULL_IMAGE_NAME }}:latest'." + docker buildx imagetools create \ + --tag ${{ env.FULL_IMAGE_NAME }}:latest \ + ${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }} + fi - echo "Total tags to process: ${#TAGS[@]}" - for tag in "${TAGS[@]}"; do - 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 - done + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:${{ env.IMAGE_TAG }} - echo "Multi-architecture manifest merge completed successfully." + if [[ "${{ env.LATEST }}" == "true" ]]; then + docker buildx imagetools inspect ${{ env.FULL_IMAGE_NAME }}:latest + fi diff --git a/README.md b/README.md index 4ec57635..9e7b375c 100644 --- a/README.md +++ b/README.md @@ -81,11 +81,13 @@ - [v2rayN](https://github.com/2dust/v2rayN) - [Furious](https://github.com/LorenEteval/Furious) - [Invisible Man - Xray](https://github.com/InvisibleManVPN/InvisibleMan-XRayClient) + - [AnyPortal](https://github.com/AnyPortal/AnyPortal) - Android - [v2rayNG](https://github.com/2dust/v2rayNG) - [X-flutter](https://github.com/XTLS/X-flutter) - [SaeedDev94/Xray](https://github.com/SaeedDev94/Xray) - [SimpleXray](https://github.com/lhear/SimpleXray) + - [AnyPortal](https://github.com/AnyPortal/AnyPortal) - iOS & macOS arm64 - [Happ](https://apps.apple.com/app/happ-proxy-utility/id6504287215) - [Streisand](https://apps.apple.com/app/streisand/id6450534064) @@ -97,11 +99,13 @@ - [Furious](https://github.com/LorenEteval/Furious) - [OneXray](https://github.com/OneXray/OneXray) - [GoXRay](https://github.com/goxray/desktop) + - [AnyPortal](https://github.com/AnyPortal/AnyPortal) - Linux - [v2rayA](https://github.com/v2rayA/v2rayA) - [Furious](https://github.com/LorenEteval/Furious) - [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... diff --git a/app/dns/nameserver_quic.go b/app/dns/nameserver_quic.go index 5512edc4..31af657e 100644 --- a/app/dns/nameserver_quic.go +++ b/app/dns/nameserver_quic.go @@ -32,7 +32,7 @@ type QUICNameServer struct { sync.RWMutex cacheController *CacheController destination *net.Destination - connection quic.Connection + connection *quic.Conn 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 { case <-s.Context().Done(): return false @@ -229,8 +229,8 @@ func isActive(s quic.Connection) bool { } } -func (s *QUICNameServer) getConnection() (quic.Connection, error) { - var conn quic.Connection +func (s *QUICNameServer) getConnection() (*quic.Conn, error) { + var conn *quic.Conn s.RLock() conn = s.connection if conn != nil && isActive(conn) { @@ -263,7 +263,7 @@ func (s *QUICNameServer) getConnection() (quic.Connection, error) { return conn, nil } -func (s *QUICNameServer) openConnection() (quic.Connection, error) { +func (s *QUICNameServer) openConnection() (*quic.Conn, error) { tlsConfig := tls.Config{} quicConfig := &quic.Config{ HandshakeIdleTimeout: handshakeTimeout, @@ -283,7 +283,7 @@ func (s *QUICNameServer) openConnection() (quic.Connection, error) { 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() if err != nil { return nil, err diff --git a/app/observatory/burst/config.pb.go b/app/observatory/burst/config.pb.go index ffbd0689..570384ae 100644 --- a/app/observatory/burst/config.pb.go +++ b/app/observatory/burst/config.pb.go @@ -90,6 +90,8 @@ type HealthPingConfig struct { SamplingCount int32 `protobuf:"varint,4,opt,name=samplingCount,proto3" json:"samplingCount,omitempty"` // ping timeout, int64 values of time.Duration 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() { @@ -157,6 +159,13 @@ func (x *HealthPingConfig) GetTimeout() int64 { 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_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, 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, - 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, 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, @@ -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, 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, - 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, 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, diff --git a/app/observatory/burst/config.proto b/app/observatory/burst/config.proto index ead75478..a60978d5 100644 --- a/app/observatory/burst/config.proto +++ b/app/observatory/burst/config.proto @@ -26,4 +26,7 @@ message HealthPingConfig { int32 samplingCount = 4; // ping timeout, int64 values of time.Duration int64 timeout = 5; + // http method to make request + string httpMethod = 6; + } diff --git a/app/observatory/burst/healthping.go b/app/observatory/burst/healthping.go index 0d8cab9f..cb1c3402 100644 --- a/app/observatory/burst/healthping.go +++ b/app/observatory/burst/healthping.go @@ -19,6 +19,7 @@ type HealthPingSettings struct { Interval time.Duration `json:"interval"` SamplingCount int `json:"sampling"` Timeout time.Duration `json:"timeout"` + HttpMethod string `json:"httpMethod"` } // 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 { settings := &HealthPingSettings{} if config != nil { + + var httpMethod string + if config.HttpMethod == "" { + httpMethod = "HEAD" + } else { + httpMethod = strings.TrimSpace(config.HttpMethod) + } + settings = &HealthPingSettings{ Connectivity: strings.TrimSpace(config.Connectivity), Destination: strings.TrimSpace(config.Destination), Interval: time.Duration(config.Interval), SamplingCount: int(config.SamplingCount), Timeout: time.Duration(config.Timeout), + HttpMethod: httpMethod, } } if settings.Destination == "" { @@ -164,7 +174,7 @@ func (h *HealthPing) doCheck(tags []string, duration time.Duration, rounds int) } time.AfterFunc(delay, func() { errors.LogDebug(h.ctx, "checking ", handler) - delay, err := client.MeasureDelay() + delay, err := client.MeasureDelay(h.Settings.HttpMethod) if err == nil { ch <- &rtt{ handler: handler, @@ -251,7 +261,7 @@ func (h *HealthPing) checkConnectivity() bool { h.Settings.Connectivity, h.Settings.Timeout, ) - if _, err := tester.MeasureDelay(); err != nil { + if _, err := tester.MeasureDelay(h.Settings.HttpMethod); err != nil { return false } return true diff --git a/app/observatory/burst/ping.go b/app/observatory/burst/ping.go index 5ea1433a..fcb40f1a 100644 --- a/app/observatory/burst/ping.go +++ b/app/observatory/burst/ping.go @@ -2,6 +2,7 @@ package burst import ( "context" + "io" "net/http" "time" @@ -51,20 +52,28 @@ func newHTTPClient(ctxv context.Context, dispatcher routing.Dispatcher, handler } // 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 { panic("pingClient not initialized") } - req, err := http.NewRequest(http.MethodHead, s.destination, nil) + + req, err := http.NewRequest(httpMethod, s.destination, nil) if err != nil { return rttFailed, err } + start := time.Now() resp, err := s.httpClient.Do(req) if err != nil { 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() + return time.Since(start), nil } diff --git a/app/proxyman/command/command.go b/app/proxyman/command/command.go index 227a95c3..a86f0921 100644 --- a/app/proxyman/command/command.go +++ b/app/proxyman/command/command.go @@ -103,13 +103,22 @@ func (s *handlerServer) AlterInbound(ctx context.Context, request *AlterInboundR func (s *handlerServer) ListInbounds(ctx context.Context, request *ListInboundsRequest) (*ListInboundsResponse, error) { handlers := s.ihm.ListHandlers(ctx) response := &ListInboundsResponse{} - for _, handler := range handlers { - response.Inbounds = append(response.Inbounds, &core.InboundHandlerConfig{ - Tag: handler.Tag(), - ReceiverSettings: handler.ReceiverSettings(), - ProxySettings: handler.ProxySettings(), - }) + if request.GetIsOnlyTags() { + for _, handler := range handlers { + response.Inbounds = append(response.Inbounds, &core.InboundHandlerConfig{ + Tag: handler.Tag(), + }) + } + } else { + for _, handler := range handlers { + response.Inbounds = append(response.Inbounds, &core.InboundHandlerConfig{ + Tag: handler.Tag(), + ReceiverSettings: handler.ReceiverSettings(), + ProxySettings: handler.ProxySettings(), + }) + } } + return response, nil } diff --git a/app/proxyman/command/command.pb.go b/app/proxyman/command/command.pb.go index f4219d91..6c127960 100644 --- a/app/proxyman/command/command.pb.go +++ b/app/proxyman/command/command.pb.go @@ -368,6 +368,8 @@ type ListInboundsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + IsOnlyTags bool `protobuf:"varint,1,opt,name=isOnlyTags,proto3" json:"isOnlyTags,omitempty"` } func (x *ListInboundsRequest) Reset() { @@ -400,6 +402,13 @@ func (*ListInboundsRequest) Descriptor() ([]byte, []int) { 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 { state protoimpl.MessageState 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, 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, - 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, - 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, 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, diff --git a/app/proxyman/command/command.proto b/app/proxyman/command/command.proto index 4ff499eb..71f8f0dc 100644 --- a/app/proxyman/command/command.proto +++ b/app/proxyman/command/command.proto @@ -37,7 +37,9 @@ message AlterInboundRequest { message AlterInboundResponse {} -message ListInboundsRequest {} +message ListInboundsRequest { + bool isOnlyTags = 1; +} message ListInboundsResponse { repeated core.InboundHandlerConfig inbounds = 1; diff --git a/app/stats/command/command.go b/app/stats/command/command.go index 64e31d38..db5283c8 100644 --- a/app/stats/command/command.go +++ b/app/stats/command/command.go @@ -12,6 +12,8 @@ import ( "github.com/xtls/xray-core/core" feature_stats "github.com/xtls/xray-core/features/stats" grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" ) // 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) { c := s.stats.GetCounter(request.Name) if c == nil { - return nil, errors.New(request.Name, " not found.") + return nil, status.Error(codes.NotFound, request.Name+" not found.") } var value int64 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) { c := s.stats.GetOnlineMap(request.Name) 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()) return &GetStatsResponse{ @@ -64,7 +66,7 @@ func (s *statsServer) GetStatsOnlineIpList(ctx context.Context, request *GetStat c := s.stats.GetOnlineMap(request.Name) 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) diff --git a/common/signal/timer.go b/common/signal/timer.go index a7f59cd9..ece9f496 100644 --- a/common/signal/timer.go +++ b/common/signal/timer.go @@ -67,9 +67,9 @@ func (t *ActivityTimer) SetTimeout(timeout time.Duration) { t.checkTask.Close() } t.checkTask = checkTask - t.Unlock() t.Update() common.Must(checkTask.Start()) + t.Unlock() } func CancelAfterInactivity(ctx context.Context, cancel context.CancelFunc, timeout time.Duration) *ActivityTimer { diff --git a/go.mod b/go.mod index 2b589d1c..d6821e83 100644 --- a/go.mod +++ b/go.mod @@ -9,10 +9,10 @@ require ( github.com/golang/mock v1.7.0-rc.1 github.com/google/go-cmp v0.7.0 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/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/sagernet/sing v0.5.1 github.com/sagernet/sing-shadowsocks v0.2.7 @@ -20,12 +20,12 @@ require ( github.com/stretchr/testify v1.10.0 github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e github.com/vishvananda/netlink v1.3.1 - github.com/xtls/reality v0.0.0-20250608132114-50752aec6bfb + github.com/xtls/reality v0.0.0-20250715055725-05a351a64521 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.39.0 - golang.org/x/net v0.41.0 - golang.org/x/sync v0.15.0 - golang.org/x/sys v0.33.0 + golang.org/x/crypto v0.40.0 + golang.org/x/net v0.42.0 + golang.org/x/sync v0.16.0 + golang.org/x/sys v0.34.0 golang.zx2c4.com/wireguard v0.0.0-20231211153847-12269c276173 google.golang.org/grpc v1.73.0 google.golang.org/protobuf v1.36.6 @@ -38,22 +38,19 @@ require ( github.com/andybalholm/brotli v1.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 // indirect - github.com/go-task/slim-sprig/v3 v3.0.0 // 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/klauspost/compress v1.17.8 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect - github.com/onsi/ginkgo/v2 v2.19.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.5.1 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect github.com/vishvananda/netns v0.0.5 // indirect go.uber.org/mock v0.5.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/tools v0.33.0 // indirect + golang.org/x/tools v0.34.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index c0fb7cb8..08252eb5 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,6 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 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-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/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -26,8 +24,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/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 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/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= @@ -40,12 +36,8 @@ github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0N github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= -github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= -github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0= +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/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc= @@ -56,8 +48,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb 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/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA= -github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ= +github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= +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.7.3/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= @@ -78,8 +70,8 @@ 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/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= 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-20250608132114-50752aec6bfb/go.mod h1:yD47RN65bDLZgyHWMfFDiqlzrq4usDMt/Xzsk6tMbhw= +github.com/xtls/reality v0.0.0-20250715055725-05a351a64521 h1:hQQSzX6Y40nY1XT1TKAEpKwUHUUy3UvYKQIclLjYx9U= +github.com/xtls/reality v0.0.0-20250715055725-05a351a64521/go.mod h1:yD47RN65bDLZgyHWMfFDiqlzrq4usDMt/Xzsk6tMbhw= 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/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= @@ -99,20 +91,20 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs 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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +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.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= 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-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.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= -golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -121,21 +113,21 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc 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.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +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/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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= -golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +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/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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 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.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/infra/conf/dns_proxy.go b/infra/conf/dns_proxy.go index 3425f22e..e731ee7c 100644 --- a/infra/conf/dns_proxy.go +++ b/infra/conf/dns_proxy.go @@ -30,7 +30,7 @@ func (c *DNSOutboundConfig) Build() (proto.Message, error) { switch c.NonIPQuery { case "": c.NonIPQuery = "drop" - case "drop", "skip": + case "drop", "skip", "reject": default: return nil, errors.New(`unknown "nonIPQuery": `, c.NonIPQuery) } diff --git a/infra/conf/router_strategy.go b/infra/conf/router_strategy.go index 98bcc8d1..464cbcfb 100644 --- a/infra/conf/router_strategy.go +++ b/infra/conf/router_strategy.go @@ -2,6 +2,7 @@ package conf import ( "google.golang.org/protobuf/proto" + "strings" "github.com/xtls/xray-core/app/observatory/burst" "github.com/xtls/xray-core/app/router" @@ -51,15 +52,23 @@ type healthCheckSettings struct { Interval duration.Duration `json:"interval"` SamplingCount int `json:"sampling"` Timeout duration.Duration `json:"timeout"` + HttpMethod string `json:"httpMethod"` } func (h healthCheckSettings) Build() (proto.Message, error) { + var httpMethod string + if h.HttpMethod == "" { + httpMethod = "HEAD" + } else { + httpMethod = strings.TrimSpace(h.HttpMethod) + } return &burst.HealthPingConfig{ Destination: h.Destination, Connectivity: h.Connectivity, Interval: int64(h.Interval), Timeout: int64(h.Timeout), SamplingCount: int32(h.SamplingCount), + HttpMethod: httpMethod, }, nil } diff --git a/infra/conf/transport_internet.go b/infra/conf/transport_internet.go index d762be32..f6c46efb 100644 --- a/infra/conf/transport_internet.go +++ b/infra/conf/transport_internet.go @@ -544,7 +544,7 @@ func (c *REALITYConfig) Build() (proto.Message, error) { } default: if _, err = strconv.Atoi(s); err == nil { - s = "127.0.0.1:" + s + s = "localhost:" + s } if _, _, err = net.SplitHostPort(s); err == nil { c.Type = "tcp" diff --git a/infra/conf/trojan.go b/infra/conf/trojan.go index 62c8f55b..4a230514 100644 --- a/infra/conf/trojan.go +++ b/infra/conf/trojan.go @@ -155,7 +155,7 @@ func (c *TrojanServerConfig) Build() (proto.Message, error) { } } else { 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 { fb.Type = "tcp" diff --git a/infra/conf/vless.go b/infra/conf/vless.go index 27da68dc..5d4ace6f 100644 --- a/infra/conf/vless.go +++ b/infra/conf/vless.go @@ -111,7 +111,7 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) { } } else { 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 { fb.Type = "tcp" diff --git a/infra/conf/vless_test.go b/infra/conf/vless_test.go index 0f702437..8f1baaa5 100644 --- a/infra/conf/vless_test.go +++ b/infra/conf/vless_test.go @@ -110,7 +110,7 @@ func TestVLessInbound(t *testing.T) { Alpn: "", Path: "", Type: "tcp", - Dest: "127.0.0.1:80", + Dest: "localhost:80", Xver: 0, }, { diff --git a/main/commands/all/api/inbounds_list.go b/main/commands/all/api/inbounds_list.go index e4267a6b..6060074c 100644 --- a/main/commands/all/api/inbounds_list.go +++ b/main/commands/all/api/inbounds_list.go @@ -7,7 +7,7 @@ import ( var cmdListInbounds = &base.Command{ 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", Long: ` List inbounds in Xray. @@ -29,14 +29,17 @@ Example: func executeListInbounds(cmd *base.Command, args []string) { setSharedFlags(cmd) + var isOnlyTagsStr string + cmd.Flag.StringVar(&isOnlyTagsStr, "isOnlyTags", "", "") cmd.Flag.Parse(args) + isOnlyTags := isOnlyTagsStr == "true" conn, ctx, close := dialAPIServer() defer close() client := handlerService.NewHandlerServiceClient(conn) - resp, err := client.ListInbounds(ctx, &handlerService.ListInboundsRequest{}) + resp, err := client.ListInbounds(ctx, &handlerService.ListInboundsRequest{IsOnlyTags: isOnlyTags}) if err != nil { base.Fatalf("failed to list inbounds: %s", err) } diff --git a/main/commands/all/tls/ping.go b/main/commands/all/tls/ping.go index 7d0ea0d6..99c03b97 100644 --- a/main/commands/all/tls/ping.go +++ b/main/commands/all/tls/ping.go @@ -6,6 +6,9 @@ import ( "encoding/base64" "fmt" "net" + "reflect" + "strconv" + "unsafe" "github.com/xtls/xray-core/main/commands/base" . "github.com/xtls/xray-core/transport/internet/tls" @@ -36,8 +39,13 @@ func executePing(cmd *base.Command, args []string) { base.Fatalf("domain not specified") } - domain := cmdPing.Flag.Arg(0) - fmt.Println("Tls ping: ", domain) + domainWithPort := cmdPing.Flag.Arg(0) + fmt.Println("Tls ping: ", domainWithPort) + TargetPort := 443 + domain, port, err := net.SplitHostPort(domainWithPort) + if err == nil { + TargetPort, _ = strconv.Atoi(port) + } var ip net.IP if len(*pingIPStr) > 0 { @@ -58,14 +66,14 @@ func executePing(cmd *base.Command, args []string) { fmt.Println("-------------------") 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 { base.Fatalf("Failed to dial tcp: %s", err) } tlsConn := gotls.Client(tcpConn, &gotls.Config{ InsecureSkipVerify: true, NextProtos: []string{"http/1.1"}, - MaxVersion: gotls.VersionTLS12, + MaxVersion: gotls.VersionTLS13, MinVersion: gotls.VersionTLS12, // Do not release tool before v5's refactor // VerifyPeerCertificate: showCert(), @@ -75,6 +83,7 @@ func executePing(cmd *base.Command, args []string) { fmt.Println("Handshake failure: ", err) } else { fmt.Println("Handshake succeeded") + printTLSConnDetail(tlsConn) printCertificates(tlsConn.ConnectionState().PeerCertificates) } tlsConn.Close() @@ -90,7 +99,7 @@ func executePing(cmd *base.Command, args []string) { tlsConn := gotls.Client(tcpConn, &gotls.Config{ ServerName: domain, NextProtos: []string{"http/1.1"}, - MaxVersion: gotls.VersionTLS12, + MaxVersion: gotls.VersionTLS13, MinVersion: gotls.VersionTLS12, // Do not release tool before v5's refactor // VerifyPeerCertificate: showCert(), @@ -100,6 +109,7 @@ func executePing(cmd *base.Command, args []string) { fmt.Println("handshake failure: ", err) } else { fmt.Println("handshake succeeded") + printTLSConnDetail(tlsConn) printCertificates(tlsConn.ConnectionState().PeerCertificates) } tlsConn.Close() @@ -117,6 +127,23 @@ func printCertificates(certs []*x509.Certificate) { } } +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("Post-Quantum key exchange:", PostQuantum, "("+curveID.String()+")") + } else { + fmt.Println("Post-Quantum key exchange: false (RSA Exchange)") + } +} + func showCert() func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { hash := GenerateCertChainHash(rawCerts) diff --git a/proxy/dns/dns.go b/proxy/dns/dns.go index 3308faef..0f83f5ac 100644 --- a/proxy/dns/dns.go +++ b/proxy/dns/dns.go @@ -187,6 +187,9 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet. if len(h.blockTypes) > 0 { for _, blocktype := range h.blockTypes { 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) return nil } @@ -199,6 +202,11 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, d internet. b.Release() continue } + if h.nonIPQuery == "reject" { + go h.rejectNonIPQuery(id, qType, domain, writer) + b.Release() + continue + } } 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 { access sync.Mutex dialer func() (stat.Connection, error) diff --git a/proxy/trojan/validator.go b/proxy/trojan/validator.go index bfe53812..7841a249 100644 --- a/proxy/trojan/validator.go +++ b/proxy/trojan/validator.go @@ -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. func (v *Validator) GetByEmail(email string) *protocol.MemoryUser { + email = strings.ToLower(email) u, _ := v.email.Load(email) if u != nil { return u.(*protocol.MemoryUser) diff --git a/proxy/vless/validator.go b/proxy/vless/validator.go index ddac6cbb..d1356c5f 100644 --- a/proxy/vless/validator.go +++ b/proxy/vless/validator.go @@ -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. func (v *MemoryValidator) GetByEmail(email string) *protocol.MemoryUser { + email = strings.ToLower(email) u, _ := v.email.Load(email) if u != nil { return u.(*protocol.MemoryUser) diff --git a/transport/internet/splithttp/dialer.go b/transport/internet/splithttp/dialer.go index c5fba78b..dee5f486 100644 --- a/transport/internet/splithttp/dialer.go +++ b/transport/internet/splithttp/dialer.go @@ -161,7 +161,7 @@ func createHTTPClient(dest net.Destination, streamSettings *internet.MemoryStrea transport = &http3.Transport{ QUICConfig: quicConfig, 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) if err != nil { return nil, err diff --git a/transport/internet/tls/config.go b/transport/internet/tls/config.go index d6701a7d..517bf40b 100644 --- a/transport/internet/tls/config.go +++ b/transport/internet/tls/config.go @@ -486,11 +486,11 @@ func ConfigFromStreamSettings(settings *internet.MemoryStreamConfig) *Config { func ParseCurveName(curveNames []string) []tls.CurveID { curveMap := map[string]tls.CurveID{ - "curvep256": tls.CurveP256, - "curvep384": tls.CurveP384, - "curvep521": tls.CurveP521, - "x25519": tls.X25519, - "x25519kyber768draft00": 0x6399, + "curvep256": tls.CurveP256, + "curvep384": tls.CurveP384, + "curvep521": tls.CurveP521, + "x25519": tls.X25519, + "x25519mlkem768": tls.X25519MLKEM768, } var curveIDs []tls.CurveID