diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml new file mode 100644 index 00000000..73bacb26 --- /dev/null +++ b/.github/workflows/docker.yaml @@ -0,0 +1,43 @@ +name: Release Docker Image + +on: + push: + tags: + - v* + +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Set up Checkout + uses: actions/checkout@v2 + + - name: Install Buildx and QEMU + run: | + export DOCKER_BUILDKIT=1 + docker build --platform=local -o . git://github.com/docker/buildx + mkdir -p ~/.docker/cli-plugins + mv buildx ~/.docker/cli-plugins/docker-buildx + docker run --rm --privileged multiarch/qemu-user-static:latest --reset -p yes --credential yes + docker buildx create --use --name build --node build --driver-opt network=host + + - name: Log in to Docker Hub + env: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} + run: | + echo "${DOCKER_PASSWORD}" | docker login --username ${DOCKER_USERNAME} --password-stdin + + - name: Build and push Docker image + env: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + DOCKER_IMAGE_PLATFORM: linux/386,linux/amd64,linux/arm/v7,linux/arm64 + run: | + DOCKER_IMAGE_NAME=$(echo $DOCKER_USERNAME | tr '[:upper:]' '[:lower:]')/v2fly-core + DOCKER_IMAGE_VERSION=${GITHUB_REF#refs/*/} + docker buildx build \ + --platform "$DOCKER_IMAGE_PLATFORM" \ + --output "type=image,push=true" \ + --tag "$DOCKER_IMAGE_NAME":"$DOCKER_IMAGE_VERSION" \ + --tag "$DOCKER_IMAGE_NAME":latest \ + --file ./Dockerfile . diff --git a/Dockerfile b/Dockerfile index bff3dfe8..2431d3b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # STEP 1 build executable binary ############################ FROM golang:alpine AS builder -RUN apk update && apk add --no-cache git bash wget +RUN apk update && apk add --no-cache git bash wget curl WORKDIR /go/src/v2ray.com/core RUN git clone --progress https://github.com/v2fly/v2ray-core.git . && \ bash ./release/user-package.sh nosource noconf codename=$(git describe --tags) buildname=docker-fly abpathtgz=/tmp/v2ray.tgz diff --git a/app/dns/dohdns.go b/app/dns/dohdns.go index 02b0906c..3f4f7dcd 100644 --- a/app/dns/dohdns.go +++ b/app/dns/dohdns.go @@ -58,6 +58,7 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, clientIP net. MaxIdleConns: 30, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 30 * time.Second, + ForceAttemptHTTP2: true, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { dest, err := net.ParseDestination(network + ":" + addr) if err != nil { @@ -89,7 +90,8 @@ func NewDoHLocalNameServer(url *url.URL, clientIP net.IP) *DoHNameServer { url.Scheme = "https" s := baseDOHNameServer(url, "DOHL", clientIP) tr := &http.Transport{ - IdleConnTimeout: 90 * time.Second, + IdleConnTimeout: 90 * time.Second, + ForceAttemptHTTP2: true, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { dest, err := net.ParseDestination(network + ":" + addr) if err != nil { diff --git a/app/proxyman/inbound/inbound.go b/app/proxyman/inbound/inbound.go index 52835694..fa8ffb78 100644 --- a/app/proxyman/inbound/inbound.go +++ b/app/proxyman/inbound/inbound.go @@ -150,6 +150,13 @@ func NewHandler(ctx context.Context, config *core.InboundHandlerConfig) (inbound return nil, newError("not a ReceiverConfig").AtError() } + streamSettings := receiverSettings.StreamSettings + if streamSettings != nil && streamSettings.SocketSettings != nil { + ctx = session.ContextWithSockopt(ctx, &session.Sockopt{ + Mark: streamSettings.SocketSettings.Mark, + }) + } + allocStrategy := receiverSettings.AllocationStrategy if allocStrategy == nil || allocStrategy.Type == proxyman.AllocationStrategy_Always { return NewAlwaysOnInboundHandler(ctx, tag, receiverSettings, proxySettings) diff --git a/azure-pipelines.template.yml b/azure-pipelines.template.yml deleted file mode 100644 index a982efbe..00000000 --- a/azure-pipelines.template.yml +++ /dev/null @@ -1,22 +0,0 @@ -jobs: -- job: ${{ parameters.name }} - timeoutInMinutes: 30 - - pool: - vmImage: ${{ parameters.vmImage }} - - steps: - - checkout: self - - task: GoTool@0 - inputs: - version: '1.14' - - script: | - go test -p 1 -v -timeout 30m ./... - workingDirectory: '$(Build.SourcesDirectory)' - displayName: 'Test' - - - script: | - go build ./common/buf - condition: always() - workingDirectory: '$(Build.SourcesDirectory)' - displayName: 'Test Buf error' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d1ccf6ad..7408e56c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -2,71 +2,25 @@ trigger: batch: true branches: include: - - master - - dev* - - refs/tags/* + - master + - dev* + - refs/tags/* -pr: - - master - - dev* +pool: + vmImage: 'ubuntu-latest' -jobs: -- template: azure-pipelines.template.yml - parameters: - name: linux - vmImage: 'ubuntu-latest' +variables: + - group: GithubToken + - name: GOPATH + value: '$(system.defaultWorkingDirectory)/gopath' + - name: BAZEL_VER + value: '0.23.0' -- template: azure-pipelines.template.yml - parameters: - name: windows - vmImage: 'windows-latest' - -- template: azure-pipelines.template.yml - parameters: - name: macos - vmImage: 'macOS-latest' - -- job: linux_coverage - dependsOn: linux - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - timeoutInMinutes: 30 - - pool: - vmImage: 'ubuntu-latest' - -#variables: -# CODECOV_TOKEN: '$(coverage.token)' - - - steps: - - checkout: self - - task: GoTool@0 - inputs: - version: '1.14.4' - - script: | - bash ./testing/coverage/coverall - workingDirectory: '$(Build.SourcesDirectory)' - displayName: 'Coverage' - -- job: make_release - dependsOn: linux - condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) - timeoutInMinutes: 60 - - pool: - vmImage: 'ubuntu-latest' - variables: - - group: GithubToken - - name: GOPATH - value: '$(system.defaultWorkingDirectory)/gopath' - - name: BAZEL_VER - value: '0.23.0' - - steps: +steps: - checkout: self - task: GoTool@0 inputs: - version: '1.14.4' + version: '1.14.6' - script: | mkdir triggersrc ls -I "triggersrc" | xargs cp -rf -t triggersrc diff --git a/common/buf/buffer_test.go b/common/buf/buffer_test.go index 6231e449..34961c8f 100644 --- a/common/buf/buffer_test.go +++ b/common/buf/buffer_test.go @@ -22,7 +22,7 @@ func TestBufferClear(t *testing.T) { buffer.Clear() if buffer.Len() != 0 { - t.Error("expect 0 lenght, but got ", buffer.Len()) + t.Error("expect 0 length, but got ", buffer.Len()) } } diff --git a/common/crypto/auth.go b/common/crypto/auth.go index d1f1187d..2e64c811 100644 --- a/common/crypto/auth.go +++ b/common/crypto/auth.go @@ -248,13 +248,14 @@ func (w *AuthenticationWriter) seal(b []byte) (*buf.Buffer, error) { paddingSize = int32(w.padding.NextPaddingLen()) } - totalSize := encryptedSize + paddingSize + sizeBytes := w.sizeParser.SizeBytes() + totalSize := sizeBytes + encryptedSize + paddingSize if totalSize > buf.Size { return nil, newError("size too large: ", totalSize) } eb := buf.New() - w.sizeParser.Encode(uint16(encryptedSize+paddingSize), eb.Extend(w.sizeParser.SizeBytes())) + w.sizeParser.Encode(uint16(encryptedSize+paddingSize), eb.Extend(sizeBytes)) if _, err := w.auth.Seal(eb.Extend(encryptedSize)[:0], b); err != nil { eb.Release() return nil, err diff --git a/common/mux/server.go b/common/mux/server.go index 48c4a23a..8985bdd7 100644 --- a/common/mux/server.go +++ b/common/mux/server.go @@ -35,7 +35,7 @@ func (s *Server) Type() interface{} { return s.dispatcher.Type() } -// Dispatch impliments routing.Dispatcher +// Dispatch implements routing.Dispatcher func (s *Server) Dispatch(ctx context.Context, dest net.Destination) (*transport.Link, error) { if dest.Address != muxCoolAddress { return s.dispatcher.Dispatch(ctx, dest) diff --git a/common/session/context.go b/common/session/context.go index a836bc6b..7f1d7df9 100644 --- a/common/session/context.go +++ b/common/session/context.go @@ -10,6 +10,7 @@ const ( outboundSessionKey contentSessionKey muxPreferedSessionKey + sockoptSessionKey ) // ContextWithID returns a new context with the given ID. @@ -70,3 +71,16 @@ func MuxPreferedFromContext(ctx context.Context) bool { } return false } + +// ContextWithSockopt returns a new context with Socket configs included +func ContextWithSockopt(ctx context.Context, s *Sockopt) context.Context { + return context.WithValue(ctx, sockoptSessionKey, s) +} + +// SockoptFromContext returns Socket configs in this context, or nil if not contained. +func SockoptFromContext(ctx context.Context) *Sockopt { + if sockopt, ok := ctx.Value(sockoptSessionKey).(*Sockopt); ok { + return sockopt + } + return nil +} diff --git a/common/session/session.go b/common/session/session.go index 52252648..86172c8a 100644 --- a/common/session/session.go +++ b/common/session/session.go @@ -72,6 +72,12 @@ type Content struct { SkipRoutePick bool } +// Sockopt is the settings for socket connection. +type Sockopt struct { + // Mark of the socket connection. + Mark int32 +} + func (c *Content) SetAttribute(name string, value interface{}) { if c.Attributes == nil { c.Attributes = make(map[string]interface{}) diff --git a/core.go b/core.go index 2844a270..58d8723b 100644 --- a/core.go +++ b/core.go @@ -19,7 +19,7 @@ import ( ) var ( - version = "4.26.0" + version = "4.27.0" build = "Custom" codename = "V2Fly, a community-driven edition of V2Ray." intro = "A unified platform for anti-censorship." diff --git a/features/stats/stats.go b/features/stats/stats.go index 267416e6..abc2ba18 100644 --- a/features/stats/stats.go +++ b/features/stats/stats.go @@ -22,7 +22,7 @@ type Counter interface { type Manager interface { features.Feature - // RegisterCounter registers a new counter to the manager. The identifier string must not be emtpy, and unique among other counters. + // RegisterCounter registers a new counter to the manager. The identifier string must not be empty, and unique among other counters. RegisterCounter(string) (Counter, error) UnregisterCounter(string) error // GetCounter returns a counter by its identifier. diff --git a/go.mod b/go.mod index 5ce4ac05..40335fe8 100644 --- a/go.mod +++ b/go.mod @@ -2,12 +2,11 @@ module v2ray.com/core require ( github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc // indirect - github.com/golang/mock v1.4.3 + github.com/golang/mock v1.4.4 github.com/golang/protobuf v1.4.2 - github.com/google/go-cmp v0.5.0 + github.com/google/go-cmp v0.5.1 github.com/gorilla/websocket v1.4.2 - github.com/miekg/dns v1.1.29 - github.com/refraction-networking/utls v0.0.0-20190909200633-43c36d3c1f57 + github.com/miekg/dns v1.1.31 github.com/seiflotfy/cuckoofilter v0.0.0-20200511222245-56093a4d3841 github.com/stretchr/testify v1.6.1 github.com/xiaokangwang/VSign v0.0.0-20200704130305-63f4b4d7a751 @@ -16,7 +15,7 @@ require ( golang.org/x/net v0.0.0-20200602114024-627f9648deb9 golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd - google.golang.org/grpc v1.30.0 + google.golang.org/grpc v1.31.0 google.golang.org/protobuf v1.25.0 h12.io/socks v1.0.1 ) diff --git a/go.sum b/go.sum index f34cc196..dcf98418 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -38,19 +40,23 @@ github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364 h1:5XxdakFhqd9dnXoAZy1Mb2R/DZ6D1e+0bGC/JhucGYI= github.com/h12w/go-socks5 v0.0.0-20200522160539-76189e178364/go.mod h1:eDJQioIyy4Yn3MVivT7rv/39gAJTrA7lgmYr8EW950c= github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg= github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo= +github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= +github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo= +github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/refraction-networking/utls v0.0.0-20190909200633-43c36d3c1f57 h1:SL1K0QAuC1b54KoY1pjPWe6kSlsFHwK9/oC960fKrTY= -github.com/refraction-networking/utls v0.0.0-20190909200633-43c36d3c1f57/go.mod h1:tz9gX959MEFfFN5whTIocCLUG57WiILqtdVxI8c6Wj0= github.com/seiflotfy/cuckoofilter v0.0.0-20200511222245-56093a4d3841 h1:pnfutQFsV7ySmHUeX6ANGfPsBo29RctUvDn8G3rmJVw= github.com/seiflotfy/cuckoofilter v0.0.0-20200511222245-56093a4d3841/go.mod h1:ET5mVvNjwaGXRgZxO9UZr7X+8eAf87AfIYNwRSp9s4Y= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -126,6 +132,8 @@ google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0 h1:T7P4R73V3SSDPhH7WW7ATbfViLtmamH0DKrP3f9AuDI= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/infra/bazel/build.bzl b/infra/bazel/build.bzl index 31217570..57ffc91d 100644 --- a/infra/bazel/build.bzl +++ b/infra/bazel/build.bzl @@ -3,7 +3,7 @@ def _go_command(ctx): if ctx.attr.os == "windows": output = output + ".exe" - output_file = ctx.actions.declare_file(ctx.attr.os + "/" + ctx.attr.arch + "/" + output) + output_file = ctx.actions.declare_file(ctx.attr.os + "/" + ctx.attr.arch + "/" + ctx.attr.ver + "/" + output) pkg = ctx.attr.pkg ld_flags = "-s -w" @@ -61,6 +61,7 @@ foreign_go_binary = rule( 'output': attr.string(), 'os': attr.string(mandatory=True), 'arch': attr.string(mandatory=True), + 'ver': attr.string(mandatory=True), 'mips': attr.string(), 'arm': attr.string(), 'ld': attr.string(), diff --git a/infra/bazel/matrix.bzl b/infra/bazel/matrix.bzl index 1c53021d..3db57d5b 100644 --- a/infra/bazel/matrix.bzl +++ b/infra/bazel/matrix.bzl @@ -1,22 +1,25 @@ SUPPORTED_MATRIX = [ - ("windows", "amd64"), - ("windows", "386"), - ("windows", "arm"), - ("darwin", "amd64"), - ("linux", "amd64"), - ("linux", "386"), - ("linux", "arm64"), - ("linux", "arm"), - ("linux", "mips64"), - ("linux", "mips"), - ("linux", "mips64le"), - ("linux", "mipsle"), - ("linux", "ppc64"), - ("linux", "ppc64le"), - ("linux", "s390x"), - ("freebsd", "amd64"), - ("freebsd", "386"), - ("openbsd", "amd64"), - ("openbsd", "386"), - ("dragonfly", "amd64"), + ("windows", "amd64", "0"), + ("windows", "386", "0"), + ("windows", "arm", "7"), + ("darwin", "amd64", "0"), + ("linux", "amd64", "0"), + ("linux", "386", "0"), + ("linux", "arm64", "0"), + ("linux", "arm", "7"), + ("linux", "arm", "6"), + ("linux", "arm", "5"), + ("linux", "mips64", "0"), + ("linux", "mips", "0"), + ("linux", "mips64le", "0"), + ("linux", "mipsle", "0"), + ("linux", "ppc64", "0"), + ("linux", "ppc64le", "0"), + ("linux", "riscv64", "0"), + ("linux", "s390x", "0"), + ("freebsd", "amd64", "0"), + ("freebsd", "386", "0"), + ("openbsd", "amd64", "0"), + ("openbsd", "386", "0"), + ("dragonfly", "amd64", "0"), ] diff --git a/infra/conf/router.go b/infra/conf/router.go index b3870e45..cf4879d2 100644 --- a/infra/conf/router.go +++ b/infra/conf/router.go @@ -260,9 +260,19 @@ func parseDomainRule(domain string) ([]*router.Domain, error) { } return domains, nil } - - if strings.HasPrefix(domain, "ext:") { - kv := strings.Split(domain[4:], ":") + var isExtDatFile = 0 + { + const prefix = "ext:" + if strings.HasPrefix(domain, prefix) { + isExtDatFile = len(prefix) + } + const prefixQualified = "ext-domain:" + if strings.HasPrefix(domain, prefixQualified) { + isExtDatFile = len(prefixQualified) + } + } + if isExtDatFile != 0 { + kv := strings.Split(domain[isExtDatFile:], ":") if len(kv) != 2 { return nil, newError("invalid external resource: ", domain) } @@ -314,9 +324,19 @@ func toCidrList(ips StringList) ([]*router.GeoIP, error) { }) continue } - - if strings.HasPrefix(ip, "ext:") { - kv := strings.Split(ip[4:], ":") + var isExtDatFile = 0 + { + const prefix = "ext:" + if strings.HasPrefix(ip, prefix) { + isExtDatFile = len(prefix) + } + const prefixQualified = "ext-ip:" + if strings.HasPrefix(ip, prefixQualified) { + isExtDatFile = len(prefixQualified) + } + } + if isExtDatFile != 0 { + kv := strings.Split(ip[isExtDatFile:], ":") if len(kv) != 2 { return nil, newError("invalid external resource: ", ip) } diff --git a/infra/conf/shadowsocks.go b/infra/conf/shadowsocks.go index 8f61ccf8..efd62f12 100644 --- a/infra/conf/shadowsocks.go +++ b/infra/conf/shadowsocks.go @@ -26,6 +26,8 @@ func cipherFromString(c string) shadowsocks.CipherType { return shadowsocks.CipherType_AES_256_GCM case "chacha20-poly1305", "aead_chacha20_poly1305", "chacha20-ietf-poly1305": return shadowsocks.CipherType_CHACHA20_POLY1305 + case "none", "plain": + return shadowsocks.CipherType_NONE default: return shadowsocks.CipherType_UNKNOWN } diff --git a/infra/conf/v2ray.go b/infra/conf/v2ray.go index 8f0d7d1a..f8499923 100644 --- a/infra/conf/v2ray.go +++ b/infra/conf/v2ray.go @@ -19,6 +19,7 @@ var ( "http": func() interface{} { return new(HttpServerConfig) }, "shadowsocks": func() interface{} { return new(ShadowsocksServerConfig) }, "socks": func() interface{} { return new(SocksServerConfig) }, + "vless": func() interface{} { return new(VLessInboundConfig) }, "vmess": func() interface{} { return new(VMessInboundConfig) }, "mtproto": func() interface{} { return new(MTProtoServerConfig) }, }, "protocol", "settings") @@ -28,8 +29,9 @@ var ( "freedom": func() interface{} { return new(FreedomConfig) }, "http": func() interface{} { return new(HttpClientConfig) }, "shadowsocks": func() interface{} { return new(ShadowsocksClientConfig) }, - "vmess": func() interface{} { return new(VMessOutboundConfig) }, "socks": func() interface{} { return new(SocksClientConfig) }, + "vless": func() interface{} { return new(VLessOutboundConfig) }, + "vmess": func() interface{} { return new(VMessOutboundConfig) }, "mtproto": func() interface{} { return new(MTProtoClientConfig) }, "dns": func() interface{} { return new(DnsOutboundConfig) }, }, "protocol", "settings") diff --git a/infra/conf/vless.go b/infra/conf/vless.go new file mode 100644 index 00000000..b1353705 --- /dev/null +++ b/infra/conf/vless.go @@ -0,0 +1,175 @@ +package conf + +import ( + "encoding/json" + + "github.com/golang/protobuf/proto" + + "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" + "v2ray.com/core/common/serial" + "v2ray.com/core/proxy/vless" + "v2ray.com/core/proxy/vless/inbound" + "v2ray.com/core/proxy/vless/outbound" +) + +type VLessInboundFallback struct { + Addr *Address `json:"addr"` + Port uint16 `json:"port"` + Unix string `json:"unix"` + Xver uint16 `json:"xver"` +} + +type VLessInboundConfig struct { + Users []json.RawMessage `json:"clients"` + Decryption string `json:"decryption"` + Fallback *VLessInboundFallback `json:"fallback"` + Fallback_h2 *VLessInboundFallback `json:"fallback_h2"` +} + +// Build implements Buildable +func (c *VLessInboundConfig) Build() (proto.Message, error) { + + config := new(inbound.Config) + + if c.Decryption != "none" { + return nil, newError(`please add/set "decryption":"none" directly to every VLESS "settings"`) + } + config.Decryption = c.Decryption + + if c.Fallback != nil { + if c.Fallback.Xver > 2 { + return nil, newError(`VLESS "fallback": invalid PROXY protocol version, "xver" only accepts 0, 1, 2`) + } + if c.Fallback.Unix != "" { + if c.Fallback.Unix[0] == '@' { + c.Fallback.Unix = "\x00" + c.Fallback.Unix[1:] + } + } else { + if c.Fallback.Port == 0 { + return nil, newError(`please fill in a valid value for "port" in VLESS "fallback"`) + } + } + if c.Fallback.Addr == nil { + c.Fallback.Addr = &Address{ + Address: net.ParseAddress("127.0.0.1"), + } + } + config.Fallback = &inbound.Fallback{ + Addr: c.Fallback.Addr.Build(), + Port: uint32(c.Fallback.Port), + Unix: c.Fallback.Unix, + Xver: uint32(c.Fallback.Xver), + } + } + + if c.Fallback_h2 != nil { + if config.Fallback == nil { + return nil, newError(`VLESS "fallback_h2" can't exist alone without "fallback"`) + } + if c.Fallback_h2.Xver > 2 { + return nil, newError(`VLESS "fallback_h2": invalid PROXY protocol version, "xver" only accepts 0, 1, 2`) + } + if c.Fallback_h2.Unix != "" { + if c.Fallback_h2.Unix[0] == '@' { + c.Fallback_h2.Unix = "\x00" + c.Fallback_h2.Unix[1:] + } + } else { + if c.Fallback_h2.Port == 0 { + return nil, newError(`please fill in a valid value for "port" in VLESS "fallback_h2"`) + } + } + if c.Fallback_h2.Addr == nil { + c.Fallback_h2.Addr = &Address{ + Address: net.ParseAddress("127.0.0.1"), + } + } + config.FallbackH2 = &inbound.FallbackH2{ + Addr: c.Fallback_h2.Addr.Build(), + Port: uint32(c.Fallback_h2.Port), + Unix: c.Fallback_h2.Unix, + Xver: uint32(c.Fallback_h2.Xver), + } + } + + config.User = make([]*protocol.User, len(c.Users)) + for idx, rawData := range c.Users { + user := new(protocol.User) + if err := json.Unmarshal(rawData, user); err != nil { + return nil, newError("invalid VLESS user").Base(err) + } + account := new(vless.Account) + if err := json.Unmarshal(rawData, account); err != nil { + return nil, newError("invalid VLESS user").Base(err) + } + + if account.Schedulers != "" { + return nil, newError(`VLESS attr "schedulers" is not available in this version`) + } + if account.Encryption != "" { + return nil, newError(`VLESS attr "encryption" should not in inbound settings`) + } + + user.Account = serial.ToTypedMessage(account) + config.User[idx] = user + } + + return config, nil +} + +type VLessOutboundTarget struct { + Address *Address `json:"address"` + Port uint16 `json:"port"` + Users []json.RawMessage `json:"users"` +} + +type VLessOutboundConfig struct { + Receivers []*VLessOutboundTarget `json:"vnext"` +} + +// Build implements Buildable +func (c *VLessOutboundConfig) Build() (proto.Message, error) { + + config := new(outbound.Config) + + if len(c.Receivers) == 0 { + return nil, newError("0 VLESS receiver configured") + } + serverSpecs := make([]*protocol.ServerEndpoint, len(c.Receivers)) + for idx, rec := range c.Receivers { + if len(rec.Users) == 0 { + return nil, newError("0 user configured for VLESS outbound") + } + if rec.Address == nil { + return nil, newError("address is not set in VLESS outbound config") + } + spec := &protocol.ServerEndpoint{ + Address: rec.Address.Build(), + Port: uint32(rec.Port), + } + for _, rawUser := range rec.Users { + user := new(protocol.User) + if err := json.Unmarshal(rawUser, user); err != nil { + return nil, newError("invalid VLESS user").Base(err) + } + account := new(vless.Account) + if err := json.Unmarshal(rawUser, account); err != nil { + return nil, newError("invalid VLESS user").Base(err) + } + + if account.Schedulers != "" { + return nil, newError(`VLESS attr "schedulers" is not available in this version`) + } + if account.Encryption != "none" { + return nil, newError(`please add/set "encryption":"none" for every VLESS user in "users"`) + } + + user.Account = serial.ToTypedMessage(account) + spec.User = append(spec.User, user) + } + serverSpecs[idx] = spec + } + config.Receiver = serverSpecs + + return config, nil +} diff --git a/infra/conf/vless_test.go b/infra/conf/vless_test.go new file mode 100644 index 00000000..813ef80f --- /dev/null +++ b/infra/conf/vless_test.go @@ -0,0 +1,121 @@ +package conf_test + +import ( + "testing" + + "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" + "v2ray.com/core/common/serial" + . "v2ray.com/core/infra/conf" + "v2ray.com/core/proxy/vless" + "v2ray.com/core/proxy/vless/inbound" + "v2ray.com/core/proxy/vless/outbound" +) + +func TestVLessOutbound(t *testing.T) { + creator := func() Buildable { + return new(VLessOutboundConfig) + } + + runMultiTestCase(t, []TestCase{ + { + Input: `{ + "vnext": [{ + "address": "example.com", + "port": 443, + "users": [ + { + "id": "27848739-7e62-4138-9fd3-098a63964b6b", + "schedulers": "", + "encryption": "none", + "level": 0 + } + ] + }] + }`, + Parser: loadJSON(creator), + Output: &outbound.Config{ + Receiver: []*protocol.ServerEndpoint{ + { + Address: &net.IPOrDomain{ + Address: &net.IPOrDomain_Domain{ + Domain: "example.com", + }, + }, + Port: 443, + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vless.Account{ + Id: "27848739-7e62-4138-9fd3-098a63964b6b", + Schedulers: "", + Encryption: "none", + }), + Level: 0, + }, + }, + }, + }, + }, + }, + }) +} + +func TestVLessInbound(t *testing.T) { + creator := func() Buildable { + return new(VLessInboundConfig) + } + + runMultiTestCase(t, []TestCase{ + { + Input: `{ + "clients": [ + { + "id": "27848739-7e62-4138-9fd3-098a63964b6b", + "schedulers": "", + "level": 0, + "email": "love@v2fly.org" + } + ], + "decryption": "none", + "fallback": { + "port": 80 + }, + "fallback_h2": { + "unix": "@/dev/shm/domain.socket", + "xver": 2 + } + }`, + Parser: loadJSON(creator), + Output: &inbound.Config{ + User: []*protocol.User{ + { + Account: serial.ToTypedMessage(&vless.Account{ + Id: "27848739-7e62-4138-9fd3-098a63964b6b", + Schedulers: "", + }), + Level: 0, + Email: "love@v2fly.org", + }, + }, + Decryption: "none", + Fallback: &inbound.Fallback{ + Addr: &net.IPOrDomain{ + Address: &net.IPOrDomain_Ip{ + Ip: []byte{127, 0, 0, 1}, + }, + }, + Port: 80, + }, + FallbackH2: &inbound.FallbackH2{ + Addr: &net.IPOrDomain{ + Address: &net.IPOrDomain_Ip{ + Ip: []byte{127, 0, 0, 1}, + }, + }, + Unix: "\x00/dev/shm/domain.socket", + Xver: 2, + }, + }, + }, + }) +} diff --git a/infra/control/main/BUILD b/infra/control/main/BUILD index 58691980..02ba8976 100644 --- a/infra/control/main/BUILD +++ b/infra/control/main/BUILD @@ -1,5 +1,3 @@ -load("//infra/bazel:build.bzl", "foreign_go_binary") -load("//infra/bazel:gpg.bzl", "gpg_sign") load("//infra/bazel:matrix.bzl", "SUPPORTED_MATRIX") load("//infra/control/main:targets.bzl", "gen_targets") diff --git a/infra/control/main/targets.bzl b/infra/control/main/targets.bzl index 91bdf608..9e576ba8 100644 --- a/infra/control/main/targets.bzl +++ b/infra/control/main/targets.bzl @@ -2,67 +2,21 @@ load("//infra/bazel:build.bzl", "foreign_go_binary") load("//infra/bazel:gpg.bzl", "gpg_sign") def gen_targets(matrix): - output = "v2ctl" pkg = "v2ray.com/core/infra/control/main" + output = "v2ctl" - for (os, arch) in matrix: - bin_name = "v2ctl_" + os + "_" + arch - foreign_go_binary( - name = bin_name, - pkg = pkg, - output = output, - os = os, - arch = arch, - gotags = "confonly", - ) - - gpg_sign( - name = bin_name + "_sig", - base = ":" + bin_name, - ) + for (os, arch, ver) in matrix: - if arch in ["mips", "mipsle"]: - bin_name = "v2ctl_" + os + "_" + arch + "_softfloat" - foreign_go_binary( - name = bin_name, - pkg = pkg, - output = output + "_softfloat", - os = os, - arch = arch, - mips = "softfloat", - gotags = "confonly", - ) - - gpg_sign( - name = bin_name + "_sig", - base = ":" + bin_name, - ) - if arch in ["arm"]: - bin_name = "v2ctl_" + os + "_" + arch + "_armv7" + bin_name = "v2ctl_" + os + "_" + arch + "_" + ver foreign_go_binary( name = bin_name, pkg = pkg, - output = output + "_armv7", - os = os, - arch = arch, - arm = "7", - gotags = "confonly", - ) - - gpg_sign( - name = bin_name + "_sig", - base = ":" + bin_name, - ) - - bin_name = "v2ctl_" + os + "_" + arch + "_armv6" - foreign_go_binary( - name = bin_name, - pkg = pkg, - output = output + "_armv6", + output = output, os = os, arch = arch, - arm = "6", + ver = ver, + arm = ver, gotags = "confonly", ) @@ -71,14 +25,15 @@ def gen_targets(matrix): base = ":" + bin_name, ) - bin_name = "v2ctl_" + os + "_" + arch + "_armv5" + else: + bin_name = "v2ctl_" + os + "_" + arch foreign_go_binary( name = bin_name, pkg = pkg, output = output, os = os, arch = arch, - arm = "5", + ver = ver, gotags = "confonly", ) @@ -86,3 +41,21 @@ def gen_targets(matrix): name = bin_name + "_sig", base = ":" + bin_name, ) + + if arch in ["mips", "mipsle"]: + bin_name = "v2ctl_" + os + "_" + arch + "_softfloat" + foreign_go_binary( + name = bin_name, + pkg = pkg, + output = output + "_softfloat", + os = os, + arch = arch, + ver = ver, + mips = "softfloat", + gotags = "confonly", + ) + + gpg_sign( + name = bin_name + "_sig", + base = ":" + bin_name, + ) diff --git a/main/BUILD b/main/BUILD index cab4297d..98a45e1f 100644 --- a/main/BUILD +++ b/main/BUILD @@ -1,5 +1,3 @@ -load("//infra/bazel:build.bzl", "foreign_go_binary") -load("//infra/bazel:gpg.bzl", "gpg_sign") load("//infra/bazel:matrix.bzl", "SUPPORTED_MATRIX") load("//main:targets.bzl", "gen_targets") diff --git a/main/distro/all/all.go b/main/distro/all/all.go index 71144c08..9df5b6ac 100644 --- a/main/distro/all/all.go +++ b/main/distro/all/all.go @@ -31,6 +31,8 @@ import ( _ "v2ray.com/core/proxy/mtproto" _ "v2ray.com/core/proxy/shadowsocks" _ "v2ray.com/core/proxy/socks" + _ "v2ray.com/core/proxy/vless/inbound" + _ "v2ray.com/core/proxy/vless/outbound" _ "v2ray.com/core/proxy/vmess/inbound" _ "v2ray.com/core/proxy/vmess/outbound" diff --git a/main/targets.bzl b/main/targets.bzl index 95fbc21a..0bd7eb46 100644 --- a/main/targets.bzl +++ b/main/targets.bzl @@ -5,125 +5,18 @@ def gen_targets(matrix): pkg = "v2ray.com/core/main" output = "v2ray" - for (os, arch) in matrix: - bin_name = "v2ray_" + os + "_" + arch - foreign_go_binary( - name = bin_name, - pkg = pkg, - output = output, - os = os, - arch = arch, - ) + for (os, arch, ver) in matrix: - gpg_sign( - name = bin_name + "_sig", - base = ":" + bin_name, - ) - - if os in ["windows"]: - bin_name = "v2ray_" + os + "_" + arch + "_nowindow" - foreign_go_binary( - name = bin_name, - pkg = pkg, - output = "w" + output, - os = os, - arch = arch, - ld = "-H windowsgui", - ) - - gpg_sign( - name = bin_name + "_sig", - base = ":" + bin_name, - ) - - bin_name = "v2ray_" + os + "_" + arch + "_armv7_nowindow" - foreign_go_binary( - name = bin_name, - pkg = pkg, - output = "w" + output + "_armv7", - os = os, - arch = arch, - arm = "7", - ld = "-H windowsgui", - ) - - gpg_sign( - name = bin_name + "_sig", - base = ":" + bin_name, - ) - - bin_name = "v2ray_" + os + "_" + arch + "_armv6_nowindow" - foreign_go_binary( - name = bin_name, - pkg = pkg, - output = "w" + output + "_armv6", - os = os, - arch = arch, - arm = "6", - ld = "-H windowsgui", - ) - - gpg_sign( - name = bin_name + "_sig", - base = ":" + bin_name, - ) - - bin_name = "v2ray_" + os + "_" + arch + "_armv5_nowindow" - foreign_go_binary( - name = bin_name, - pkg = pkg, - output = "w" + output, - os = os, - arch = arch, - arm = "5", - ld = "-H windowsgui", - ) - - gpg_sign( - name = bin_name + "_sig", - base = ":" + bin_name, - ) - - if arch in ["mips", "mipsle"]: - bin_name = "v2ray_" + os + "_" + arch + "_softfloat" - foreign_go_binary( - name = bin_name, - pkg = pkg, - output = output+"_softfloat", - os = os, - arch = arch, - mips = "softfloat", - ) - - gpg_sign( - name = bin_name + "_sig", - base = ":" + bin_name, - ) - if arch in ["arm"]: - bin_name = "v2ray_" + os + "_" + arch + "_armv7" - foreign_go_binary( - name = bin_name, - pkg = pkg, - output = output + "_armv7", - os = os, - arch = arch, - arm = "7", - ) - - gpg_sign( - name = bin_name + "_sig", - base = ":" + bin_name, - ) - - bin_name = "v2ray_" + os + "_" + arch + "_armv6" + bin_name = "v2ray_" + os + "_" + arch + "_" + ver foreign_go_binary( name = bin_name, pkg = pkg, - output = output + "_armv6", + output = output, os = os, arch = arch, - arm = "6", + ver = ver, + arm = ver, ) gpg_sign( @@ -131,17 +24,70 @@ def gen_targets(matrix): base = ":" + bin_name, ) - bin_name = "v2ray_" + os + "_" + arch + "_armv5" + if os in ["windows"]: + bin_name = "v2ray_" + os + "_" + arch + "_" + ver + "_nowindow" + foreign_go_binary( + name = bin_name, + pkg = pkg, + output = "w" + output, + os = os, + arch = arch, + ver = ver, + arm = ver, + ld = "-H windowsgui", + ) + + gpg_sign( + name = bin_name + "_sig", + base = ":" + bin_name, + ) + + else: + bin_name = "v2ray_" + os + "_" + arch foreign_go_binary( name = bin_name, pkg = pkg, output = output, os = os, arch = arch, - arm = "5", + ver = ver, ) gpg_sign( name = bin_name + "_sig", base = ":" + bin_name, ) + + if os in ["windows"]: + bin_name = "v2ray_" + os + "_" + arch + "_nowindow" + foreign_go_binary( + name = bin_name, + pkg = pkg, + output = "w" + output, + os = os, + arch = arch, + ver = ver, + ld = "-H windowsgui", + ) + + gpg_sign( + name = bin_name + "_sig", + base = ":" + bin_name, + ) + + if arch in ["mips", "mipsle"]: + bin_name = "v2ray_" + os + "_" + arch + "_softfloat" + foreign_go_binary( + name = bin_name, + pkg = pkg, + output = output + "_softfloat", + os = os, + arch = arch, + ver = ver, + mips = "softfloat", + ) + + gpg_sign( + name = bin_name + "_sig", + base = ":" + bin_name, + ) diff --git a/proxy/dokodemo/dokodemo.go b/proxy/dokodemo/dokodemo.go index 33930cc4..24ebe098 100644 --- a/proxy/dokodemo/dokodemo.go +++ b/proxy/dokodemo/dokodemo.go @@ -12,6 +12,7 @@ import ( "v2ray.com/core" "v2ray.com/core/common" "v2ray.com/core/common/buf" + "v2ray.com/core/common/log" "v2ray.com/core/common/net" "v2ray.com/core/common/protocol" "v2ray.com/core/common/session" @@ -26,7 +27,7 @@ func init() { common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { d := new(DokodemoDoor) err := core.RequireFeatures(ctx, func(pm policy.Manager) error { - return d.Init(config.(*Config), pm) + return d.Init(config.(*Config), pm, session.SockoptFromContext(ctx)) }) return d, err })) @@ -37,10 +38,11 @@ type DokodemoDoor struct { config *Config address net.Address port net.Port + sockopt *session.Sockopt } // Init initializes the DokodemoDoor instance with necessary parameters. -func (d *DokodemoDoor) Init(config *Config, pm policy.Manager) error { +func (d *DokodemoDoor) Init(config *Config, pm policy.Manager, sockopt *session.Sockopt) error { if (config.NetworkList == nil || len(config.NetworkList.Network) == 0) && len(config.Networks) == 0 { return newError("no network specified") } @@ -48,6 +50,7 @@ func (d *DokodemoDoor) Init(config *Config, pm policy.Manager) error { d.address = config.GetPredefinedAddress() d.port = net.Port(config.Port) d.policyManager = pm + d.sockopt = sockopt return nil } @@ -106,6 +109,14 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in } } + ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ + From: conn.RemoteAddr(), + To: dest, + Status: log.AccessAccepted, + Reason: "", + }) + newError("received request for ", conn.RemoteAddr()).WriteToLog(session.ExportIDToError(ctx)) + plcy := d.policy() ctx, cancel := context.WithCancel(ctx) timer := signal.CancelAfterInactivity(ctx, cancel, plcy.Timeouts.ConnectionIdle) @@ -156,6 +167,9 @@ func (d *DokodemoDoor) Process(ctx context.Context, network net.Network, conn in sockopt.BindAddress = dest.Address.IP() sockopt.BindPort = uint32(dest.Port) } + if d.sockopt != nil { + sockopt.Mark = d.sockopt.Mark + } tConn, err := internet.DialSystem(ctx, net.DestinationFromAddr(conn.RemoteAddr()), sockopt) if err != nil { return err diff --git a/proxy/http/client.go b/proxy/http/client.go index 5dd2d7c0..120a5299 100644 --- a/proxy/http/client.go +++ b/proxy/http/client.go @@ -71,6 +71,7 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter return newError("target not specified.") } target := outbound.Target + targetAddr := target.NetAddr() if target.Network == net.Network_UDP { return newError("UDP is not supported by HTTP outbound") @@ -83,9 +84,8 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter server := c.serverPicker.PickServer() dest := server.Destination() user = server.PickUser() - targetAddr := target.NetAddr() - netConn, err := setUpHttpTunnel(ctx, dest, targetAddr, user, dialer) + netConn, err := setUpHTTPTunnel(ctx, dest, targetAddr, user, dialer) if netConn != nil { conn = internet.Connection(netConn) } @@ -125,27 +125,22 @@ func (c *Client) Process(ctx context.Context, link *transport.Link, dialer inter return nil } -// setUpHttpTunnel will create a socket tunnel via HTTP CONNECT method -func setUpHttpTunnel(ctx context.Context, dest net.Destination, target string, user *protocol.MemoryUser, dialer internet.Dialer) (net.Conn, error) { - req := (&http.Request{ - Method: "CONNECT", +// setUpHTTPTunnel will create a socket tunnel via HTTP CONNECT method +func setUpHTTPTunnel(ctx context.Context, dest net.Destination, target string, user *protocol.MemoryUser, dialer internet.Dialer) (net.Conn, error) { + req := &http.Request{ + Method: http.MethodConnect, URL: &url.URL{Host: target}, - Header: make(http.Header), + Header: http.Header{"Proxy-Connection": []string{"Keep-Alive"}}, Host: target, - }).WithContext(ctx) + } if user != nil && user.Account != nil { account := user.Account.(*Account) auth := account.GetUsername() + ":" + account.GetPassword() req.Header.Set("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth))) } - req.Header.Set("Proxy-Connection", "Keep-Alive") - - connectHttp1 := func(rawConn net.Conn) (net.Conn, error) { - req.Proto = "HTTP/1.1" - req.ProtoMajor = 1 - req.ProtoMinor = 1 + connectHTTP1 := func(rawConn net.Conn) (net.Conn, error) { err := req.Write(rawConn) if err != nil { rawConn.Close() @@ -165,10 +160,7 @@ func setUpHttpTunnel(ctx context.Context, dest net.Destination, target string, u return rawConn, nil } - connectHttp2 := func(rawConn net.Conn, h2clientConn *http2.ClientConn) (net.Conn, error) { - req.Proto = "HTTP/2.0" - req.ProtoMajor = 2 - req.ProtoMinor = 0 + connectHTTP2 := func(rawConn net.Conn, h2clientConn *http2.ClientConn) (net.Conn, error) { pr, pw := io.Pipe() req.Body = pr @@ -182,24 +174,21 @@ func setUpHttpTunnel(ctx context.Context, dest net.Destination, target string, u rawConn.Close() return nil, newError("Proxy responded with non 200 code: " + resp.Status) } - return newHttp2Conn(rawConn, pw, resp.Body), nil + return newHTTP2Conn(rawConn, pw, resp.Body), nil } cachedH2Mutex.Lock() defer cachedH2Mutex.Unlock() if cachedConn, found := cachedH2Conns[dest]; found { - if cachedConn.rawConn != nil && cachedConn.h2Conn != nil { - rc := cachedConn.rawConn - cc := cachedConn.h2Conn - if cc.CanTakeNewRequest() { - proxyConn, err := connectHttp2(rc, cc) - if err != nil { - return nil, err - } - - return proxyConn, nil + rc, cc := cachedConn.rawConn, cachedConn.h2Conn + if cc.CanTakeNewRequest() { + proxyConn, err := connectHTTP2(rc, cc) + if err != nil { + return nil, err } + + return proxyConn, nil } } @@ -208,8 +197,13 @@ func setUpHttpTunnel(ctx context.Context, dest net.Destination, target string, u return nil, err } + iConn := rawConn + if statConn, ok := iConn.(*internet.StatCouterConnection); ok { + iConn = statConn.Connection + } + nextProto := "" - if tlsConn, ok := rawConn.(*tls.Conn); ok { + if tlsConn, ok := iConn.(*tls.Conn); ok { if err := tlsConn.Handshake(); err != nil { rawConn.Close() return nil, err @@ -218,10 +212,8 @@ func setUpHttpTunnel(ctx context.Context, dest net.Destination, target string, u } switch nextProto { - case "": - fallthrough - case "http/1.1": - return connectHttp1(rawConn) + case "", "http/1.1": + return connectHTTP1(rawConn) case "h2": t := http2.Transport{} h2clientConn, err := t.NewClientConn(rawConn) @@ -230,7 +222,7 @@ func setUpHttpTunnel(ctx context.Context, dest net.Destination, target string, u return nil, err } - proxyConn, err := connectHttp2(rawConn, h2clientConn) + proxyConn, err := connectHTTP2(rawConn, h2clientConn) if err != nil { rawConn.Close() return nil, err @@ -251,7 +243,7 @@ func setUpHttpTunnel(ctx context.Context, dest net.Destination, target string, u } } -func newHttp2Conn(c net.Conn, pipedReqBody *io.PipeWriter, respBody io.ReadCloser) net.Conn { +func newHTTP2Conn(c net.Conn, pipedReqBody *io.PipeWriter, respBody io.ReadCloser) net.Conn { return &http2Conn{Conn: c, in: pipedReqBody, out: respBody} } @@ -274,18 +266,6 @@ func (h *http2Conn) Close() error { return h.out.Close() } -func (h *http2Conn) CloseConn() error { - return h.Conn.Close() -} - -func (h *http2Conn) CloseWrite() error { - return h.in.Close() -} - -func (h *http2Conn) CloseRead() error { - return h.out.Close() -} - func init() { common.Must(common.RegisterConfig((*ClientConfig)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { return NewClient(ctx, config.(*ClientConfig)) diff --git a/proxy/vless/account.go b/proxy/vless/account.go new file mode 100644 index 00000000..ed76d5de --- /dev/null +++ b/proxy/vless/account.go @@ -0,0 +1,40 @@ +// +build !confonly + +package vless + +import ( + "v2ray.com/core/common/protocol" + "v2ray.com/core/common/uuid" +) + +// AsAccount implements protocol.Account.AsAccount(). +func (a *Account) AsAccount() (protocol.Account, error) { + id, err := uuid.ParseString(a.Id) + if err != nil { + return nil, newError("failed to parse ID").Base(err).AtError() + } + return &MemoryAccount{ + ID: protocol.NewID(id), + Schedulers: a.Schedulers, // needs parser here? + Encryption: a.Encryption, // needs parser here? + }, nil +} + +// MemoryAccount is an in-memory form of VLess account. +type MemoryAccount struct { + // ID of the account. + ID *protocol.ID + // Schedulers of the account. + Schedulers string + // Encryption of the account. Used for client connections, and only accepts "none" for now. + Encryption string +} + +// Equals implements protocol.Account.Equals(). +func (a *MemoryAccount) Equals(account protocol.Account) bool { + vlessAccount, ok := account.(*MemoryAccount) + if !ok { + return false + } + return a.ID.Equals(vlessAccount.ID) +} diff --git a/proxy/vless/account.pb.go b/proxy/vless/account.pb.go new file mode 100644 index 00000000..da5cc167 --- /dev/null +++ b/proxy/vless/account.pb.go @@ -0,0 +1,175 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0 +// protoc v3.12.3 +// source: v2ray.com/core/proxy/vless/account.proto + +package vless + +import ( + proto "github.com/golang/protobuf/proto" + 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) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type Account struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // ID of the account, in the form of a UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57". + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // Schedulers settings. + Schedulers string `protobuf:"bytes,2,opt,name=schedulers,proto3" json:"schedulers,omitempty"` + // Encryption settings. Only applies to client side, and only accepts "none" for now. + Encryption string `protobuf:"bytes,3,opt,name=encryption,proto3" json:"encryption,omitempty"` +} + +func (x *Account) Reset() { + *x = Account{} + if protoimpl.UnsafeEnabled { + mi := &file_v2ray_com_core_proxy_vless_account_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Account) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Account) ProtoMessage() {} + +func (x *Account) ProtoReflect() protoreflect.Message { + mi := &file_v2ray_com_core_proxy_vless_account_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Account.ProtoReflect.Descriptor instead. +func (*Account) Descriptor() ([]byte, []int) { + return file_v2ray_com_core_proxy_vless_account_proto_rawDescGZIP(), []int{0} +} + +func (x *Account) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Account) GetSchedulers() string { + if x != nil { + return x.Schedulers + } + return "" +} + +func (x *Account) GetEncryption() string { + if x != nil { + return x.Encryption + } + return "" +} + +var File_v2ray_com_core_proxy_vless_account_proto protoreflect.FileDescriptor + +var file_v2ray_com_core_proxy_vless_account_proto_rawDesc = []byte{ + 0x0a, 0x28, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x16, 0x76, 0x32, 0x72, 0x61, + 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, + 0x73, 0x73, 0x22, 0x59, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, + 0x0a, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x0a, + 0x0a, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x3e, 0x0a, + 0x1a, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x05, 0x76, + 0x6c, 0x65, 0x73, 0x73, 0xaa, 0x02, 0x16, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, + 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_v2ray_com_core_proxy_vless_account_proto_rawDescOnce sync.Once + file_v2ray_com_core_proxy_vless_account_proto_rawDescData = file_v2ray_com_core_proxy_vless_account_proto_rawDesc +) + +func file_v2ray_com_core_proxy_vless_account_proto_rawDescGZIP() []byte { + file_v2ray_com_core_proxy_vless_account_proto_rawDescOnce.Do(func() { + file_v2ray_com_core_proxy_vless_account_proto_rawDescData = protoimpl.X.CompressGZIP(file_v2ray_com_core_proxy_vless_account_proto_rawDescData) + }) + return file_v2ray_com_core_proxy_vless_account_proto_rawDescData +} + +var file_v2ray_com_core_proxy_vless_account_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_v2ray_com_core_proxy_vless_account_proto_goTypes = []interface{}{ + (*Account)(nil), // 0: v2ray.core.proxy.vless.Account +} +var file_v2ray_com_core_proxy_vless_account_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_v2ray_com_core_proxy_vless_account_proto_init() } +func file_v2ray_com_core_proxy_vless_account_proto_init() { + if File_v2ray_com_core_proxy_vless_account_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_v2ray_com_core_proxy_vless_account_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Account); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_v2ray_com_core_proxy_vless_account_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_v2ray_com_core_proxy_vless_account_proto_goTypes, + DependencyIndexes: file_v2ray_com_core_proxy_vless_account_proto_depIdxs, + MessageInfos: file_v2ray_com_core_proxy_vless_account_proto_msgTypes, + }.Build() + File_v2ray_com_core_proxy_vless_account_proto = out.File + file_v2ray_com_core_proxy_vless_account_proto_rawDesc = nil + file_v2ray_com_core_proxy_vless_account_proto_goTypes = nil + file_v2ray_com_core_proxy_vless_account_proto_depIdxs = nil +} diff --git a/proxy/vless/account.proto b/proxy/vless/account.proto new file mode 100644 index 00000000..e259c78d --- /dev/null +++ b/proxy/vless/account.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package v2ray.core.proxy.vless; +option csharp_namespace = "V2Ray.Core.Proxy.Vless"; +option go_package = "vless"; +option java_package = "com.v2ray.core.proxy.vless"; +option java_multiple_files = true; + +message Account { + // ID of the account, in the form of a UUID, e.g., "66ad4540-b58c-4ad2-9926-ea63445a9b57". + string id = 1; + // Schedulers settings. + string schedulers = 2; + // Encryption settings. Only applies to client side, and only accepts "none" for now. + string encryption = 3; +} diff --git a/proxy/vless/encoding/addons.go b/proxy/vless/encoding/addons.go new file mode 100644 index 00000000..80d66f30 --- /dev/null +++ b/proxy/vless/encoding/addons.go @@ -0,0 +1,83 @@ +// +build !confonly + +package encoding + +import ( + "io" + + "github.com/golang/protobuf/proto" + + "v2ray.com/core/common/buf" + "v2ray.com/core/common/protocol" +) + +func EncodeHeaderAddons(buffer *buf.Buffer, addons *Addons) error { + + switch addons.Scheduler { + default: + + if err := buffer.WriteByte(0); err != nil { + return newError("failed to write addons protobuf length").Base(err) + } + + } + + return nil + +} + +func DecodeHeaderAddons(buffer *buf.Buffer, reader io.Reader) (*Addons, error) { + + addons := new(Addons) + + buffer.Clear() + if _, err := buffer.ReadFullFrom(reader, 1); err != nil { + return nil, newError("failed to read addons protobuf length").Base(err) + } + + if length := int32(buffer.Byte(0)); length != 0 { + + buffer.Clear() + if _, err := buffer.ReadFullFrom(reader, length); err != nil { + return nil, newError("failed to read addons protobuf value").Base(err) + } + + if err := proto.Unmarshal(buffer.Bytes(), addons); err != nil { + return nil, newError("failed to unmarshal addons protobuf value").Base(err) + } + + // Verification. + switch addons.Scheduler { + default: + + } + + } + + return addons, nil + +} + +// EncodeBodyAddons returns a Writer that auto-encrypt content written by caller. +func EncodeBodyAddons(writer io.Writer, request *protocol.RequestHeader, addons *Addons) buf.Writer { + + switch addons.Scheduler { + default: + + return buf.NewWriter(writer) + + } + +} + +// DecodeBodyAddons returns a Reader from which caller can fetch decrypted body. +func DecodeBodyAddons(reader io.Reader, request *protocol.RequestHeader, addons *Addons) buf.Reader { + + switch addons.Scheduler { + default: + + return buf.NewReader(reader) + + } + +} diff --git a/proxy/vless/encoding/addons.pb.go b/proxy/vless/encoding/addons.pb.go new file mode 100644 index 00000000..271356b6 --- /dev/null +++ b/proxy/vless/encoding/addons.pb.go @@ -0,0 +1,386 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: v2ray.com/core/proxy/vless/encoding/addons.proto + +package encoding + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +type Addons struct { + Scheduler string `protobuf:"bytes,1,opt,name=Scheduler,proto3" json:"Scheduler,omitempty"` + SchedulerV []byte `protobuf:"bytes,2,opt,name=SchedulerV,proto3" json:"SchedulerV,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Addons) Reset() { *m = Addons{} } +func (m *Addons) String() string { return proto.CompactTextString(m) } +func (*Addons) ProtoMessage() {} +func (*Addons) Descriptor() ([]byte, []int) { + return fileDescriptor_d597c8244066ecf1, []int{0} +} +func (m *Addons) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Addons) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Addons.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Addons) XXX_Merge(src proto.Message) { + xxx_messageInfo_Addons.Merge(m, src) +} +func (m *Addons) XXX_Size() int { + return m.Size() +} +func (m *Addons) XXX_DiscardUnknown() { + xxx_messageInfo_Addons.DiscardUnknown(m) +} + +var xxx_messageInfo_Addons proto.InternalMessageInfo + +func (m *Addons) GetScheduler() string { + if m != nil { + return m.Scheduler + } + return "" +} + +func (m *Addons) GetSchedulerV() []byte { + if m != nil { + return m.SchedulerV + } + return nil +} + +func init() { + proto.RegisterType((*Addons)(nil), "v2ray.core.proxy.vless.encoding.Addons") +} + +func init() { + proto.RegisterFile("v2ray.com/core/proxy/vless/encoding/addons.proto", fileDescriptor_d597c8244066ecf1) +} + +var fileDescriptor_d597c8244066ecf1 = []byte{ + // 193 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x28, 0x33, 0x2a, 0x4a, + 0xac, 0xd4, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0x2f, 0x4a, 0xd5, 0x2f, 0x28, 0xca, 0xaf, 0xa8, + 0xd4, 0x2f, 0xcb, 0x49, 0x2d, 0x2e, 0xd6, 0x4f, 0xcd, 0x4b, 0xce, 0x4f, 0xc9, 0xcc, 0x4b, 0xd7, + 0x4f, 0x4c, 0x49, 0xc9, 0xcf, 0x2b, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x87, 0xe9, + 0x28, 0x4a, 0xd5, 0x03, 0xab, 0xd6, 0x03, 0xab, 0xd6, 0x83, 0xa9, 0x56, 0x72, 0xe3, 0x62, 0x73, + 0x04, 0x6b, 0x10, 0x92, 0xe1, 0xe2, 0x0c, 0x4e, 0xce, 0x48, 0x4d, 0x29, 0xcd, 0x49, 0x2d, 0x92, + 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x42, 0x08, 0x08, 0xc9, 0x71, 0x71, 0xc1, 0x39, 0x61, 0x12, + 0x4c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, 0x48, 0x22, 0x4e, 0xc9, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, + 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x8c, 0xc7, 0x72, 0x0c, 0x5c, 0xca, 0xc9, 0xf9, 0xb9, + 0x7a, 0x04, 0xac, 0x0f, 0x60, 0x8c, 0xe2, 0x80, 0xb1, 0x57, 0x31, 0xc9, 0x87, 0x19, 0x05, 0x25, + 0x56, 0xea, 0x39, 0x83, 0x54, 0x07, 0x80, 0x55, 0x87, 0x81, 0x55, 0xbb, 0x42, 0x55, 0x24, 0xb1, + 0x81, 0x3d, 0x65, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xb6, 0x78, 0xd4, 0xe2, 0x08, 0x01, 0x00, + 0x00, +} + +func (m *Addons) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Addons) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Addons) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.SchedulerV) > 0 { + i -= len(m.SchedulerV) + copy(dAtA[i:], m.SchedulerV) + i = encodeVarintAddons(dAtA, i, uint64(len(m.SchedulerV))) + i-- + dAtA[i] = 0x12 + } + if len(m.Scheduler) > 0 { + i -= len(m.Scheduler) + copy(dAtA[i:], m.Scheduler) + i = encodeVarintAddons(dAtA, i, uint64(len(m.Scheduler))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintAddons(dAtA []byte, offset int, v uint64) int { + offset -= sovAddons(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Addons) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Scheduler) + if l > 0 { + n += 1 + l + sovAddons(uint64(l)) + } + l = len(m.SchedulerV) + if l > 0 { + n += 1 + l + sovAddons(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovAddons(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozAddons(x uint64) (n int) { + return sovAddons(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Addons) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAddons + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Addons: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Addons: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Scheduler", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAddons + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAddons + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAddons + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Scheduler = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SchedulerV", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAddons + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAddons + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthAddons + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SchedulerV = append(m.SchedulerV[:0], dAtA[iNdEx:postIndex]...) + if m.SchedulerV == nil { + m.SchedulerV = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAddons(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAddons + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthAddons + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAddons(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAddons + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAddons + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAddons + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthAddons + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupAddons + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthAddons + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthAddons = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAddons = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupAddons = fmt.Errorf("proto: unexpected end of group") +) diff --git a/proxy/vless/encoding/addons.proto b/proxy/vless/encoding/addons.proto new file mode 100644 index 00000000..39ae2503 --- /dev/null +++ b/proxy/vless/encoding/addons.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package v2ray.core.proxy.vless.encoding; +option csharp_namespace = "V2Ray.Core.Proxy.Vless.Encoding"; +option go_package = "encoding"; +option java_package = "com.v2ray.core.proxy.vless.encoding"; +option java_multiple_files = true; + +message Addons { + string Scheduler = 1; + bytes SchedulerV = 2; +} diff --git a/proxy/vless/encoding/encoding.go b/proxy/vless/encoding/encoding.go new file mode 100644 index 00000000..1a238397 --- /dev/null +++ b/proxy/vless/encoding/encoding.go @@ -0,0 +1,173 @@ +package encoding + +import ( + "io" + + "v2ray.com/core/common/buf" + "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" + "v2ray.com/core/proxy/vless" +) + +//go:generate errorgen + +const ( + Version = byte(0) +) + +var addrParser = protocol.NewAddressParser( + protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4), + protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain), + protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv6), net.AddressFamilyIPv6), + protocol.PortThenAddress(), +) + +// EncodeRequestHeader writes encoded request header into the given writer. +func EncodeRequestHeader(writer io.Writer, request *protocol.RequestHeader, requestAddons *Addons) error { + + buffer := buf.StackNew() + defer buffer.Release() + + if err := buffer.WriteByte(request.Version); err != nil { + return newError("failed to write request version").Base(err) + } + + if _, err := buffer.Write(request.User.Account.(*vless.MemoryAccount).ID.Bytes()); err != nil { + return newError("failed to write request user id").Base(err) + } + + if err := EncodeHeaderAddons(&buffer, requestAddons); err != nil { + return newError("failed to encode request header addons").Base(err) + } + + if err := buffer.WriteByte(byte(request.Command)); err != nil { + return newError("failed to write request command").Base(err) + } + + if request.Command != protocol.RequestCommandMux { + if err := addrParser.WriteAddressPort(&buffer, request.Address, request.Port); err != nil { + return newError("failed to write request address and port").Base(err) + } + } + + if _, err := writer.Write(buffer.Bytes()); err != nil { + return newError("failed to write request header").Base(err) + } + + return nil +} + +// DecodeRequestHeader decodes and returns (if successful) a RequestHeader from an input stream. +func DecodeRequestHeader(reader io.Reader, validator *vless.Validator) (*protocol.RequestHeader, *Addons, error, *buf.Buffer) { + + buffer := buf.StackNew() + defer buffer.Release() + + pre := buf.New() + + if _, err := buffer.ReadFullFrom(reader, 1); err != nil { + pre.Write(buffer.Bytes()) + return nil, nil, newError("failed to read request version").Base(err), pre + } + + request := &protocol.RequestHeader{ + Version: buffer.Byte(0), + } + + pre.Write(buffer.Bytes()) + + switch request.Version { + case 0: + + buffer.Clear() + if _, err := buffer.ReadFullFrom(reader, protocol.IDBytesLen); err != nil { + pre.Write(buffer.Bytes()) + return nil, nil, newError("failed to read request user id").Base(err), pre + } + + var id [16]byte + copy(id[:], buffer.Bytes()) + + if request.User = validator.Get(id); request.User == nil { + pre.Write(buffer.Bytes()) + return nil, nil, newError("invalid request user id"), pre + } + + requestAddons, err := DecodeHeaderAddons(&buffer, reader) + if err != nil { + return nil, nil, newError("failed to decode request header addons").Base(err), nil + } + + buffer.Clear() + if _, err := buffer.ReadFullFrom(reader, 1); err != nil { + return nil, nil, newError("failed to read request command").Base(err), nil + } + + request.Command = protocol.RequestCommand(buffer.Byte(0)) + switch request.Command { + case protocol.RequestCommandMux: + request.Address = net.DomainAddress("v1.mux.cool") + request.Port = 0 + case protocol.RequestCommandTCP, protocol.RequestCommandUDP: + if addr, port, err := addrParser.ReadAddressPort(&buffer, reader); err == nil { + request.Address = addr + request.Port = port + } + } + + if request.Address == nil { + return nil, nil, newError("invalid request address"), nil + } + + return request, requestAddons, nil, nil + + default: + + return nil, nil, newError("invalid request version"), pre + + } + +} + +// EncodeResponseHeader writes encoded response header into the given writer. +func EncodeResponseHeader(writer io.Writer, request *protocol.RequestHeader, responseAddons *Addons) error { + + buffer := buf.StackNew() + defer buffer.Release() + + if err := buffer.WriteByte(request.Version); err != nil { + return newError("failed to write response version").Base(err) + } + + if err := EncodeHeaderAddons(&buffer, responseAddons); err != nil { + return newError("failed to encode response header addons").Base(err) + } + + if _, err := writer.Write(buffer.Bytes()); err != nil { + return newError("failed to write response header").Base(err) + } + + return nil +} + +// DecodeResponseHeader decodes and returns (if successful) a ResponseHeader from an input stream. +func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader, responseAddons *Addons) error { + + buffer := buf.StackNew() + defer buffer.Release() + + if _, err := buffer.ReadFullFrom(reader, 1); err != nil { + return newError("failed to read response version").Base(err) + } + + if buffer.Byte(0) != request.Version { + return newError("unexpected response version. Expecting ", int(request.Version), " but actually ", int(buffer.Byte(0))) + } + + responseAddons, err := DecodeHeaderAddons(&buffer, reader) + if err != nil { + return newError("failed to decode response header addons").Base(err) + } + + return nil +} diff --git a/proxy/vless/encoding/encoding_test.go b/proxy/vless/encoding/encoding_test.go new file mode 100644 index 00000000..1ca2cd08 --- /dev/null +++ b/proxy/vless/encoding/encoding_test.go @@ -0,0 +1,126 @@ +package encoding_test + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + + "v2ray.com/core/common" + "v2ray.com/core/common/buf" + "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" + "v2ray.com/core/common/uuid" + "v2ray.com/core/proxy/vless" + . "v2ray.com/core/proxy/vless/encoding" +) + +func toAccount(a *vless.Account) protocol.Account { + account, err := a.AsAccount() + common.Must(err) + return account +} + +func TestRequestSerialization(t *testing.T) { + user := &protocol.MemoryUser{ + Level: 0, + Email: "test@v2fly.org", + } + id := uuid.New() + account := &vless.Account{ + Id: id.String(), + } + user.Account = toAccount(account) + + expectedRequest := &protocol.RequestHeader{ + Version: Version, + User: user, + Command: protocol.RequestCommandTCP, + Address: net.DomainAddress("www.v2fly.org"), + Port: net.Port(443), + } + expectedAddons := &Addons{} + + buffer := buf.StackNew() + common.Must(EncodeRequestHeader(&buffer, expectedRequest, expectedAddons)) + + Validator := new(vless.Validator) + Validator.Add(user) + + actualRequest, actualAddons, err, _ := DecodeRequestHeader(&buffer, Validator) + common.Must(err) + + if r := cmp.Diff(actualRequest, expectedRequest, cmp.AllowUnexported(protocol.ID{})); r != "" { + t.Error(r) + } + if r := cmp.Diff(actualAddons, expectedAddons); r != "" { + t.Error(r) + } +} + +func TestInvalidRequest(t *testing.T) { + user := &protocol.MemoryUser{ + Level: 0, + Email: "test@v2fly.org", + } + id := uuid.New() + account := &vless.Account{ + Id: id.String(), + } + user.Account = toAccount(account) + + expectedRequest := &protocol.RequestHeader{ + Version: Version, + User: user, + Command: protocol.RequestCommand(100), + Address: net.DomainAddress("www.v2fly.org"), + Port: net.Port(443), + } + expectedAddons := &Addons{} + + buffer := buf.StackNew() + common.Must(EncodeRequestHeader(&buffer, expectedRequest, expectedAddons)) + + Validator := new(vless.Validator) + Validator.Add(user) + + _, _, err, _ := DecodeRequestHeader(&buffer, Validator) + if err == nil { + t.Error("nil error") + } +} + +func TestMuxRequest(t *testing.T) { + user := &protocol.MemoryUser{ + Level: 0, + Email: "test@v2fly.org", + } + id := uuid.New() + account := &vless.Account{ + Id: id.String(), + } + user.Account = toAccount(account) + + expectedRequest := &protocol.RequestHeader{ + Version: Version, + User: user, + Command: protocol.RequestCommandMux, + Address: net.DomainAddress("v1.mux.cool"), + } + expectedAddons := &Addons{} + + buffer := buf.StackNew() + common.Must(EncodeRequestHeader(&buffer, expectedRequest, expectedAddons)) + + Validator := new(vless.Validator) + Validator.Add(user) + + actualRequest, actualAddons, err, _ := DecodeRequestHeader(&buffer, Validator) + common.Must(err) + + if r := cmp.Diff(actualRequest, expectedRequest, cmp.AllowUnexported(protocol.ID{})); r != "" { + t.Error(r) + } + if r := cmp.Diff(actualAddons, expectedAddons); r != "" { + t.Error(r) + } +} diff --git a/proxy/vless/encoding/errors.generated.go b/proxy/vless/encoding/errors.generated.go new file mode 100644 index 00000000..6fb68210 --- /dev/null +++ b/proxy/vless/encoding/errors.generated.go @@ -0,0 +1,9 @@ +package encoding + +import "v2ray.com/core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/vless/errors.generated.go b/proxy/vless/errors.generated.go new file mode 100644 index 00000000..79734c10 --- /dev/null +++ b/proxy/vless/errors.generated.go @@ -0,0 +1,9 @@ +package vless + +import "v2ray.com/core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/vless/inbound/config.go b/proxy/vless/inbound/config.go new file mode 100644 index 00000000..039be433 --- /dev/null +++ b/proxy/vless/inbound/config.go @@ -0,0 +1,3 @@ +// +build !confonly + +package inbound diff --git a/proxy/vless/inbound/config.pb.go b/proxy/vless/inbound/config.pb.go new file mode 100644 index 00000000..bb8f5c41 --- /dev/null +++ b/proxy/vless/inbound/config.pb.go @@ -0,0 +1,391 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0 +// protoc v3.12.3 +// source: v2ray.com/core/proxy/vless/inbound/config.proto + +package inbound + +import ( + proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + net "v2ray.com/core/common/net" + protocol "v2ray.com/core/common/protocol" +) + +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) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type Fallback struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Addr *net.IPOrDomain `protobuf:"bytes,1,opt,name=addr,proto3" json:"addr,omitempty"` + Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` + Unix string `protobuf:"bytes,3,opt,name=unix,proto3" json:"unix,omitempty"` + Xver uint32 `protobuf:"varint,4,opt,name=xver,proto3" json:"xver,omitempty"` +} + +func (x *Fallback) Reset() { + *x = Fallback{} + if protoimpl.UnsafeEnabled { + mi := &file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Fallback) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Fallback) ProtoMessage() {} + +func (x *Fallback) ProtoReflect() protoreflect.Message { + mi := &file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Fallback.ProtoReflect.Descriptor instead. +func (*Fallback) Descriptor() ([]byte, []int) { + return file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Fallback) GetAddr() *net.IPOrDomain { + if x != nil { + return x.Addr + } + return nil +} + +func (x *Fallback) GetPort() uint32 { + if x != nil { + return x.Port + } + return 0 +} + +func (x *Fallback) GetUnix() string { + if x != nil { + return x.Unix + } + return "" +} + +func (x *Fallback) GetXver() uint32 { + if x != nil { + return x.Xver + } + return 0 +} + +type FallbackH2 struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Addr *net.IPOrDomain `protobuf:"bytes,1,opt,name=addr,proto3" json:"addr,omitempty"` + Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` + Unix string `protobuf:"bytes,3,opt,name=unix,proto3" json:"unix,omitempty"` + Xver uint32 `protobuf:"varint,4,opt,name=xver,proto3" json:"xver,omitempty"` +} + +func (x *FallbackH2) Reset() { + *x = FallbackH2{} + if protoimpl.UnsafeEnabled { + mi := &file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FallbackH2) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FallbackH2) ProtoMessage() {} + +func (x *FallbackH2) ProtoReflect() protoreflect.Message { + mi := &file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FallbackH2.ProtoReflect.Descriptor instead. +func (*FallbackH2) Descriptor() ([]byte, []int) { + return file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescGZIP(), []int{1} +} + +func (x *FallbackH2) GetAddr() *net.IPOrDomain { + if x != nil { + return x.Addr + } + return nil +} + +func (x *FallbackH2) GetPort() uint32 { + if x != nil { + return x.Port + } + return 0 +} + +func (x *FallbackH2) GetUnix() string { + if x != nil { + return x.Unix + } + return "" +} + +func (x *FallbackH2) GetXver() uint32 { + if x != nil { + return x.Xver + } + return 0 +} + +type Config struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + User []*protocol.User `protobuf:"bytes,1,rep,name=user,proto3" json:"user,omitempty"` + // Decryption settings. Only applies to server side, and only accepts "none" for now. + Decryption string `protobuf:"bytes,2,opt,name=decryption,proto3" json:"decryption,omitempty"` + Fallback *Fallback `protobuf:"bytes,3,opt,name=fallback,proto3" json:"fallback,omitempty"` + FallbackH2 *FallbackH2 `protobuf:"bytes,4,opt,name=fallback_h2,json=fallbackH2,proto3" json:"fallback_h2,omitempty"` +} + +func (x *Config) Reset() { + *x = Config{} + if protoimpl.UnsafeEnabled { + mi := &file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes[2] + 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_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && 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_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescGZIP(), []int{2} +} + +func (x *Config) GetUser() []*protocol.User { + if x != nil { + return x.User + } + return nil +} + +func (x *Config) GetDecryption() string { + if x != nil { + return x.Decryption + } + return "" +} + +func (x *Config) GetFallback() *Fallback { + if x != nil { + return x.Fallback + } + return nil +} + +func (x *Config) GetFallbackH2() *FallbackH2 { + if x != nil { + return x.FallbackH2 + } + return nil +} + +var File_v2ray_com_core_proxy_vless_inbound_config_proto protoreflect.FileDescriptor + +var file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDesc = []byte{ + 0x0a, 0x2f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x69, 0x6e, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x1e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, + 0x64, 0x1a, 0x27, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, + 0x65, 0x2f, 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, 0x29, 0x76, 0x32, 0x72, 0x61, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, + 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7d, 0x0a, 0x08, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x12, 0x35, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x21, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, 0x6f, 0x6d, 0x61, + 0x69, 0x6e, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x12, 0x0a, 0x04, + 0x75, 0x6e, 0x69, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, 0x6e, 0x69, 0x78, + 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, + 0x78, 0x76, 0x65, 0x72, 0x22, 0x80, 0x01, 0x0a, 0x0b, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, + 0x6b, 0x5f, 0x68, 0x32, 0x12, 0x35, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x49, 0x50, 0x4f, 0x72, 0x44, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x70, + 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x75, 0x6e, 0x69, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x75, + 0x6e, 0x69, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x78, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x04, 0x78, 0x76, 0x65, 0x72, 0x22, 0xf2, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x34, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x20, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x55, 0x73, + 0x65, 0x72, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x65, 0x63, 0x72, + 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x64, 0x65, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x08, 0x66, 0x61, 0x6c, 0x6c, + 0x62, 0x61, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x76, 0x32, 0x72, + 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, + 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x46, 0x61, 0x6c, 0x6c, + 0x62, 0x61, 0x63, 0x6b, 0x52, 0x08, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x12, 0x4c, + 0x0a, 0x0b, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x68, 0x32, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, + 0x6f, 0x75, 0x6e, 0x64, 0x2e, 0x46, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x68, 0x32, + 0x52, 0x0a, 0x66, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x48, 0x32, 0x42, 0x50, 0x0a, 0x22, + 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6e, 0x62, 0x6f, 0x75, + 0x6e, 0x64, 0x50, 0x01, 0x5a, 0x07, 0x69, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, 0x1e, + 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, + 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x49, 0x6e, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescOnce sync.Once + file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescData = file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDesc +) + +func file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescGZIP() []byte { + file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescOnce.Do(func() { + file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescData) + }) + return file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDescData +} + +var file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_v2ray_com_core_proxy_vless_inbound_config_proto_goTypes = []interface{}{ + (*Fallback)(nil), // 0: v2ray.core.proxy.vless.inbound.Fallback + (*FallbackH2)(nil), // 1: v2ray.core.proxy.vless.inbound.Fallback_h2 + (*Config)(nil), // 2: v2ray.core.proxy.vless.inbound.Config + (*net.IPOrDomain)(nil), // 3: v2ray.core.common.net.IPOrDomain + (*protocol.User)(nil), // 4: v2ray.core.common.protocol.User +} +var file_v2ray_com_core_proxy_vless_inbound_config_proto_depIdxs = []int32{ + 3, // 0: v2ray.core.proxy.vless.inbound.Fallback.addr:type_name -> v2ray.core.common.net.IPOrDomain + 3, // 1: v2ray.core.proxy.vless.inbound.Fallback_h2.addr:type_name -> v2ray.core.common.net.IPOrDomain + 4, // 2: v2ray.core.proxy.vless.inbound.Config.user:type_name -> v2ray.core.common.protocol.User + 0, // 3: v2ray.core.proxy.vless.inbound.Config.fallback:type_name -> v2ray.core.proxy.vless.inbound.Fallback + 1, // 4: v2ray.core.proxy.vless.inbound.Config.fallback_h2:type_name -> v2ray.core.proxy.vless.inbound.Fallback_h2 + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_v2ray_com_core_proxy_vless_inbound_config_proto_init() } +func file_v2ray_com_core_proxy_vless_inbound_config_proto_init() { + if File_v2ray_com_core_proxy_vless_inbound_config_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Fallback); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FallbackH2); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_v2ray_com_core_proxy_vless_inbound_config_proto_goTypes, + DependencyIndexes: file_v2ray_com_core_proxy_vless_inbound_config_proto_depIdxs, + MessageInfos: file_v2ray_com_core_proxy_vless_inbound_config_proto_msgTypes, + }.Build() + File_v2ray_com_core_proxy_vless_inbound_config_proto = out.File + file_v2ray_com_core_proxy_vless_inbound_config_proto_rawDesc = nil + file_v2ray_com_core_proxy_vless_inbound_config_proto_goTypes = nil + file_v2ray_com_core_proxy_vless_inbound_config_proto_depIdxs = nil +} diff --git a/proxy/vless/inbound/config.proto b/proxy/vless/inbound/config.proto new file mode 100644 index 00000000..1a0bffe8 --- /dev/null +++ b/proxy/vless/inbound/config.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +package v2ray.core.proxy.vless.inbound; +option csharp_namespace = "V2Ray.Core.Proxy.Vless.Inbound"; +option go_package = "inbound"; +option java_package = "com.v2ray.core.proxy.vless.inbound"; +option java_multiple_files = true; + +import "v2ray.com/core/common/net/address.proto"; +import "v2ray.com/core/common/protocol/user.proto"; + +message Fallback { + v2ray.core.common.net.IPOrDomain addr = 1; + uint32 port = 2; + string unix = 3; + uint32 xver = 4; +} + +message Fallback_h2 { + v2ray.core.common.net.IPOrDomain addr = 1; + uint32 port = 2; + string unix = 3; + uint32 xver = 4; +} + +message Config { + repeated v2ray.core.common.protocol.User user = 1; + // Decryption settings. Only applies to server side, and only accepts "none" for now. + string decryption = 2; + Fallback fallback = 3; + Fallback_h2 fallback_h2 = 4; +} diff --git a/proxy/vless/inbound/errors.generated.go b/proxy/vless/inbound/errors.generated.go new file mode 100644 index 00000000..90d805b1 --- /dev/null +++ b/proxy/vless/inbound/errors.generated.go @@ -0,0 +1,9 @@ +package inbound + +import "v2ray.com/core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/vless/inbound/inbound.go b/proxy/vless/inbound/inbound.go new file mode 100644 index 00000000..a19bbdd8 --- /dev/null +++ b/proxy/vless/inbound/inbound.go @@ -0,0 +1,402 @@ +// +build !confonly + +package inbound + +//go:generate errorgen + +import ( + "context" + "encoding/hex" + "io" + "strconv" + "time" + + "v2ray.com/core" + "v2ray.com/core/common" + "v2ray.com/core/common/buf" + "v2ray.com/core/common/errors" + "v2ray.com/core/common/log" + "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" + "v2ray.com/core/common/retry" + "v2ray.com/core/common/session" + "v2ray.com/core/common/signal" + "v2ray.com/core/common/task" + "v2ray.com/core/features/dns" + feature_inbound "v2ray.com/core/features/inbound" + "v2ray.com/core/features/policy" + "v2ray.com/core/features/routing" + "v2ray.com/core/proxy/vless" + "v2ray.com/core/proxy/vless/encoding" + "v2ray.com/core/transport/internet" + "v2ray.com/core/transport/internet/tls" +) + +func init() { + common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + var dc dns.Client + if err := core.RequireFeatures(ctx, func(d dns.Client) error { + dc = d + return nil + }); err != nil { + return nil, err + } + return New(ctx, config.(*Config), dc) + })) +} + +// Handler is an inbound connection handler that handles messages in VLess protocol. +type Handler struct { + inboundHandlerManager feature_inbound.Manager + policyManager policy.Manager + validator *vless.Validator + dns dns.Client + fallback *Fallback // or nil + addrport string + fallback_h2 *FallbackH2 // or nil + addrport_h2 string +} + +// New creates a new VLess inbound handler. +func New(ctx context.Context, config *Config, dc dns.Client) (*Handler, error) { + + v := core.MustFromContext(ctx) + handler := &Handler{ + inboundHandlerManager: v.GetFeature(feature_inbound.ManagerType()).(feature_inbound.Manager), + policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), + validator: new(vless.Validator), + dns: dc, + } + + for _, user := range config.User { + u, err := user.ToMemoryUser() + if err != nil { + return nil, newError("failed to get VLESS user").Base(err).AtError() + } + if err := handler.AddUser(ctx, u); err != nil { + return nil, newError("failed to initiate user").Base(err).AtError() + } + } + + if config.Fallback != nil { + handler.fallback = config.Fallback + handler.addrport = handler.fallback.Addr.AsAddress().String() + ":" + strconv.Itoa(int(handler.fallback.Port)) + } + if config.FallbackH2 != nil { + handler.fallback_h2 = config.FallbackH2 + handler.addrport_h2 = handler.fallback_h2.Addr.AsAddress().String() + ":" + strconv.Itoa(int(handler.fallback_h2.Port)) + } + + return handler, nil +} + +// Close implements common.Closable.Close(). +func (h *Handler) Close() error { + return errors.Combine(common.Close(h.validator)) +} + +// AddUser implements proxy.UserManager.AddUser(). +func (h *Handler) AddUser(ctx context.Context, u *protocol.MemoryUser) error { + return h.validator.Add(u) +} + +// RemoveUser implements proxy.UserManager.RemoveUser(). +func (h *Handler) RemoveUser(ctx context.Context, e string) error { + return h.validator.Del(e) +} + +// Network implements proxy.Inbound.Network(). +func (*Handler) Network() []net.Network { + return []net.Network{net.Network_TCP} +} + +// Process implements proxy.Inbound.Process(). +func (h *Handler) Process(ctx context.Context, network net.Network, connection internet.Connection, dispatcher routing.Dispatcher) error { + + sessionPolicy := h.policyManager.ForLevel(0) + if err := connection.SetReadDeadline(time.Now().Add(sessionPolicy.Timeouts.Handshake)); err != nil { + return newError("unable to set read deadline").Base(err).AtWarning() + } + + first := buf.New() + first.ReadFrom(connection) + + sid := session.ExportIDToError(ctx) + newError("firstLen = ", first.Len()).AtInfo().WriteToLog(sid) + + reader := &buf.BufferedReader{ + Reader: buf.NewReader(connection), + Buffer: buf.MultiBuffer{first}, + } + + var request *protocol.RequestHeader + var requestAddons *encoding.Addons + var err error + var pre *buf.Buffer + + fallback := 0 + if h.fallback != nil { + fallback = 1 + } + + if fallback == 1 && first.Len() < 18 { + err = newError("fallback directly") + } else { + request, requestAddons, err, pre = encoding.DecodeRequestHeader(reader, h.validator) + if pre == nil { + fallback = 0 + } + } + + if err != nil { + + if fallback == 1 { + if h.fallback_h2 != nil { + iConn := connection + if statConn, ok := iConn.(*internet.StatCouterConnection); ok { + iConn = statConn.Connection + } + if tlsConn, ok := iConn.(*tls.Conn); ok { + if tlsConn.ConnectionState().NegotiatedProtocol == "h2" { + fallback = 2 + } + } + } + + var addrport string + var unixpath string + var proxyver uint32 + + switch fallback { + case 1: + addrport = h.addrport + unixpath = h.fallback.Unix + proxyver = h.fallback.Xver + newError("fallback starts").Base(err).AtInfo().WriteToLog(sid) + case 2: + addrport = h.addrport_h2 + unixpath = h.fallback_h2.Unix + proxyver = h.fallback_h2.Xver + newError("fallback_h2 starts").Base(err).AtInfo().WriteToLog(sid) + } + + var conn net.Conn + if err := retry.ExponentialBackoff(5, 100).On(func() error { + var dialer net.Dialer + var err error + if unixpath != "" { + conn, err = dialer.DialContext(ctx, "unix", unixpath) + } else { + conn, err = dialer.DialContext(ctx, "tcp", addrport) + } + if err != nil { + return err + } + return nil + }); err != nil { + return newError("failed to fallback connection").Base(err).AtWarning() + } + defer conn.Close() // nolint: errcheck + + ctx, cancel := context.WithCancel(ctx) + timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) + + writer := buf.NewWriter(connection) + + serverReader := buf.NewReader(conn) + serverWriter := buf.NewWriter(conn) + + postRequest := func() error { + defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) + if proxyver > 0 { + remoteAddr, remotePort, err := net.SplitHostPort(connection.RemoteAddr().String()) + if err != nil { + return err + } + localAddr, localPort, err := net.SplitHostPort(connection.LocalAddr().String()) + if err != nil { + return err + } + ipv4 := true + for i := 0; i < len(remoteAddr); i++ { + if remoteAddr[i] == ':' { + ipv4 = false + break + } + } + pro := buf.New() + switch proxyver { + case 1: + if ipv4 { + pro.Write([]byte("PROXY TCP4 " + remoteAddr + " " + localAddr + " " + remotePort + " " + localPort + "\r\n")) + } else { + pro.Write([]byte("PROXY TCP6 " + remoteAddr + " " + localAddr + " " + remotePort + " " + localPort + "\r\n")) + } + case 2: + pro.Write([]byte("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A\x21")) // signature + v2 + PROXY + if ipv4 { + pro.Write([]byte("\x11\x00\x0C")) // AF_INET + STREAM + 12 bytes + pro.Write(net.ParseIP(remoteAddr).To4()) + pro.Write(net.ParseIP(localAddr).To4()) + } else { + pro.Write([]byte("\x21\x00\x24")) // AF_INET6 + STREAM + 36 bytes + pro.Write(net.ParseIP(remoteAddr).To16()) + pro.Write(net.ParseIP(localAddr).To16()) + } + p1, _ := strconv.ParseInt(remotePort, 10, 64) + b1, _ := hex.DecodeString(strconv.FormatInt(p1, 16)) + p2, _ := strconv.ParseInt(localPort, 10, 64) + b2, _ := hex.DecodeString(strconv.FormatInt(p2, 16)) + if len(b1) == 1 { + pro.WriteByte(0) + } + pro.Write(b1) + if len(b2) == 1 { + pro.WriteByte(0) + } + pro.Write(b2) + } + if err := serverWriter.WriteMultiBuffer(buf.MultiBuffer{pro}); err != nil { + return newError("failed to set PROXY protocol v", proxyver).Base(err).AtWarning() + } + } + if pre != nil && pre.Len() > 0 { + if err := serverWriter.WriteMultiBuffer(buf.MultiBuffer{pre}); err != nil { + return newError("failed to fallback request pre").Base(err).AtWarning() + } + } + if err := buf.Copy(reader, serverWriter, buf.UpdateActivity(timer)); err != nil { + return err // ... + } + return nil + } + + getResponse := func() error { + defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) + if err := buf.Copy(serverReader, writer, buf.UpdateActivity(timer)); err != nil { + return err // ... + } + return nil + } + + if err := task.Run(ctx, task.OnSuccess(postRequest, task.Close(serverWriter)), task.OnSuccess(getResponse, task.Close(writer))); err != nil { + common.Interrupt(serverReader) + common.Interrupt(serverWriter) + return newError("fallback ends").Base(err).AtInfo() + } + return nil + } + + if errors.Cause(err) != io.EOF { + log.Record(&log.AccessMessage{ + From: connection.RemoteAddr(), + To: "", + Status: log.AccessRejected, + Reason: err, + }) + err = newError("invalid request from ", connection.RemoteAddr()).Base(err).AtWarning() + } + return err + } + + if request.Command != protocol.RequestCommandMux { + ctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{ + From: connection.RemoteAddr(), + To: request.Destination(), + Status: log.AccessAccepted, + Reason: "", + Email: request.User.Email, + }) + } + + newError("received request for ", request.Destination()).AtInfo().WriteToLog(sid) + + if err := connection.SetReadDeadline(time.Time{}); err != nil { + newError("unable to set back read deadline").Base(err).AtWarning().WriteToLog(sid) + } + + inbound := session.InboundFromContext(ctx) + if inbound == nil { + panic("no inbound metadata") + } + inbound.User = request.User + + sessionPolicy = h.policyManager.ForLevel(request.User.Level) + + ctx, cancel := context.WithCancel(ctx) + timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) + + ctx = policy.ContextWithBufferPolicy(ctx, sessionPolicy.Buffer) + link, err := dispatcher.Dispatch(ctx, request.Destination()) + if err != nil { + return newError("failed to dispatch request to ", request.Destination()).Base(err).AtWarning() + } + + serverReader := link.Reader + serverWriter := link.Writer + + postRequest := func() error { + defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) + + // default: clientReader := reader + clientReader := encoding.DecodeBodyAddons(reader, request, requestAddons) + + // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer + if err := buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)); err != nil { + return newError("failed to transfer request payload").Base(err).AtInfo() + } + + return nil + } + + getResponse := func() error { + defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) + + responseAddons := &encoding.Addons{ + Scheduler: requestAddons.Scheduler, + } + + bufferWriter := buf.NewBufferedWriter(buf.NewWriter(connection)) + if err := encoding.EncodeResponseHeader(bufferWriter, request, responseAddons); err != nil { + return newError("failed to encode response header").Base(err).AtWarning() + } + + // default: clientWriter := bufferWriter + clientWriter := encoding.EncodeBodyAddons(bufferWriter, request, responseAddons) + { + multiBuffer, err := serverReader.ReadMultiBuffer() + if err != nil { + return err // ... + } + if err := clientWriter.WriteMultiBuffer(multiBuffer); err != nil { + return err // ... + } + } + + // Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer + if err := bufferWriter.SetBuffered(false); err != nil { + return newError("failed to write A response payload").Base(err).AtWarning() + } + + // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer + if err := buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)); err != nil { + return newError("failed to transfer response payload").Base(err).AtInfo() + } + + // Indicates the end of response payload. + switch responseAddons.Scheduler { + default: + + } + + return nil + } + + if err := task.Run(ctx, task.OnSuccess(postRequest, task.Close(serverWriter)), getResponse); err != nil { + common.Interrupt(serverReader) + common.Interrupt(serverWriter) + return newError("connection ends").Base(err).AtInfo() + } + + return nil +} diff --git a/proxy/vless/outbound/config.go b/proxy/vless/outbound/config.go new file mode 100644 index 00000000..35bf561b --- /dev/null +++ b/proxy/vless/outbound/config.go @@ -0,0 +1,3 @@ +// +build !confonly + +package outbound diff --git a/proxy/vless/outbound/config.pb.go b/proxy/vless/outbound/config.pb.go new file mode 100644 index 00000000..cdcc3f82 --- /dev/null +++ b/proxy/vless/outbound/config.pb.go @@ -0,0 +1,164 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.25.0 +// protoc v3.12.3 +// source: v2ray.com/core/proxy/vless/outbound/config.proto + +package outbound + +import ( + proto "github.com/golang/protobuf/proto" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + protocol "v2ray.com/core/common/protocol" +) + +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) +) + +// This is a compile-time assertion that a sufficiently up-to-date version +// of the legacy proto package is being used. +const _ = proto.ProtoPackageIsVersion4 + +type Config struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Receiver []*protocol.ServerEndpoint `protobuf:"bytes,1,rep,name=receiver,proto3" json:"receiver,omitempty"` +} + +func (x *Config) Reset() { + *x = Config{} + if protoimpl.UnsafeEnabled { + mi := &file_v2ray_com_core_proxy_vless_outbound_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_v2ray_com_core_proxy_vless_outbound_config_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && 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_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescGZIP(), []int{0} +} + +func (x *Config) GetReceiver() []*protocol.ServerEndpoint { + if x != nil { + return x.Receiver + } + return nil +} + +var File_v2ray_com_core_proxy_vless_outbound_config_proto protoreflect.FileDescriptor + +var file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDesc = []byte{ + 0x0a, 0x30, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2f, 0x6f, 0x75, 0x74, + 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x1f, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x6f, 0x75, 0x74, 0x62, 0x6f, + 0x75, 0x6e, 0x64, 0x1a, 0x30, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, + 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x63, 0x6f, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x70, 0x65, 0x63, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x50, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x46, 0x0a, 0x08, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2a, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x63, + 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e, 0x53, + 0x65, 0x72, 0x76, 0x65, 0x72, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x72, + 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72, 0x42, 0x53, 0x0a, 0x23, 0x63, 0x6f, 0x6d, 0x2e, 0x76, + 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, + 0x76, 0x6c, 0x65, 0x73, 0x73, 0x2e, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x50, 0x01, + 0x5a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0xaa, 0x02, 0x1f, 0x56, 0x32, 0x52, + 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x56, 0x6c, + 0x65, 0x73, 0x73, 0x2e, 0x4f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescOnce sync.Once + file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescData = file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDesc +) + +func file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescGZIP() []byte { + file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescOnce.Do(func() { + file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescData) + }) + return file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDescData +} + +var file_v2ray_com_core_proxy_vless_outbound_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_v2ray_com_core_proxy_vless_outbound_config_proto_goTypes = []interface{}{ + (*Config)(nil), // 0: v2ray.core.proxy.vless.outbound.Config + (*protocol.ServerEndpoint)(nil), // 1: v2ray.core.common.protocol.ServerEndpoint +} +var file_v2ray_com_core_proxy_vless_outbound_config_proto_depIdxs = []int32{ + 1, // 0: v2ray.core.proxy.vless.outbound.Config.receiver:type_name -> v2ray.core.common.protocol.ServerEndpoint + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_v2ray_com_core_proxy_vless_outbound_config_proto_init() } +func file_v2ray_com_core_proxy_vless_outbound_config_proto_init() { + if File_v2ray_com_core_proxy_vless_outbound_config_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_v2ray_com_core_proxy_vless_outbound_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Config); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_v2ray_com_core_proxy_vless_outbound_config_proto_goTypes, + DependencyIndexes: file_v2ray_com_core_proxy_vless_outbound_config_proto_depIdxs, + MessageInfos: file_v2ray_com_core_proxy_vless_outbound_config_proto_msgTypes, + }.Build() + File_v2ray_com_core_proxy_vless_outbound_config_proto = out.File + file_v2ray_com_core_proxy_vless_outbound_config_proto_rawDesc = nil + file_v2ray_com_core_proxy_vless_outbound_config_proto_goTypes = nil + file_v2ray_com_core_proxy_vless_outbound_config_proto_depIdxs = nil +} diff --git a/proxy/vless/outbound/config.proto b/proxy/vless/outbound/config.proto new file mode 100644 index 00000000..732addf0 --- /dev/null +++ b/proxy/vless/outbound/config.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package v2ray.core.proxy.vless.outbound; +option csharp_namespace = "V2Ray.Core.Proxy.Vless.Outbound"; +option go_package = "outbound"; +option java_package = "com.v2ray.core.proxy.vless.outbound"; +option java_multiple_files = true; + +import "v2ray.com/core/common/protocol/server_spec.proto"; + +message Config { + repeated v2ray.core.common.protocol.ServerEndpoint receiver = 1; +} diff --git a/proxy/vless/outbound/errors.generated.go b/proxy/vless/outbound/errors.generated.go new file mode 100644 index 00000000..37e984d8 --- /dev/null +++ b/proxy/vless/outbound/errors.generated.go @@ -0,0 +1,9 @@ +package outbound + +import "v2ray.com/core/common/errors" + +type errPathObjHolder struct{} + +func newError(values ...interface{}) *errors.Error { + return errors.New(values...).WithPathObj(errPathObjHolder{}) +} diff --git a/proxy/vless/outbound/outbound.go b/proxy/vless/outbound/outbound.go new file mode 100644 index 00000000..039e4fc6 --- /dev/null +++ b/proxy/vless/outbound/outbound.go @@ -0,0 +1,177 @@ +// +build !confonly + +package outbound + +//go:generate errorgen + +import ( + "context" + "time" + + "v2ray.com/core" + "v2ray.com/core/common" + "v2ray.com/core/common/buf" + "v2ray.com/core/common/net" + "v2ray.com/core/common/protocol" + "v2ray.com/core/common/retry" + "v2ray.com/core/common/session" + "v2ray.com/core/common/signal" + "v2ray.com/core/common/task" + "v2ray.com/core/features/policy" + "v2ray.com/core/proxy/vless" + "v2ray.com/core/proxy/vless/encoding" + "v2ray.com/core/transport" + "v2ray.com/core/transport/internet" +) + +func init() { + common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) { + return New(ctx, config.(*Config)) + })) +} + +// Handler is an outbound connection handler for VLess protocol. +type Handler struct { + serverList *protocol.ServerList + serverPicker protocol.ServerPicker + policyManager policy.Manager +} + +// New creates a new VLess outbound handler. +func New(ctx context.Context, config *Config) (*Handler, error) { + + serverList := protocol.NewServerList() + for _, rec := range config.Receiver { + s, err := protocol.NewServerSpecFromPB(*rec) + if err != nil { + return nil, newError("failed to parse server spec").Base(err).AtError() + } + serverList.AddServer(s) + } + + v := core.MustFromContext(ctx) + handler := &Handler{ + serverList: serverList, + serverPicker: protocol.NewRoundRobinServerPicker(serverList), + policyManager: v.GetFeature(policy.ManagerType()).(policy.Manager), + } + + return handler, nil +} + +// Process implements proxy.Outbound.Process(). +func (v *Handler) Process(ctx context.Context, link *transport.Link, dialer internet.Dialer) error { + + var rec *protocol.ServerSpec + var conn internet.Connection + + if err := retry.ExponentialBackoff(5, 200).On(func() error { + rec = v.serverPicker.PickServer() + var err error + conn, err = dialer.Dial(ctx, rec.Destination()) + if err != nil { + return err + } + return nil + }); err != nil { + return newError("failed to find an available destination").Base(err).AtWarning() + } + defer conn.Close() // nolint: errcheck + + outbound := session.OutboundFromContext(ctx) + if outbound == nil || !outbound.Target.IsValid() { + return newError("target not specified").AtError() + } + + target := outbound.Target + newError("tunneling request to ", target, " via ", rec.Destination()).AtInfo().WriteToLog(session.ExportIDToError(ctx)) + + command := protocol.RequestCommandTCP + if target.Network == net.Network_UDP { + command = protocol.RequestCommandUDP + } + if target.Address.Family().IsDomain() && target.Address.Domain() == "v1.mux.cool" { + command = protocol.RequestCommandMux + } + + request := &protocol.RequestHeader{ + Version: encoding.Version, + User: rec.PickUser(), + Command: command, + Address: target.Address, + Port: target.Port, + } + + account := request.User.Account.(*vless.MemoryAccount) + + requestAddons := &encoding.Addons{ + Scheduler: account.Schedulers, + } + + sessionPolicy := v.policyManager.ForLevel(request.User.Level) + + ctx, cancel := context.WithCancel(ctx) + timer := signal.CancelAfterInactivity(ctx, cancel, sessionPolicy.Timeouts.ConnectionIdle) + + clientReader := link.Reader + clientWriter := link.Writer + + postRequest := func() error { + defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) + + bufferWriter := buf.NewBufferedWriter(buf.NewWriter(conn)) + if err := encoding.EncodeRequestHeader(bufferWriter, request, requestAddons); err != nil { + return newError("failed to encode request header").Base(err).AtWarning() + } + + // default: serverWriter := bufferWriter + serverWriter := encoding.EncodeBodyAddons(bufferWriter, request, requestAddons) + if err := buf.CopyOnceTimeout(clientReader, serverWriter, time.Millisecond*100); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout { + return err // ... + } + + // Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer + if err := bufferWriter.SetBuffered(false); err != nil { + return newError("failed to write A request payload").Base(err).AtWarning() + } + + // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer + if err := buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)); err != nil { + return newError("failed to transfer request payload").Base(err).AtInfo() + } + + // Indicates the end of request payload. + switch requestAddons.Scheduler { + default: + + } + + return nil + } + + getResponse := func() error { + defer timer.SetTimeout(sessionPolicy.Timeouts.UplinkOnly) + + responseAddons := new(encoding.Addons) + + if err := encoding.DecodeResponseHeader(conn, request, responseAddons); err != nil { + return newError("failed to decode response header").Base(err).AtWarning() + } + + // default: serverReader := buf.NewReader(conn) + serverReader := encoding.DecodeBodyAddons(conn, request, responseAddons) + + // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer + if err := buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)); err != nil { + return newError("failed to transfer response payload").Base(err).AtInfo() + } + + return nil + } + + if err := task.Run(ctx, postRequest, task.OnSuccess(getResponse, task.Close(clientWriter))); err != nil { + return newError("connection ends").Base(err).AtInfo() + } + + return nil +} diff --git a/proxy/vless/validator.go b/proxy/vless/validator.go new file mode 100644 index 00000000..a19cc632 --- /dev/null +++ b/proxy/vless/validator.go @@ -0,0 +1,50 @@ +// +build !confonly + +package vless + +import ( + "strings" + "sync" + + "v2ray.com/core/common/protocol" + "v2ray.com/core/common/uuid" +) + +type Validator struct { + // Considering email's usage here, map + sync.Mutex/RWMutex may have better performance. + email sync.Map + users sync.Map +} + +func (v *Validator) Add(u *protocol.MemoryUser) error { + if u.Email != "" { + _, loaded := v.email.LoadOrStore(strings.ToLower(u.Email), u) + if loaded { + return newError("User ", u.Email, " already exists.") + } + } + v.users.Store(u.Account.(*MemoryAccount).ID.UUID(), u) + return nil +} + +func (v *Validator) Del(e string) error { + if e == "" { + return newError("Email must not be empty.") + } + le := strings.ToLower(e) + u, _ := v.email.Load(le) + if u == nil { + return newError("User ", e, " not found.") + } + v.email.Delete(le) + v.users.Delete(u.(*protocol.MemoryUser).Account.(*MemoryAccount).ID.UUID()) + return nil +} + +func (v *Validator) Get(id uuid.UUID) *protocol.MemoryUser { + u, _ := v.users.Load(id) + if u != nil { + return u.(*protocol.MemoryUser) + } + return nil +} diff --git a/proxy/vless/vless.go b/proxy/vless/vless.go new file mode 100644 index 00000000..9e6dc7ab --- /dev/null +++ b/proxy/vless/vless.go @@ -0,0 +1,8 @@ +// Package vless contains the implementation of VLess protocol and transportation. +// +// VLess contains both inbound and outbound connections. VLess inbound is usually used on servers +// together with 'freedom' to talk to final destination, while VLess outbound is usually used on +// clients with 'socks' for proxying. +package vless + +//go:generate errorgen diff --git a/release/BUILD b/release/BUILD index d1781878..76f729cc 100644 --- a/release/BUILD +++ b/release/BUILD @@ -44,8 +44,8 @@ pkg_zip( "//main:v2ray_darwin_amd64", "//main:v2ray_darwin_amd64_sig", ], - out = "v2ray-macos.zip", - mappings = gen_mappings("darwin", "amd64"), + out = "v2ray-macos-64.zip", + mappings = gen_mappings("darwin", "amd64", "0"), ) pkg_zip( @@ -57,12 +57,12 @@ pkg_zip( "//infra/control/main:v2ctl_windows_amd64", "//infra/control/main:v2ctl_windows_amd64_sig", "//main:v2ray_windows_amd64", + "//main:v2ray_windows_amd64_sig", "//main:v2ray_windows_amd64_nowindow", "//main:v2ray_windows_amd64_nowindow_sig", - "//main:v2ray_windows_amd64_sig", ], out = "v2ray-windows-64.zip", - mappings = gen_mappings("windows", "amd64"), + mappings = gen_mappings("windows", "amd64", "0"), ) pkg_zip( @@ -74,41 +74,29 @@ pkg_zip( "//infra/control/main:v2ctl_windows_386", "//infra/control/main:v2ctl_windows_386_sig", "//main:v2ray_windows_386", + "//main:v2ray_windows_386_sig", "//main:v2ray_windows_386_nowindow", "//main:v2ray_windows_386_nowindow_sig", - "//main:v2ray_windows_386_sig", ], out = "v2ray-windows-32.zip", - mappings = gen_mappings("windows", "386"), + mappings = gen_mappings("windows", "386", "0"), ) pkg_zip( - name = "v2ray_windows_arm_package", + name = "v2ray_windows_armv7_package", srcs = [ ":config_json", ":doc", ":geodata", - "//infra/control/main:v2ctl_windows_arm_armv5", - "//infra/control/main:v2ctl_windows_arm_armv5_sig", - "//infra/control/main:v2ctl_windows_arm_armv6", - "//infra/control/main:v2ctl_windows_arm_armv6_sig", - "//infra/control/main:v2ctl_windows_arm_armv7", - "//infra/control/main:v2ctl_windows_arm_armv7_sig", - "//main:v2ray_windows_arm_armv5", - "//main:v2ray_windows_arm_armv5_sig", - "//main:v2ray_windows_arm_armv5_nowindow", - "//main:v2ray_windows_arm_armv5_nowindow_sig", - "//main:v2ray_windows_arm_armv6", - "//main:v2ray_windows_arm_armv6_sig", - "//main:v2ray_windows_arm_armv6_nowindow", - "//main:v2ray_windows_arm_armv6_nowindow_sig", - "//main:v2ray_windows_arm_armv7", - "//main:v2ray_windows_arm_armv7_sig", - "//main:v2ray_windows_arm_armv7_nowindow", - "//main:v2ray_windows_arm_armv7_nowindow_sig", + "//infra/control/main:v2ctl_windows_arm_7", + "//infra/control/main:v2ctl_windows_arm_7_sig", + "//main:v2ray_windows_arm_7", + "//main:v2ray_windows_arm_7_sig", + "//main:v2ray_windows_arm_7_nowindow", + "//main:v2ray_windows_arm_7_nowindow_sig", ], - out = "v2ray-windows-arm.zip", - mappings = gen_mappings("windows", "arm"), + out = "v2ray-windows-arm32-v7a.zip", + mappings = gen_mappings("windows", "arm", "7"), ) pkg_zip( @@ -123,7 +111,7 @@ pkg_zip( "//main:v2ray_freebsd_amd64_sig", ], out = "v2ray-freebsd-64.zip", - mappings = gen_mappings("freebsd", "amd64"), + mappings = gen_mappings("freebsd", "amd64", "0"), ) pkg_zip( @@ -138,7 +126,7 @@ pkg_zip( "//main:v2ray_freebsd_386_sig", ], out = "v2ray-freebsd-32.zip", - mappings = gen_mappings("freebsd", "386"), + mappings = gen_mappings("freebsd", "386", "0"), ) pkg_zip( @@ -153,7 +141,7 @@ pkg_zip( "//main:v2ray_openbsd_amd64_sig", ], out = "v2ray-openbsd-64.zip", - mappings = gen_mappings("openbsd", "amd64"), + mappings = gen_mappings("openbsd", "amd64", "0"), ) pkg_zip( @@ -168,7 +156,7 @@ pkg_zip( "//main:v2ray_openbsd_386_sig", ], out = "v2ray-openbsd-32.zip", - mappings = gen_mappings("openbsd", "386"), + mappings = gen_mappings("openbsd", "386", "0"), ) pkg_zip( @@ -183,7 +171,7 @@ pkg_zip( "//main:v2ray_dragonfly_amd64_sig", ], out = "v2ray-dragonfly-64.zip", - mappings = gen_mappings("dragonfly", "amd64"), + mappings = gen_mappings("dragonfly", "amd64", "0"), ) pkg_zip( @@ -199,7 +187,7 @@ pkg_zip( "//main:v2ray_linux_amd64_sig", ], out = "v2ray-linux-64.zip", - mappings = gen_mappings("linux", "amd64"), + mappings = gen_mappings("linux", "amd64", "0"), ) pkg_zip( @@ -215,51 +203,75 @@ pkg_zip( "//main:v2ray_linux_386_sig", ], out = "v2ray-linux-32.zip", - mappings = gen_mappings("linux", "386"), + mappings = gen_mappings("linux", "386", "0"), ) pkg_zip( - name = "v2ray_linux_arm_package", + name = "v2ray_linux_arm64_package", srcs = [ ":config_json", ":doc", ":geodata", ":systemd", - "//infra/control/main:v2ctl_linux_arm_armv5", - "//infra/control/main:v2ctl_linux_arm_armv5_sig", - "//infra/control/main:v2ctl_linux_arm_armv6", - "//infra/control/main:v2ctl_linux_arm_armv6_sig", - "//infra/control/main:v2ctl_linux_arm_armv7", - "//infra/control/main:v2ctl_linux_arm_armv7_sig", - "//main:v2ray_linux_arm_armv5", - "//main:v2ray_linux_arm_armv5_sig", - "//main:v2ray_linux_arm_armv6", - "//main:v2ray_linux_arm_armv6_sig", - "//main:v2ray_linux_arm_armv7", - "//main:v2ray_linux_arm_armv7_sig", + "//infra/control/main:v2ctl_linux_arm64", + "//infra/control/main:v2ctl_linux_arm64_sig", + "//main:v2ray_linux_arm64", + "//main:v2ray_linux_arm64_sig", ], - out = "v2ray-linux-arm.zip", - mappings = gen_mappings("linux", "arm"), + out = "v2ray-linux-arm64-v8a.zip", + mappings = gen_mappings("linux", "arm64", "0"), ) pkg_zip( - name = "v2ray_linux_arm64_package", + name = "v2ray_linux_armv7_package", srcs = [ ":config_json", ":doc", ":geodata", ":systemd", - "//infra/control/main:v2ctl_linux_arm64", - "//infra/control/main:v2ctl_linux_arm64_sig", - "//main:v2ray_linux_arm64", - "//main:v2ray_linux_arm64_sig", + "//infra/control/main:v2ctl_linux_arm_7", + "//infra/control/main:v2ctl_linux_arm_7_sig", + "//main:v2ray_linux_arm_7", + "//main:v2ray_linux_arm_7_sig", ], - out = "v2ray-linux-arm64.zip", - mappings = gen_mappings("linux", "arm64"), + out = "v2ray-linux-arm32-v7a.zip", + mappings = gen_mappings("linux", "arm", "7"), ) pkg_zip( - name = "v2ray_linux_mips_package", + name = "v2ray_linux_armv6_package", + srcs = [ + ":config_json", + ":doc", + ":geodata", + ":systemd", + "//infra/control/main:v2ctl_linux_arm_6", + "//infra/control/main:v2ctl_linux_arm_6_sig", + "//main:v2ray_linux_arm_6", + "//main:v2ray_linux_arm_6_sig", + ], + out = "v2ray-linux-arm32-v6.zip", + mappings = gen_mappings("linux", "arm", "6"), +) + +pkg_zip( + name = "v2ray_linux_armv5_package", + srcs = [ + ":config_json", + ":doc", + ":geodata", + ":systemd", + "//infra/control/main:v2ctl_linux_arm_5", + "//infra/control/main:v2ctl_linux_arm_5_sig", + "//main:v2ray_linux_arm_5", + "//main:v2ray_linux_arm_5_sig", + ], + out = "v2ray-linux-arm32-v5.zip", + mappings = gen_mappings("linux", "arm", "5"), +) + +pkg_zip( + name = "v2ray_linux_mips32_package", srcs = [ ":config_json", ":doc", @@ -274,12 +286,12 @@ pkg_zip( "//main:v2ray_linux_mips_softfloat", "//main:v2ray_linux_mips_softfloat_sig", ], - out = "v2ray-linux-mips.zip", - mappings = gen_mappings("linux", "mips"), + out = "v2ray-linux-mips32.zip", + mappings = gen_mappings("linux", "mips", "0"), ) pkg_zip( - name = "v2ray_linux_mipsle_package", + name = "v2ray_linux_mips32le_package", srcs = [ ":config_json", ":doc", @@ -294,8 +306,8 @@ pkg_zip( "//main:v2ray_linux_mipsle_softfloat", "//main:v2ray_linux_mipsle_softfloat_sig", ], - out = "v2ray-linux-mipsle.zip", - mappings = gen_mappings("linux", "mipsle"), + out = "v2ray-linux-mips32le.zip", + mappings = gen_mappings("linux", "mipsle", "0"), ) pkg_zip( @@ -311,7 +323,7 @@ pkg_zip( "//main:v2ray_linux_mips64_sig", ], out = "v2ray-linux-mips64.zip", - mappings = gen_mappings("linux", "mips64"), + mappings = gen_mappings("linux", "mips64", "0"), ) pkg_zip( @@ -327,7 +339,23 @@ pkg_zip( "//main:v2ray_linux_mips64le_sig", ], out = "v2ray-linux-mips64le.zip", - mappings = gen_mappings("linux", "mips64le"), + mappings = gen_mappings("linux", "mips64le", "0"), +) + +pkg_zip( + name = "v2ray_linux_riscv64_package", + srcs = [ + ":config_json", + ":doc", + ":geodata", + ":systemd", + "//infra/control/main:v2ctl_linux_riscv64", + "//infra/control/main:v2ctl_linux_riscv64_sig", + "//main:v2ray_linux_riscv64", + "//main:v2ray_linux_riscv64_sig", + ], + out = "v2ray-linux-riscv64.zip", + mappings = gen_mappings("linux", "riscv64", "0"), ) pkg_zip( @@ -343,7 +371,7 @@ pkg_zip( "//main:v2ray_linux_s390x_sig", ], out = "v2ray-linux-s390x.zip", - mappings = gen_mappings("linux", "s390x"), + mappings = gen_mappings("linux", "s390x", "0"), ) pkg_zip( @@ -359,7 +387,7 @@ pkg_zip( "//main:v2ray_linux_ppc64_sig", ], out = "v2ray-linux-ppc64.zip", - mappings = gen_mappings("linux", "ppc64"), + mappings = gen_mappings("linux", "ppc64", "0"), ) pkg_zip( @@ -375,5 +403,5 @@ pkg_zip( "//main:v2ray_linux_ppc64le_sig", ], out = "v2ray-linux-ppc64le.zip", - mappings = gen_mappings("linux", "ppc64le"), + mappings = gen_mappings("linux", "ppc64le", "0"), ) diff --git a/release/bleedingrelease.sh b/release/bleedingrelease.sh index a2293992..b67a224f 100755 --- a/release/bleedingrelease.sh +++ b/release/bleedingrelease.sh @@ -34,20 +34,23 @@ pushd ${ART_ROOT} { go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen version ${RELEASE_TAG} go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen project "v2flyunstable" - go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-macos.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-macos-64.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-windows-64.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-windows-32.zip - go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-windows-arm.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-windows-arm32-v7a.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-64.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-32.zip - go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-arm.zip - go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-arm64.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-arm64-v8a.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-arm32-v7a.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-arm32-v6.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-arm32-v5.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-mips64.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-mips64le.zip - go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-mips.zip - go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-mipsle.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-mips32.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-mips32le.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-ppc64.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-ppc64le.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-riscv64.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-s390x.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-freebsd-64.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-freebsd-32.zip @@ -65,20 +68,23 @@ pushd ${ART_ROOT} go run github.com/xiaokangwang/V2BuildAssist/v2buildutil post commit "${RELEASE_SHA}" < buildcomment popd -upload ${ART_ROOT}/v2ray-macos.zip +upload ${ART_ROOT}/v2ray-macos-64.zip upload ${ART_ROOT}/v2ray-windows-64.zip upload ${ART_ROOT}/v2ray-windows-32.zip -upload ${ART_ROOT}/v2ray-windows-arm.zip +upload ${ART_ROOT}/v2ray-windows-arm32-v7a.zip upload ${ART_ROOT}/v2ray-linux-64.zip upload ${ART_ROOT}/v2ray-linux-32.zip -upload ${ART_ROOT}/v2ray-linux-arm.zip -upload ${ART_ROOT}/v2ray-linux-arm64.zip +upload ${ART_ROOT}/v2ray-linux-arm64-v8a.zip +upload ${ART_ROOT}/v2ray-linux-arm32-v7a.zip +upload ${ART_ROOT}/v2ray-linux-arm32-v6.zip +upload ${ART_ROOT}/v2ray-linux-arm32-v5.zip upload ${ART_ROOT}/v2ray-linux-mips64.zip upload ${ART_ROOT}/v2ray-linux-mips64le.zip -upload ${ART_ROOT}/v2ray-linux-mips.zip -upload ${ART_ROOT}/v2ray-linux-mipsle.zip +upload ${ART_ROOT}/v2ray-linux-mips32.zip +upload ${ART_ROOT}/v2ray-linux-mips32le.zip upload ${ART_ROOT}/v2ray-linux-ppc64.zip upload ${ART_ROOT}/v2ray-linux-ppc64le.zip +upload ${ART_ROOT}/v2ray-linux-riscv64.zip upload ${ART_ROOT}/v2ray-linux-s390x.zip upload ${ART_ROOT}/v2ray-freebsd-64.zip upload ${ART_ROOT}/v2ray-freebsd-32.zip diff --git a/release/mapping.bzl b/release/mapping.bzl index eae4518a..fb7d6959 100644 --- a/release/mapping.bzl +++ b/release/mapping.bzl @@ -1,7 +1,7 @@ -def gen_mappings(os, arch): +def gen_mappings(os, arch, ver): return { "v2ray_core/release/doc": "doc", "v2ray_core/release/config": "", - "v2ray_core/main/" + os + "/" + arch: "", - "v2ray_core/infra/control/main/" + os + "/" + arch: "", + "v2ray_core/main/" + os + "/" + arch + "/" + ver: "", + "v2ray_core/infra/control/main/" + os + "/" + arch + "/" + ver : "", } diff --git a/release/tagrelease.sh b/release/tagrelease.sh index b43df5c6..6994b2b0 100755 --- a/release/tagrelease.sh +++ b/release/tagrelease.sh @@ -59,20 +59,23 @@ pushd ${ART_ROOT} { go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen version "${GITHUB_RELEASE_TAG}" go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen project "v2fly" - go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-macos.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-macos-64.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-windows-64.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-windows-32.zip - go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-windows-arm.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-windows-arm32-v7a.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-64.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-32.zip - go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-arm.zip - go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-arm64.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-arm64-v8a.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-arm32-v7a.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-arm32-v6.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-arm32-v5.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-mips64.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-mips64le.zip - go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-mips.zip - go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-mipsle.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-mips32.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-mips32le.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-ppc64.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-ppc64le.zip + go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-riscv64.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-linux-s390x.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-freebsd-64.zip go run github.com/xiaokangwang/V2BuildAssist/v2buildutil gen file v2ray-freebsd-32.zip @@ -84,20 +87,23 @@ pushd ${ART_ROOT} popd -upload ${ART_ROOT}/v2ray-macos.zip +upload ${ART_ROOT}/v2ray-macos-64.zip upload ${ART_ROOT}/v2ray-windows-64.zip upload ${ART_ROOT}/v2ray-windows-32.zip -upload ${ART_ROOT}/v2ray-windows-arm.zip +upload ${ART_ROOT}/v2ray-windows-arm32-v7a.zip upload ${ART_ROOT}/v2ray-linux-64.zip upload ${ART_ROOT}/v2ray-linux-32.zip -upload ${ART_ROOT}/v2ray-linux-arm.zip -upload ${ART_ROOT}/v2ray-linux-arm64.zip +upload ${ART_ROOT}/v2ray-linux-arm64-v8a.zip +upload ${ART_ROOT}/v2ray-linux-arm32-v7a.zip +upload ${ART_ROOT}/v2ray-linux-arm32-v6.zip +upload ${ART_ROOT}/v2ray-linux-arm32-v5.zip upload ${ART_ROOT}/v2ray-linux-mips64.zip upload ${ART_ROOT}/v2ray-linux-mips64le.zip -upload ${ART_ROOT}/v2ray-linux-mips.zip -upload ${ART_ROOT}/v2ray-linux-mipsle.zip +upload ${ART_ROOT}/v2ray-linux-mips32.zip +upload ${ART_ROOT}/v2ray-linux-mips32le.zip upload ${ART_ROOT}/v2ray-linux-ppc64.zip upload ${ART_ROOT}/v2ray-linux-ppc64le.zip +upload ${ART_ROOT}/v2ray-linux-riscv64.zip upload ${ART_ROOT}/v2ray-linux-s390x.zip upload ${ART_ROOT}/v2ray-freebsd-64.zip upload ${ART_ROOT}/v2ray-freebsd-32.zip diff --git a/transport/internet/sockopt_darwin.go b/transport/internet/sockopt_darwin.go index ccac238d..e6281923 100644 --- a/transport/internet/sockopt_darwin.go +++ b/transport/internet/sockopt_darwin.go @@ -50,3 +50,11 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) func bindAddr(fd uintptr, address []byte, port uint32) error { return nil } + +func setReuseAddr(fd uintptr) error { + return nil +} + +func setReusePort(fd uintptr) error { + return nil +} diff --git a/transport/internet/sockopt_freebsd.go b/transport/internet/sockopt_freebsd.go index 46634cb6..48c5eda6 100644 --- a/transport/internet/sockopt_freebsd.go +++ b/transport/internet/sockopt_freebsd.go @@ -188,15 +188,8 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) } func bindAddr(fd uintptr, ip []byte, port uint32) error { - if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { - return newError("failed to set resuse_addr").Base(err).AtWarning() - } - - if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePortLB, 1); err != nil { - if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePort, 1); err != nil { - return newError("failed to set resuse_port").Base(err).AtWarning() - } - } + setReuseAddr(fd) + setReusePort(fd) var sockaddr syscall.Sockaddr @@ -219,3 +212,19 @@ func bindAddr(fd uintptr, ip []byte, port uint32) error { return syscall.Bind(int(fd), sockaddr) } + +func setReuseAddr(fd uintptr) error { + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { + return newError("failed to set SO_REUSEADDR").Base(err).AtWarning() + } + return nil +} + +func setReusePort(fd uintptr) error { + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePortLB, 1); err != nil { + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReUsePort, 1); err != nil { + return newError("failed to set SO_REUSEPORT").Base(err).AtWarning() + } + } + return nil +} diff --git a/transport/internet/sockopt_linux.go b/transport/internet/sockopt_linux.go index 068879e8..9c1c8f31 100644 --- a/transport/internet/sockopt_linux.go +++ b/transport/internet/sockopt_linux.go @@ -15,13 +15,8 @@ const ( ) func bindAddr(fd uintptr, ip []byte, port uint32) error { - if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { - return newError("failed to set resuse_addr").Base(err).AtWarning() - } - - if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil { - return newError("failed to set resuse_port").Base(err).AtWarning() - } + setReuseAddr(fd) + setReusePort(fd) var sockaddr syscall.Sockaddr @@ -107,9 +102,19 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) } } + return nil +} + +func setReuseAddr(fd uintptr) error { + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { + return newError("failed to set SO_REUSEADDR").Base(err).AtWarning() + } + return nil +} + +func setReusePort(fd uintptr) error { if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil { return newError("failed to set SO_REUSEPORT").Base(err).AtWarning() } - return nil } diff --git a/transport/internet/sockopt_other.go b/transport/internet/sockopt_other.go index 962c981d..ac61296b 100644 --- a/transport/internet/sockopt_other.go +++ b/transport/internet/sockopt_other.go @@ -13,3 +13,11 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) func bindAddr(fd uintptr, ip []byte, port uint32) error { return nil } + +func setReuseAddr(fd uintptr) error { + return nil +} + +func setReusePort(fd uintptr) error { + return nil +} diff --git a/transport/internet/sockopt_windows.go b/transport/internet/sockopt_windows.go index f47aa07a..cb6292bc 100644 --- a/transport/internet/sockopt_windows.go +++ b/transport/internet/sockopt_windows.go @@ -46,3 +46,11 @@ func applyInboundSocketOptions(network string, fd uintptr, config *SocketConfig) func bindAddr(fd uintptr, ip []byte, port uint32) error { return nil } + +func setReuseAddr(fd uintptr) error { + return nil +} + +func setReusePort(fd uintptr) error { + return nil +} diff --git a/transport/internet/system_listener.go b/transport/internet/system_listener.go index 4c85c9ae..b17cbbba 100644 --- a/transport/internet/system_listener.go +++ b/transport/internet/system_listener.go @@ -27,6 +27,8 @@ func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []co } } + setReusePort(fd) + for _, controller := range controllers { if err := controller(network, address, fd); err != nil { newError("failed to apply external controller").Base(err).WriteToLog(session.ExportIDToError(ctx)) @@ -39,9 +41,7 @@ func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []co func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.Listener, error) { var lc net.ListenConfig - if sockopt != nil || len(dl.controllers) > 0 { - lc.Control = getControlFunc(ctx, sockopt, dl.controllers) - } + lc.Control = getControlFunc(ctx, sockopt, dl.controllers) return lc.Listen(ctx, addr.Network(), addr.String()) } @@ -49,9 +49,7 @@ func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *S func (dl *DefaultListener) ListenPacket(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.PacketConn, error) { var lc net.ListenConfig - if sockopt != nil || len(dl.controllers) > 0 { - lc.Control = getControlFunc(ctx, sockopt, dl.controllers) - } + lc.Control = getControlFunc(ctx, sockopt, dl.controllers) return lc.ListenPacket(ctx, addr.Network(), addr.String()) } diff --git a/transport/internet/tcp/dialer.go b/transport/internet/tcp/dialer.go index 322583a4..e6f414e0 100644 --- a/transport/internet/tcp/dialer.go +++ b/transport/internet/tcp/dialer.go @@ -22,11 +22,14 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *internet.Me if config := tls.ConfigFromStreamSettings(streamSettings); config != nil { tlsConfig := config.GetTLSConfig(tls.WithDestination(dest)) - if config.IsExperiment8357() { - conn = tls.UClient(conn, tlsConfig) - } else { - conn = tls.Client(conn, tlsConfig) - } + /* + if config.IsExperiment8357() { + conn = tls.UClient(conn, tlsConfig) + } else { + conn = tls.Client(conn, tlsConfig) + } + */ + conn = tls.Client(conn, tlsConfig) } tcpSettings := streamSettings.ProtocolSettings.(*Config) diff --git a/transport/internet/tls/tls.go b/transport/internet/tls/tls.go index baf5d393..6319a61d 100644 --- a/transport/internet/tls/tls.go +++ b/transport/internet/tls/tls.go @@ -7,8 +7,6 @@ import ( "v2ray.com/core/common/buf" "v2ray.com/core/common/net" - - utls "github.com/refraction-networking/utls" ) //go:generate errorgen @@ -45,6 +43,7 @@ func Client(c net.Conn, config *tls.Config) net.Conn { return &Conn{Conn: tlsConn} } +/* func copyConfig(c *tls.Config) *utls.Config { return &utls.Config{ NextProtos: c.NextProtos, @@ -59,6 +58,7 @@ func UClient(c net.Conn, config *tls.Config) net.Conn { uConfig := copyConfig(config) return utls.Client(c, uConfig) } +*/ // Server initiates a TLS server handshake on the given connection. func Server(c net.Conn, config *tls.Config) net.Conn {