├── README.md ├── frp-1.jpg ├── frp-2.jpg ├── frp-dev ├── .circleci │ └── config.yml ├── .github │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE │ │ ├── bug_report.yaml │ │ ├── config.yml │ │ └── feature_request.yaml │ └── workflows │ │ ├── build-and-push-image.yml │ │ ├── golangci-lint.yml │ │ ├── goreleaser.yml │ │ └── stale.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yml ├── LICENSE ├── Makefile ├── Makefile.cross-compiles ├── README.md ├── README_zh.md ├── Release.md ├── assets │ ├── assets.go │ ├── frpc │ │ ├── embed.go │ │ └── static │ │ │ ├── 535877f50039c0cb49a6196a5b7517cd.woff │ │ │ ├── 732389ded34cb9c52dd88271f1345af9.ttf │ │ │ ├── favicon.ico │ │ │ ├── index.html │ │ │ ├── manifest.js │ │ │ └── vendor.js │ └── frps │ │ ├── embed.go │ │ └── static │ │ ├── 535877f50039c0cb49a6196a5b7517cd.woff │ │ ├── 732389ded34cb9c52dd88271f1345af9.ttf │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── manifest.js │ │ └── vendor.js ├── client │ ├── admin.go │ ├── admin_api.go │ ├── control.go │ ├── event │ │ └── event.go │ ├── health │ │ └── health.go │ ├── proxy │ │ ├── proxy.go │ │ ├── proxy_manager.go │ │ └── proxy_wrapper.go │ ├── service.go │ ├── visitor.go │ └── visitor_manager.go ├── cmd │ ├── frpc │ │ ├── main.go │ │ └── sub │ │ │ ├── http.go │ │ │ ├── https.go │ │ │ ├── reload.go │ │ │ ├── root.go │ │ │ ├── status.go │ │ │ ├── stcp.go │ │ │ ├── sudp.go │ │ │ ├── tcp.go │ │ │ ├── tcpmux.go │ │ │ ├── udp.go │ │ │ ├── verify.go │ │ │ └── xtcp.go │ └── frps │ │ ├── main.go │ │ ├── root.go │ │ └── verify.go ├── conf │ ├── frpc.ini │ ├── frpc_full.ini │ ├── frps.ini │ └── frps_full.ini ├── doc │ ├── pic │ │ ├── architecture.png │ │ ├── dashboard.png │ │ ├── donate-alipay.png │ │ ├── donate-wechatpay.png │ │ ├── sponsor_doppler.png │ │ ├── sponsor_workos.png │ │ └── zsxq.jpg │ └── server_plugin.md ├── dockerfiles │ ├── Dockerfile-for-frpc │ └── Dockerfile-for-frps ├── go.mod ├── go.sum ├── hack │ └── run-e2e.sh ├── package.sh ├── pkg │ ├── auth │ │ ├── auth.go │ │ ├── oidc.go │ │ └── token.go │ ├── config │ │ ├── README.md │ │ ├── client.go │ │ ├── client_test.go │ │ ├── parse.go │ │ ├── proxy.go │ │ ├── proxy_test.go │ │ ├── server.go │ │ ├── server_test.go │ │ ├── types.go │ │ ├── types_test.go │ │ ├── utils.go │ │ ├── value.go │ │ ├── visitor.go │ │ └── visitor_test.go │ ├── consts │ │ └── consts.go │ ├── errors │ │ └── errors.go │ ├── metrics │ │ ├── aggregate │ │ │ └── server.go │ │ ├── mem │ │ │ ├── server.go │ │ │ └── types.go │ │ ├── metrics.go │ │ └── prometheus │ │ │ └── server.go │ ├── msg │ │ ├── ctl.go │ │ └── msg.go │ ├── nathole │ │ └── nathole.go │ ├── plugin │ │ ├── client │ │ │ ├── http2https.go │ │ │ ├── http_proxy.go │ │ │ ├── https2http.go │ │ │ ├── https2https.go │ │ │ ├── plugin.go │ │ │ ├── socks5.go │ │ │ ├── static_file.go │ │ │ └── unix_domain_socket.go │ │ └── server │ │ │ ├── http.go │ │ │ ├── manager.go │ │ │ ├── plugin.go │ │ │ ├── tracer.go │ │ │ └── types.go │ ├── proto │ │ └── udp │ │ │ ├── udp.go │ │ │ └── udp_test.go │ ├── transport │ │ └── tls.go │ └── util │ │ ├── limit │ │ ├── reader.go │ │ └── writer.go │ │ ├── log │ │ └── log.go │ │ ├── metric │ │ ├── counter.go │ │ ├── counter_test.go │ │ ├── date_counter.go │ │ ├── date_counter_test.go │ │ └── metrics.go │ │ ├── net │ │ ├── conn.go │ │ ├── dial.go │ │ ├── http.go │ │ ├── kcp.go │ │ ├── listener.go │ │ ├── tls.go │ │ ├── udp.go │ │ └── websocket.go │ │ ├── tcpmux │ │ └── httpconnect.go │ │ ├── util │ │ ├── http.go │ │ ├── util.go │ │ └── util_test.go │ │ ├── version │ │ ├── version.go │ │ └── version_test.go │ │ ├── vhost │ │ ├── http.go │ │ ├── https.go │ │ ├── https_test.go │ │ ├── resource.go │ │ ├── router.go │ │ └── vhost.go │ │ └── xlog │ │ ├── ctx.go │ │ └── xlog.go ├── server │ ├── control.go │ ├── controller │ │ └── resource.go │ ├── dashboard.go │ ├── dashboard_api.go │ ├── group │ │ ├── group.go │ │ ├── http.go │ │ ├── tcp.go │ │ └── tcpmux.go │ ├── metrics │ │ └── metrics.go │ ├── ports │ │ └── ports.go │ ├── proxy │ │ ├── http.go │ │ ├── https.go │ │ ├── proxy.go │ │ ├── stcp.go │ │ ├── sudp.go │ │ ├── tcp.go │ │ ├── tcpmux.go │ │ ├── udp.go │ │ └── xtcp.go │ ├── service.go │ └── visitor │ │ └── visitor.go ├── test │ └── e2e │ │ ├── basic │ │ ├── basic.go │ │ ├── client.go │ │ ├── client_server.go │ │ ├── cmd.go │ │ ├── config.go │ │ ├── http.go │ │ └── server.go │ │ ├── e2e.go │ │ ├── e2e_test.go │ │ ├── examples.go │ │ ├── features │ │ ├── bandwidth_limit.go │ │ ├── chaos.go │ │ ├── group.go │ │ ├── heartbeat.go │ │ ├── monitor.go │ │ └── real_ip.go │ │ ├── framework │ │ ├── cleanup.go │ │ ├── client.go │ │ ├── consts │ │ │ └── consts.go │ │ ├── expect.go │ │ ├── framework.go │ │ ├── ginkgowrapper │ │ │ └── wrapper.go │ │ ├── log.go │ │ ├── mockservers.go │ │ ├── process.go │ │ ├── request.go │ │ ├── test_context.go │ │ └── util.go │ │ ├── mock │ │ └── server │ │ │ ├── httpserver │ │ │ └── server.go │ │ │ ├── interface.go │ │ │ └── streamserver │ │ │ └── server.go │ │ ├── pkg │ │ ├── cert │ │ │ ├── generator.go │ │ │ └── selfsigned.go │ │ ├── port │ │ │ ├── port.go │ │ │ └── util.go │ │ ├── process │ │ │ └── process.go │ │ ├── request │ │ │ └── request.go │ │ ├── rpc │ │ │ └── rpc.go │ │ ├── sdk │ │ │ └── client │ │ │ │ └── client.go │ │ └── utils │ │ │ └── utils.go │ │ ├── plugin │ │ ├── client.go │ │ ├── server.go │ │ └── utils.go │ │ └── suites.go └── web │ ├── frpc │ ├── .babelrc │ ├── .gitignore │ ├── Makefile │ ├── package.json │ ├── postcss.config.js │ ├── src │ │ ├── App.vue │ │ ├── assets │ │ │ └── favicon.ico │ │ ├── components │ │ │ ├── Configure.vue │ │ │ └── Overview.vue │ │ ├── index.html │ │ ├── main.js │ │ ├── router │ │ │ └── index.js │ │ └── utils │ │ │ ├── less │ │ │ └── custom.less │ │ │ └── status.js │ ├── webpack.config.js │ └── yarn.lock │ └── frps │ ├── .babelrc │ ├── .gitignore │ ├── Makefile │ ├── package.json │ ├── postcss.config.js │ ├── src │ ├── App.vue │ ├── assets │ │ └── favicon.ico │ ├── components │ │ ├── Overview.vue │ │ ├── ProxiesHttp.vue │ │ ├── ProxiesHttps.vue │ │ ├── ProxiesStcp.vue │ │ ├── ProxiesSudp.vue │ │ ├── ProxiesTcp.vue │ │ ├── ProxiesUdp.vue │ │ └── Traffic.vue │ ├── index.html │ ├── main.js │ ├── router │ │ └── index.js │ └── utils │ │ ├── chart.js │ │ ├── less │ │ └── custom.less │ │ └── proxy.js │ ├── webpack.config.js │ └── yarn.lock └── xor.py /frp-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M1r0ku/frp-Modify/ca899c3e75b9916bec5b124c5829afb2250f7603/frp-1.jpg -------------------------------------------------------------------------------- /frp-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M1r0ku/frp-Modify/ca899c3e75b9916bec5b124c5829afb2250f7603/frp-2.jpg -------------------------------------------------------------------------------- /frp-dev/.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | go-version-latest: 4 | docker: 5 | - image: cimg/go:1.19-node 6 | resource_class: large 7 | steps: 8 | - checkout 9 | - run: make 10 | - run: make alltest 11 | go-version-last: 12 | docker: 13 | - image: cimg/go:1.18-node 14 | resource_class: large 15 | steps: 16 | - checkout 17 | - run: make 18 | - run: make alltest 19 | 20 | workflows: 21 | version: 2 22 | build_and_test: 23 | jobs: 24 | - go-version-latest 25 | - go-version-last 26 | -------------------------------------------------------------------------------- /frp-dev/.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [fatedier] 4 | -------------------------------------------------------------------------------- /frp-dev/.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug report 2 | description: Report a bug to help us improve frp 3 | 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking the time to fill out this bug report! 9 | - type: textarea 10 | id: bug-description 11 | attributes: 12 | label: Bug Description 13 | description: Tell us what issues you ran into 14 | placeholder: Include information about what you tried, what you expected to happen, and what actually happened. The more details, the better! 15 | validations: 16 | required: true 17 | - type: input 18 | id: frpc-version 19 | attributes: 20 | label: frpc Version 21 | description: Include the output of `frpc -v` 22 | validations: 23 | required: true 24 | - type: input 25 | id: frps-version 26 | attributes: 27 | label: frps Version 28 | description: Include the output of `frps -v` 29 | validations: 30 | required: true 31 | - type: input 32 | id: system-architecture 33 | attributes: 34 | label: System Architecture 35 | description: Include which architecture you used, such as `linux/amd64`, `windows/amd64` 36 | validations: 37 | required: true 38 | - type: textarea 39 | id: config 40 | attributes: 41 | label: Configurations 42 | description: Include what configurrations you used and ran into this problem 43 | placeholder: Pay attention to hiding the token and password in your output 44 | validations: 45 | required: true 46 | - type: textarea 47 | id: log 48 | attributes: 49 | label: Logs 50 | description: Prefer you providing releated error logs here 51 | placeholder: Pay attention to hiding your personal informations 52 | - type: textarea 53 | id: steps-to-reproduce 54 | attributes: 55 | label: Steps to reproduce 56 | description: How to reproduce it? It's important for us to find the bug 57 | value: | 58 | 1. 59 | 2. 60 | 3. 61 | ... 62 | - type: checkboxes 63 | id: area 64 | attributes: 65 | label: Affected area 66 | options: 67 | - label: "Docs" 68 | - label: "Installation" 69 | - label: "Performance and Scalability" 70 | - label: "Security" 71 | - label: "User Experience" 72 | - label: "Test and Release" 73 | - label: "Developer Infrastructure" 74 | - label: "Client Plugin" 75 | - label: "Server Plugin" 76 | - label: "Extensions" 77 | - label: "Others" 78 | -------------------------------------------------------------------------------- /frp-dev/.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | -------------------------------------------------------------------------------- /frp-dev/.github/ISSUE_TEMPLATE/feature_request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea to improve frp 3 | title: "[Feature Request] " 4 | 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | This is only used to request new product features. 10 | - type: textarea 11 | id: feature-request 12 | attributes: 13 | label: Describe the feature request 14 | description: Tell us what's you want and why it should be added in frp. 15 | validations: 16 | required: true 17 | - type: textarea 18 | id: alternatives 19 | attributes: 20 | label: Describe alternatives you've considered 21 | - type: checkboxes 22 | id: area 23 | attributes: 24 | label: Affected area 25 | options: 26 | - label: "Docs" 27 | - label: "Installation" 28 | - label: "Performance and Scalability" 29 | - label: "Security" 30 | - label: "User Experience" 31 | - label: "Test and Release" 32 | - label: "Developer Infrastructure" 33 | - label: "Client Plugin" 34 | - label: "Server Plugin" 35 | - label: "Extensions" 36 | - label: "Others" 37 | -------------------------------------------------------------------------------- /frp-dev/.github/workflows/build-and-push-image.yml: -------------------------------------------------------------------------------- 1 | name: Build Image and Publish to Dockerhub & GPR 2 | 3 | on: 4 | release: 5 | types: [ created ] 6 | workflow_dispatch: 7 | inputs: 8 | tag: 9 | description: 'Image tag' 10 | required: true 11 | default: 'test' 12 | jobs: 13 | image: 14 | name: Build Image from Dockerfile and binaries 15 | runs-on: ubuntu-latest 16 | steps: 17 | # environment 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | with: 21 | fetch-depth: '0' 22 | 23 | - name: Set up QEMU 24 | uses: docker/setup-qemu-action@v1 25 | 26 | - name: Set up Docker Buildx 27 | uses: docker/setup-buildx-action@v1 28 | 29 | # get image tag name 30 | - name: Get Image Tag Name 31 | run: | 32 | if [ x${{ github.event.inputs.tag }} == x"" ]; then 33 | echo "TAG_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV 34 | else 35 | echo "TAG_NAME=${{ github.event.inputs.tag }}" >> $GITHUB_ENV 36 | fi 37 | - name: Login to DockerHub 38 | uses: docker/login-action@v1 39 | with: 40 | username: ${{ secrets.DOCKERHUB_USERNAME }} 41 | password: ${{ secrets.DOCKERHUB_PASSWORD }} 42 | 43 | - name: Login to the GPR 44 | uses: docker/login-action@v1 45 | with: 46 | registry: ghcr.io 47 | username: ${{ github.repository_owner }} 48 | password: ${{ secrets.GPR_TOKEN }} 49 | 50 | # prepare image tags 51 | - name: Prepare Image Tags 52 | run: | 53 | echo "DOCKERFILE_FRPC_PATH=dockerfiles/Dockerfile-for-frpc" >> $GITHUB_ENV 54 | echo "DOCKERFILE_FRPS_PATH=dockerfiles/Dockerfile-for-frps" >> $GITHUB_ENV 55 | echo "TAG_FRPC=fatedier/frpc:${{ env.TAG_NAME }}" >> $GITHUB_ENV 56 | echo "TAG_FRPS=fatedier/frps:${{ env.TAG_NAME }}" >> $GITHUB_ENV 57 | echo "TAG_FRPC_GPR=ghcr.io/fatedier/frpc:${{ env.TAG_NAME }}" >> $GITHUB_ENV 58 | echo "TAG_FRPS_GPR=ghcr.io/fatedier/frps:${{ env.TAG_NAME }}" >> $GITHUB_ENV 59 | 60 | - name: Build and push frpc 61 | uses: docker/build-push-action@v2 62 | with: 63 | context: . 64 | file: ./dockerfiles/Dockerfile-for-frpc 65 | platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le,linux/riscv64,linux/s390x 66 | push: true 67 | tags: | 68 | ${{ env.TAG_FRPC }} 69 | ${{ env.TAG_FRPC_GPR }} 70 | 71 | - name: Build and push frps 72 | uses: docker/build-push-action@v2 73 | with: 74 | context: . 75 | file: ./dockerfiles/Dockerfile-for-frps 76 | platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le,linux/riscv64,linux/s390x 77 | push: true 78 | tags: | 79 | ${{ env.TAG_FRPS }} 80 | ${{ env.TAG_FRPS_GPR }} 81 | -------------------------------------------------------------------------------- /frp-dev/.github/workflows/golangci-lint.yml: -------------------------------------------------------------------------------- 1 | name: golangci-lint 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - dev 7 | pull_request: 8 | permissions: 9 | contents: read 10 | # Optional: allow read access to pull request. Use with `only-new-issues` option. 11 | pull-requests: read 12 | jobs: 13 | golangci: 14 | name: lint 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/setup-go@v3 18 | with: 19 | go-version: 1.19 20 | - uses: actions/checkout@v3 21 | - name: golangci-lint 22 | uses: golangci/golangci-lint-action@v3 23 | with: 24 | # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version 25 | version: v1.49.0 26 | 27 | # Optional: golangci-lint command line arguments. 28 | # args: --issues-exit-code=0 29 | 30 | # Optional: show only new issues if it's a pull request. The default value is `false`. 31 | # only-new-issues: true 32 | 33 | # Optional: if set to true then the all caching functionality will be complete disabled, 34 | # takes precedence over all other caching options. 35 | # skip-cache: true 36 | 37 | # Optional: if set to true then the action don't cache or restore ~/go/pkg. 38 | # skip-pkg-cache: true 39 | 40 | # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. 41 | # skip-build-cache: true 42 | -------------------------------------------------------------------------------- /frp-dev/.github/workflows/goreleaser.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | goreleaser: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v2 12 | with: 13 | fetch-depth: 0 14 | 15 | - name: Set up Go 16 | uses: actions/setup-go@v2 17 | with: 18 | go-version: 1.19 19 | 20 | - run: | 21 | # https://github.com/actions/setup-go/issues/107 22 | cp -f `which go` /usr/bin/go 23 | 24 | - name: Make All 25 | run: | 26 | ./package.sh 27 | 28 | - name: Run GoReleaser 29 | uses: goreleaser/goreleaser-action@v2 30 | with: 31 | version: latest 32 | args: release --rm-dist --release-notes=./Release.md 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GPR_TOKEN }} 35 | -------------------------------------------------------------------------------- /frp-dev/.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues" 2 | on: 3 | schedule: 4 | - cron: "20 0 * * *" 5 | workflow_dispatch: 6 | inputs: 7 | debug-only: 8 | description: 'In debug mod' 9 | required: false 10 | default: 'false' 11 | jobs: 12 | stale: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/stale@v5 16 | with: 17 | repo-token: ${{ secrets.GITHUB_TOKEN }} 18 | stale-issue-message: 'Issues go stale after 30d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.' 19 | stale-pr-message: "PRs go stale after 30d of inactivity. Stale PRs rot after an additional 7d of inactivity and eventually close." 20 | stale-issue-label: 'lifecycle/stale' 21 | exempt-issue-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned' 22 | stale-pr-label: 'lifecycle/stale' 23 | exempt-pr-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned' 24 | days-before-stale: 30 25 | days-before-close: 7 26 | debug-only: ${{ github.event.inputs.debug-only }} 27 | exempt-all-pr-milestones: true 28 | exempt-all-pr-assignees: true 29 | -------------------------------------------------------------------------------- /frp-dev/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | # Self 27 | bin/ 28 | packages/ 29 | release/ 30 | test/bin/ 31 | vendor/ 32 | dist/ 33 | .idea/ 34 | 35 | # Cache 36 | *.swp 37 | -------------------------------------------------------------------------------- /frp-dev/.goreleaser.yml: -------------------------------------------------------------------------------- 1 | builds: 2 | - skip: true 3 | checksum: 4 | name_template: '{{ .ProjectName }}_sha256_checksums.txt' 5 | algorithm: sha256 6 | extra_files: 7 | - glob: ./release/packages/* 8 | release: 9 | # Same as for github 10 | # Note: it can only be one: either github, gitlab or gitea 11 | github: 12 | owner: fatedier 13 | name: frp 14 | 15 | draft: false 16 | 17 | # You can add extra pre-existing files to the release. 18 | # The filename on the release will be the last part of the path (base). If 19 | # another file with the same name exists, the latest one found will be used. 20 | # Defaults to empty. 21 | extra_files: 22 | - glob: ./release/packages/* 23 | -------------------------------------------------------------------------------- /frp-dev/Makefile: -------------------------------------------------------------------------------- 1 | export PATH := $(GOPATH)/bin:$(PATH) 2 | export GO111MODULE=on 3 | LDFLAGS := -s -w 4 | 5 | all: fmt build 6 | 7 | build: frps frpc 8 | 9 | # compile assets into binary file 10 | file: 11 | rm -rf ./assets/frps/static/* 12 | rm -rf ./assets/frpc/static/* 13 | cp -rf ./web/frps/dist/* ./assets/frps/static 14 | cp -rf ./web/frpc/dist/* ./assets/frpc/static 15 | 16 | fmt: 17 | go fmt ./... 18 | 19 | vet: 20 | go vet ./... 21 | 22 | frps: 23 | env CGO_ENABLED=0 go build -trimpath -ldflags "$(LDFLAGS)" -o bin/frps ./cmd/frps 24 | 25 | frpc: 26 | env CGO_ENABLED=0 go build -trimpath -ldflags "$(LDFLAGS)" -o bin/frpc ./cmd/frpc 27 | 28 | test: gotest 29 | 30 | gotest: 31 | go test -v --cover ./assets/... 32 | go test -v --cover ./cmd/... 33 | go test -v --cover ./client/... 34 | go test -v --cover ./server/... 35 | go test -v --cover ./pkg/... 36 | 37 | e2e: 38 | ./hack/run-e2e.sh 39 | 40 | e2e-trace: 41 | DEBUG=true LOG_LEVEL=trace ./hack/run-e2e.sh 42 | 43 | alltest: vet gotest e2e 44 | 45 | clean: 46 | rm -f ./bin/frpc 47 | rm -f ./bin/frps 48 | -------------------------------------------------------------------------------- /frp-dev/Makefile.cross-compiles: -------------------------------------------------------------------------------- 1 | export PATH := $(GOPATH)/bin:$(PATH) 2 | export GO111MODULE=on 3 | LDFLAGS := -s -w 4 | 5 | os-archs=darwin:amd64 darwin:arm64 freebsd:386 freebsd:amd64 linux:386 linux:amd64 linux:arm linux:arm64 windows:386 windows:amd64 linux:mips64 linux:mips64le linux:mips:softfloat linux:mipsle:softfloat linux:riscv64 6 | 7 | all: build 8 | 9 | build: app 10 | 11 | app: 12 | @$(foreach n, $(os-archs),\ 13 | os=$(shell echo "$(n)" | cut -d : -f 1);\ 14 | arch=$(shell echo "$(n)" | cut -d : -f 2);\ 15 | gomips=$(shell echo "$(n)" | cut -d : -f 3);\ 16 | target_suffix=$${os}_$${arch};\ 17 | echo "Build $${os}-$${arch}...";\ 18 | env CGO_ENABLED=0 GOOS=$${os} GOARCH=$${arch} GOMIPS=$${gomips} go build -trimpath -ldflags "$(LDFLAGS)" -o ./release/frpc_$${target_suffix} ./cmd/frpc;\ 19 | env CGO_ENABLED=0 GOOS=$${os} GOARCH=$${arch} GOMIPS=$${gomips} go build -trimpath -ldflags "$(LDFLAGS)" -o ./release/frps_$${target_suffix} ./cmd/frps;\ 20 | echo "Build $${os}-$${arch} done";\ 21 | ) 22 | @mv ./release/frpc_windows_386 ./release/frpc_windows_386.exe 23 | @mv ./release/frps_windows_386 ./release/frps_windows_386.exe 24 | @mv ./release/frpc_windows_amd64 ./release/frpc_windows_amd64.exe 25 | @mv ./release/frps_windows_amd64 ./release/frps_windows_amd64.exe 26 | -------------------------------------------------------------------------------- /frp-dev/README_zh.md: -------------------------------------------------------------------------------- 1 | # frp 2 | 3 | [![Build Status](https://travis-ci.org/fatedier/frp.svg?branch=master)](https://travis-ci.org/fatedier/frp) 4 | [![GitHub release](https://img.shields.io/github/tag/fatedier/frp.svg?label=release)](https://github.com/fatedier/frp/releases) 5 | 6 | [README](README.md) | [中文文档](README_zh.md) 7 | 8 | frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。 9 | 10 |

Platinum Sponsors

11 | 12 | 13 |

14 | 15 | 16 | 17 |

18 | 19 | 20 | 21 |

Gold Sponsors

22 | 23 | 24 |

25 | 26 | 27 | 28 |

29 | 30 | 31 | 32 |

Silver Sponsors

33 | 34 | * Sakura Frp - 欢迎点击 "加入我们" 35 | 36 | ## 为什么使用 frp ? 37 | 38 | 通过在具有公网 IP 的节点上部署 frp 服务端,可以轻松地将内网服务穿透到公网,同时提供诸多专业的功能特性,这包括: 39 | 40 | * 客户端服务端通信支持 TCP、KCP 以及 Websocket 等多种协议。 41 | * 采用 TCP 连接流式复用,在单个连接间承载更多请求,节省连接建立时间。 42 | * 代理组间的负载均衡。 43 | * 端口复用,多个服务通过同一个服务端端口暴露。 44 | * 多个原生支持的客户端插件(静态文件查看,HTTP、SOCK5 代理等),便于独立使用 frp 客户端完成某些工作。 45 | * 高度扩展性的服务端插件系统,方便结合自身需求进行功能扩展。 46 | * 服务端和客户端 UI 页面。 47 | 48 | ## 开发状态 49 | 50 | frp 目前已被很多公司广泛用于测试、生产环境。 51 | 52 | master 分支用于发布稳定版本,dev 分支用于开发,您可以尝试下载最新的 release 版本进行测试。 53 | 54 | 我们正在进行 v2 大版本的开发,将会尝试在各个方面进行重构和升级,且不会与 v1 版本进行兼容,预计会持续一段时间。 55 | 56 | 现在的 v0 版本将会在合适的时间切换为 v1 版本并且保证兼容性,后续只做 bug 修复和优化,不再进行大的功能性更新。 57 | 58 | ## 文档 59 | 60 | 完整文档已经迁移至 [https://gofrp.org](https://gofrp.org/docs)。 61 | 62 | ## 为 frp 做贡献 63 | 64 | frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进步贡献力量。 65 | 66 | * 在使用过程中出现任何问题,可以通过 [issues](https://github.com/fatedier/frp/issues) 来反馈。 67 | * Bug 的修复可以直接提交 Pull Request 到 dev 分支。 68 | * 如果是增加新的功能特性,请先创建一个 issue 并做简单描述以及大致的实现方法,提议被采纳后,就可以创建一个实现新特性的 Pull Request。 69 | * 欢迎对说明文档做出改善,帮助更多的人使用 frp,特别是英文文档。 70 | * 贡献代码请提交 PR 至 dev 分支,master 分支仅用于发布稳定可用版本。 71 | * 如果你有任何其他方面的问题或合作,欢迎发送邮件至 fatedier@gmail.com 。 72 | 73 | **提醒:和项目相关的问题最好在 [issues](https://github.com/fatedier/frp/issues) 中反馈,这样方便其他有类似问题的人可以快速查找解决方法,并且也避免了我们重复回答一些问题。** 74 | 75 | ## 捐助 76 | 77 | 如果您觉得 frp 对你有帮助,欢迎给予我们一定的捐助来维持项目的长期发展。 78 | 79 | ### GitHub Sponsors 80 | 81 | 您可以通过 [GitHub Sponsors](https://github.com/sponsors/fatedier) 赞助我们。 82 | 83 | 企业赞助者可以将贵公司的 Logo 以及链接放置在项目 README 文件中。 84 | 85 | ### 知识星球 86 | 87 | 如果您想学习 frp 相关的知识和技术,或者寻求任何帮助及咨询,都可以通过微信扫描下方的二维码付费加入知识星球的官方社群: 88 | 89 | ![zsxq](/doc/pic/zsxq.jpg) 90 | 91 | ### 支付宝扫码捐赠 92 | 93 | ![donate-alipay](/doc/pic/donate-alipay.png) 94 | 95 | ### 微信支付捐赠 96 | 97 | ![donate-wechatpay](/doc/pic/donate-wechatpay.png) 98 | -------------------------------------------------------------------------------- /frp-dev/Release.md: -------------------------------------------------------------------------------- 1 | ### New 2 | 3 | * Use auto generated certificates if `plugin_key_path` and `plugin_crt_path` are empty for plugin `https2https` and `https2http`. 4 | * Server dashboard supports TLS configs. 5 | 6 | ### Fix 7 | 8 | * xtcp error with IPv6 address. 9 | -------------------------------------------------------------------------------- /frp-dev/assets/assets.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package assets 16 | 17 | import ( 18 | "io/fs" 19 | "net/http" 20 | ) 21 | 22 | var ( 23 | // read-only filesystem created by "embed" for embedded files 24 | content fs.FS 25 | 26 | FileSystem http.FileSystem 27 | 28 | // if prefix is not empty, we get file content from disk 29 | prefixPath string 30 | ) 31 | 32 | // if path is empty, load assets in memory 33 | // or set FileSystem using disk files 34 | func Load(path string) { 35 | prefixPath = path 36 | if prefixPath != "" { 37 | FileSystem = http.Dir(prefixPath) 38 | } else { 39 | FileSystem = http.FS(content) 40 | } 41 | } 42 | 43 | func Register(fileSystem fs.FS) { 44 | subFs, err := fs.Sub(fileSystem, "static") 45 | if err == nil { 46 | content = subFs 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /frp-dev/assets/frpc/embed.go: -------------------------------------------------------------------------------- 1 | package frpc 2 | 3 | import ( 4 | "embed" 5 | 6 | "github.com/fatedier/frp/assets" 7 | ) 8 | 9 | //go:embed static/* 10 | var content embed.FS 11 | 12 | func init() { 13 | assets.Register(content) 14 | } 15 | -------------------------------------------------------------------------------- /frp-dev/assets/frpc/static/535877f50039c0cb49a6196a5b7517cd.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M1r0ku/frp-Modify/ca899c3e75b9916bec5b124c5829afb2250f7603/frp-dev/assets/frpc/static/535877f50039c0cb49a6196a5b7517cd.woff -------------------------------------------------------------------------------- /frp-dev/assets/frpc/static/732389ded34cb9c52dd88271f1345af9.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M1r0ku/frp-Modify/ca899c3e75b9916bec5b124c5829afb2250f7603/frp-dev/assets/frpc/static/732389ded34cb9c52dd88271f1345af9.ttf -------------------------------------------------------------------------------- /frp-dev/assets/frpc/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M1r0ku/frp-Modify/ca899c3e75b9916bec5b124c5829afb2250f7603/frp-dev/assets/frpc/static/favicon.ico -------------------------------------------------------------------------------- /frp-dev/assets/frpc/static/index.html: -------------------------------------------------------------------------------- 1 | frp client admin UI
-------------------------------------------------------------------------------- /frp-dev/assets/frpc/static/manifest.js: -------------------------------------------------------------------------------- 1 | !function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,c,u){for(var i,a,f,l=0,s=[];l frps dashboard
-------------------------------------------------------------------------------- /frp-dev/assets/frps/static/manifest.js: -------------------------------------------------------------------------------- 1 | !function(e){function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}var r=window.webpackJsonp;window.webpackJsonp=function(t,u,c){for(var i,a,f,l=0,s=[];l /dev/null 6 | if [ $? -ne 0 ]; then 7 | echo "ginkgo not found, try to install..." 8 | go install github.com/onsi/ginkgo/ginkgo@v1.16.5 9 | fi 10 | 11 | debug=false 12 | if [ x${DEBUG} == x"true" ]; then 13 | debug=true 14 | fi 15 | logLevel=debug 16 | if [ x${LOG_LEVEL} != x"" ]; then 17 | logLevel=${LOG_LEVEL} 18 | fi 19 | 20 | ginkgo -nodes=8 -slowSpecThreshold=20 ${ROOT}/test/e2e -- -frpc-path=${ROOT}/bin/frpc -frps-path=${ROOT}/bin/frps -log-level=${logLevel} -debug=${debug} 21 | -------------------------------------------------------------------------------- /frp-dev/package.sh: -------------------------------------------------------------------------------- 1 | # compile for version 2 | make 3 | if [ $? -ne 0 ]; then 4 | echo "make error" 5 | exit 1 6 | fi 7 | 8 | frp_version=`./bin/frps --version` 9 | echo "build version: $frp_version" 10 | 11 | # cross_compiles 12 | make -f ./Makefile.cross-compiles 13 | 14 | rm -rf ./release/packages 15 | mkdir -p ./release/packages 16 | 17 | os_all='linux windows darwin freebsd' 18 | arch_all='386 amd64 arm arm64 mips64 mips64le mips mipsle riscv64' 19 | 20 | cd ./release 21 | 22 | for os in $os_all; do 23 | for arch in $arch_all; do 24 | frp_dir_name="frp_${frp_version}_${os}_${arch}" 25 | frp_path="./packages/frp_${frp_version}_${os}_${arch}" 26 | 27 | if [ "x${os}" = x"windows" ]; then 28 | if [ ! -f "./frpc_${os}_${arch}.exe" ]; then 29 | continue 30 | fi 31 | if [ ! -f "./frps_${os}_${arch}.exe" ]; then 32 | continue 33 | fi 34 | mkdir ${frp_path} 35 | mv ./frpc_${os}_${arch}.exe ${frp_path}/frpc.exe 36 | mv ./frps_${os}_${arch}.exe ${frp_path}/frps.exe 37 | else 38 | if [ ! -f "./frpc_${os}_${arch}" ]; then 39 | continue 40 | fi 41 | if [ ! -f "./frps_${os}_${arch}" ]; then 42 | continue 43 | fi 44 | mkdir ${frp_path} 45 | mv ./frpc_${os}_${arch} ${frp_path}/frpc 46 | mv ./frps_${os}_${arch} ${frp_path}/frps 47 | fi 48 | cp ../LICENSE ${frp_path} 49 | cp -rf ../conf/* ${frp_path} 50 | 51 | # packages 52 | cd ./packages 53 | if [ "x${os}" = x"windows" ]; then 54 | zip -rq ${frp_dir_name}.zip ${frp_dir_name} 55 | else 56 | tar -zcf ${frp_dir_name}.tar.gz ${frp_dir_name} 57 | fi 58 | cd .. 59 | rm -rf ${frp_path} 60 | done 61 | done 62 | 63 | cd - 64 | -------------------------------------------------------------------------------- /frp-dev/pkg/config/README.md: -------------------------------------------------------------------------------- 1 | So far, there is no mature Go project that does well in parsing `*.ini` files. 2 | 3 | By comparison, we have selected an open source project: `https://github.com/go-ini/ini`. 4 | 5 | This library helped us solve most of the key-value matching, but there are still some problems, such as not supporting parsing `map`. 6 | 7 | We add our own logic on the basis of this library. In the current situationwhich, we need to complete the entire `Unmarshal` in two steps: 8 | 9 | * Step#1, use `go-ini` to complete the basic parameter matching; 10 | * Step#2, parse our custom parameters to realize parsing special structure, like `map`, `array`. 11 | 12 | Some of the keywords in `tag`(like inline, extends, etc.) may be different from standard libraries such as `json` and `protobuf` in Go. For details, please refer to the library documentation: https://ini.unknwon.io/docs/intro. -------------------------------------------------------------------------------- /frp-dev/pkg/config/parse.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The frp Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package config 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "os" 21 | "path/filepath" 22 | ) 23 | 24 | func ParseClientConfig(filePath string) ( 25 | cfg ClientCommonConf, 26 | pxyCfgs map[string]ProxyConf, 27 | visitorCfgs map[string]VisitorConf, 28 | err error, 29 | ) { 30 | var content []byte 31 | content, err = GetRenderedConfFromFile(filePath) 32 | if err != nil { 33 | return 34 | } 35 | configBuffer := bytes.NewBuffer(nil) 36 | configBuffer.Write(content) 37 | 38 | // Parse common section. 39 | cfg, err = UnmarshalClientConfFromIni(content) 40 | if err != nil { 41 | return 42 | } 43 | cfg.Complete() 44 | if err = cfg.Validate(); err != nil { 45 | err = fmt.Errorf("parse config error: %v", err) 46 | return 47 | } 48 | 49 | // Aggregate proxy configs from include files. 50 | var buf []byte 51 | buf, err = getIncludeContents(cfg.IncludeConfigFiles) 52 | if err != nil { 53 | err = fmt.Errorf("getIncludeContents error: %v", err) 54 | return 55 | } 56 | configBuffer.WriteString("\n") 57 | configBuffer.Write(buf) 58 | 59 | // Parse all proxy and visitor configs. 60 | pxyCfgs, visitorCfgs, err = LoadAllProxyConfsFromIni(cfg.User, configBuffer.Bytes(), cfg.Start) 61 | if err != nil { 62 | return 63 | } 64 | return 65 | } 66 | 67 | // getIncludeContents renders all configs from paths. 68 | // files format can be a single file path or directory or regex path. 69 | func getIncludeContents(paths []string) ([]byte, error) { 70 | out := bytes.NewBuffer(nil) 71 | for _, path := range paths { 72 | absDir, err := filepath.Abs(filepath.Dir(path)) 73 | if err != nil { 74 | return nil, err 75 | } 76 | if _, err := os.Stat(absDir); os.IsNotExist(err) { 77 | return nil, err 78 | } 79 | files, err := os.ReadDir(absDir) 80 | if err != nil { 81 | return nil, err 82 | } 83 | for _, fi := range files { 84 | if fi.IsDir() { 85 | continue 86 | } 87 | absFile := filepath.Join(absDir, fi.Name()) 88 | if matched, _ := filepath.Match(filepath.Join(absDir, filepath.Base(path)), absFile); matched { 89 | tmpContent, err := GetRenderedConfFromFile(absFile) 90 | if err != nil { 91 | return nil, fmt.Errorf("render extra config %s error: %v", absFile, err) 92 | } 93 | out.Write(tmpContent) 94 | out.WriteString("\n") 95 | } 96 | } 97 | } 98 | return out.Bytes(), nil 99 | } 100 | -------------------------------------------------------------------------------- /frp-dev/pkg/config/types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package config 16 | 17 | import ( 18 | "encoding/json" 19 | "errors" 20 | "strconv" 21 | "strings" 22 | ) 23 | 24 | const ( 25 | MB = 1024 * 1024 26 | KB = 1024 27 | ) 28 | 29 | type BandwidthQuantity struct { 30 | s string // MB or KB 31 | 32 | i int64 // bytes 33 | } 34 | 35 | func NewBandwidthQuantity(s string) (BandwidthQuantity, error) { 36 | q := BandwidthQuantity{} 37 | err := q.UnmarshalString(s) 38 | if err != nil { 39 | return q, err 40 | } 41 | return q, nil 42 | } 43 | 44 | func MustBandwidthQuantity(s string) BandwidthQuantity { 45 | q := BandwidthQuantity{} 46 | err := q.UnmarshalString(s) 47 | if err != nil { 48 | panic(err) 49 | } 50 | return q 51 | } 52 | 53 | func (q *BandwidthQuantity) Equal(u *BandwidthQuantity) bool { 54 | if q == nil && u == nil { 55 | return true 56 | } 57 | if q != nil && u != nil { 58 | return q.i == u.i 59 | } 60 | return false 61 | } 62 | 63 | func (q *BandwidthQuantity) String() string { 64 | return q.s 65 | } 66 | 67 | func (q *BandwidthQuantity) UnmarshalString(s string) error { 68 | s = strings.TrimSpace(s) 69 | if s == "" { 70 | return nil 71 | } 72 | 73 | var ( 74 | base int64 75 | f float64 76 | err error 77 | ) 78 | switch { 79 | case strings.HasSuffix(s, "MB"): 80 | base = MB 81 | fstr := strings.TrimSuffix(s, "MB") 82 | f, err = strconv.ParseFloat(fstr, 64) 83 | if err != nil { 84 | return err 85 | } 86 | case strings.HasSuffix(s, "KB"): 87 | base = KB 88 | fstr := strings.TrimSuffix(s, "KB") 89 | f, err = strconv.ParseFloat(fstr, 64) 90 | if err != nil { 91 | return err 92 | } 93 | default: 94 | return errors.New("unit not support") 95 | } 96 | 97 | q.s = s 98 | q.i = int64(f * float64(base)) 99 | return nil 100 | } 101 | 102 | func (q *BandwidthQuantity) UnmarshalJSON(b []byte) error { 103 | if len(b) == 4 && string(b) == "null" { 104 | return nil 105 | } 106 | 107 | var str string 108 | err := json.Unmarshal(b, &str) 109 | if err != nil { 110 | return err 111 | } 112 | 113 | return q.UnmarshalString(str) 114 | } 115 | 116 | func (q *BandwidthQuantity) MarshalJSON() ([]byte, error) { 117 | return []byte("\"" + q.s + "\""), nil 118 | } 119 | 120 | func (q *BandwidthQuantity) Bytes() int64 { 121 | return q.i 122 | } 123 | -------------------------------------------------------------------------------- /frp-dev/pkg/config/types_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package config 16 | 17 | import ( 18 | "encoding/json" 19 | "testing" 20 | 21 | "github.com/stretchr/testify/assert" 22 | ) 23 | 24 | type Wrap struct { 25 | B BandwidthQuantity `json:"b"` 26 | Int int `json:"int"` 27 | } 28 | 29 | func TestBandwidthQuantity(t *testing.T) { 30 | assert := assert.New(t) 31 | 32 | var w Wrap 33 | err := json.Unmarshal([]byte(`{"b":"1KB","int":5}`), &w) 34 | assert.NoError(err) 35 | assert.EqualValues(1*KB, w.B.Bytes()) 36 | 37 | buf, err := json.Marshal(&w) 38 | assert.NoError(err) 39 | assert.Equal(`{"b":"1KB","int":5}`, string(buf)) 40 | } 41 | -------------------------------------------------------------------------------- /frp-dev/pkg/config/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The frp Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package config 16 | 17 | import ( 18 | "strings" 19 | ) 20 | 21 | func GetMapWithoutPrefix(set map[string]string, prefix string) map[string]string { 22 | m := make(map[string]string) 23 | 24 | for key, value := range set { 25 | if strings.HasPrefix(key, prefix) { 26 | m[strings.TrimPrefix(key, prefix)] = value 27 | } 28 | } 29 | 30 | if len(m) == 0 { 31 | return nil 32 | } 33 | 34 | return m 35 | } 36 | 37 | func GetMapByPrefix(set map[string]string, prefix string) map[string]string { 38 | m := make(map[string]string) 39 | 40 | for key, value := range set { 41 | if strings.HasPrefix(key, prefix) { 42 | m[key] = value 43 | } 44 | } 45 | 46 | if len(m) == 0 { 47 | return nil 48 | } 49 | 50 | return m 51 | } 52 | -------------------------------------------------------------------------------- /frp-dev/pkg/config/value.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The frp Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package config 16 | 17 | import ( 18 | "bytes" 19 | "os" 20 | "strings" 21 | "text/template" 22 | ) 23 | 24 | var glbEnvs map[string]string 25 | 26 | func init() { 27 | glbEnvs = make(map[string]string) 28 | envs := os.Environ() 29 | for _, env := range envs { 30 | pair := strings.SplitN(env, "=", 2) 31 | if len(pair) != 2 { 32 | continue 33 | } 34 | glbEnvs[pair[0]] = pair[1] 35 | } 36 | } 37 | 38 | type Values struct { 39 | Envs map[string]string // environment vars 40 | } 41 | 42 | func GetValues() *Values { 43 | return &Values{ 44 | Envs: glbEnvs, 45 | } 46 | } 47 | 48 | func RenderContent(in []byte) (out []byte, err error) { 49 | tmpl, errRet := template.New("frp").Parse(string(in)) 50 | if errRet != nil { 51 | err = errRet 52 | return 53 | } 54 | 55 | buffer := bytes.NewBufferString("") 56 | v := GetValues() 57 | err = tmpl.Execute(buffer, v) 58 | if err != nil { 59 | return 60 | } 61 | out = buffer.Bytes() 62 | return 63 | } 64 | 65 | func GetRenderedConfFromFile(path string) (out []byte, err error) { 66 | var b []byte 67 | b, err = os.ReadFile(path) 68 | if err != nil { 69 | return 70 | } 71 | 72 | out, err = RenderContent(b) 73 | return 74 | } 75 | -------------------------------------------------------------------------------- /frp-dev/pkg/consts/consts.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package consts 16 | 17 | var ( 18 | // proxy status 19 | Idle = "idle" 20 | Working = "working" 21 | Closed = "closed" 22 | Online = "online" 23 | Offline = "offline" 24 | 25 | // proxy type 26 | TCPProxy = "tcp" 27 | UDPProxy = "udp" 28 | TCPMuxProxy = "tcpmux" 29 | HTTPProxy = "http" 30 | HTTPSProxy = "https" 31 | STCPProxy = "stcp" 32 | XTCPProxy = "xtcp" 33 | SUDPProxy = "sudp" 34 | 35 | // authentication method 36 | TokenAuthMethod = "token" 37 | OidcAuthMethod = "oidc" 38 | 39 | // TCP multiplexer 40 | HTTPConnectTCPMultiplexer = "httpconnect" 41 | ) 42 | -------------------------------------------------------------------------------- /frp-dev/pkg/errors/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package errors 16 | 17 | import ( 18 | "errors" 19 | ) 20 | 21 | var ( 22 | ErrMsgType = errors.New("message type error") 23 | ErrCtlClosed = errors.New("control is closed") 24 | ) 25 | -------------------------------------------------------------------------------- /frp-dev/pkg/metrics/aggregate/server.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package aggregate 16 | 17 | import ( 18 | "github.com/fatedier/frp/pkg/metrics/mem" 19 | "github.com/fatedier/frp/pkg/metrics/prometheus" 20 | "github.com/fatedier/frp/server/metrics" 21 | ) 22 | 23 | // EnableMem start to mark metrics to memory monitor system. 24 | func EnableMem() { 25 | sm.Add(mem.ServerMetrics) 26 | } 27 | 28 | // EnablePrometheus start to mark metrics to prometheus. 29 | func EnablePrometheus() { 30 | sm.Add(prometheus.ServerMetrics) 31 | } 32 | 33 | var sm = &serverMetrics{} 34 | 35 | func init() { 36 | metrics.Register(sm) 37 | } 38 | 39 | type serverMetrics struct { 40 | ms []metrics.ServerMetrics 41 | } 42 | 43 | func (m *serverMetrics) Add(sm metrics.ServerMetrics) { 44 | m.ms = append(m.ms, sm) 45 | } 46 | 47 | func (m *serverMetrics) NewClient() { 48 | for _, v := range m.ms { 49 | v.NewClient() 50 | } 51 | } 52 | 53 | func (m *serverMetrics) CloseClient() { 54 | for _, v := range m.ms { 55 | v.CloseClient() 56 | } 57 | } 58 | 59 | func (m *serverMetrics) NewProxy(name string, proxyType string) { 60 | for _, v := range m.ms { 61 | v.NewProxy(name, proxyType) 62 | } 63 | } 64 | 65 | func (m *serverMetrics) CloseProxy(name string, proxyType string) { 66 | for _, v := range m.ms { 67 | v.CloseProxy(name, proxyType) 68 | } 69 | } 70 | 71 | func (m *serverMetrics) OpenConnection(name string, proxyType string) { 72 | for _, v := range m.ms { 73 | v.OpenConnection(name, proxyType) 74 | } 75 | } 76 | 77 | func (m *serverMetrics) CloseConnection(name string, proxyType string) { 78 | for _, v := range m.ms { 79 | v.CloseConnection(name, proxyType) 80 | } 81 | } 82 | 83 | func (m *serverMetrics) AddTrafficIn(name string, proxyType string, trafficBytes int64) { 84 | for _, v := range m.ms { 85 | v.AddTrafficIn(name, proxyType, trafficBytes) 86 | } 87 | } 88 | 89 | func (m *serverMetrics) AddTrafficOut(name string, proxyType string, trafficBytes int64) { 90 | for _, v := range m.ms { 91 | v.AddTrafficOut(name, proxyType, trafficBytes) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /frp-dev/pkg/metrics/mem/types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package mem 16 | 17 | import ( 18 | "time" 19 | 20 | "github.com/fatedier/frp/pkg/util/metric" 21 | ) 22 | 23 | const ( 24 | ReserveDays = 7 25 | ) 26 | 27 | type ServerStats struct { 28 | TotalTrafficIn int64 29 | TotalTrafficOut int64 30 | CurConns int64 31 | ClientCounts int64 32 | ProxyTypeCounts map[string]int64 33 | } 34 | 35 | type ProxyStats struct { 36 | Name string 37 | Type string 38 | TodayTrafficIn int64 39 | TodayTrafficOut int64 40 | LastStartTime string 41 | LastCloseTime string 42 | CurConns int64 43 | } 44 | 45 | type ProxyTrafficInfo struct { 46 | Name string 47 | TrafficIn []int64 48 | TrafficOut []int64 49 | } 50 | 51 | type ProxyStatistics struct { 52 | Name string 53 | ProxyType string 54 | TrafficIn metric.DateCounter 55 | TrafficOut metric.DateCounter 56 | CurConns metric.Counter 57 | LastStartTime time.Time 58 | LastCloseTime time.Time 59 | } 60 | 61 | type ServerStatistics struct { 62 | TotalTrafficIn metric.DateCounter 63 | TotalTrafficOut metric.DateCounter 64 | CurConns metric.Counter 65 | 66 | // counter for clients 67 | ClientCounts metric.Counter 68 | 69 | // counter for proxy types 70 | ProxyTypeCounts map[string]metric.Counter 71 | 72 | // statistics for different proxies 73 | // key is proxy name 74 | ProxyStatistics map[string]*ProxyStatistics 75 | } 76 | 77 | type Collector interface { 78 | GetServer() *ServerStats 79 | GetProxiesByType(proxyType string) []*ProxyStats 80 | GetProxiesByTypeAndName(proxyType string, proxyName string) *ProxyStats 81 | GetProxyTraffic(name string) *ProxyTrafficInfo 82 | } 83 | -------------------------------------------------------------------------------- /frp-dev/pkg/metrics/metrics.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "github.com/fatedier/frp/pkg/metrics/aggregate" 5 | ) 6 | 7 | var ( 8 | EnableMem = aggregate.EnableMem 9 | EnablePrometheus = aggregate.EnablePrometheus 10 | ) 11 | -------------------------------------------------------------------------------- /frp-dev/pkg/metrics/prometheus/server.go: -------------------------------------------------------------------------------- 1 | package prometheus 2 | 3 | import ( 4 | "github.com/prometheus/client_golang/prometheus" 5 | 6 | "github.com/fatedier/frp/server/metrics" 7 | ) 8 | 9 | const ( 10 | namespace = "frp" 11 | serverSubsystem = "server" 12 | ) 13 | 14 | var ServerMetrics metrics.ServerMetrics = newServerMetrics() 15 | 16 | type serverMetrics struct { 17 | clientCount prometheus.Gauge 18 | proxyCount *prometheus.GaugeVec 19 | connectionCount *prometheus.GaugeVec 20 | trafficIn *prometheus.CounterVec 21 | trafficOut *prometheus.CounterVec 22 | } 23 | 24 | func (m *serverMetrics) NewClient() { 25 | m.clientCount.Inc() 26 | } 27 | 28 | func (m *serverMetrics) CloseClient() { 29 | m.clientCount.Dec() 30 | } 31 | 32 | func (m *serverMetrics) NewProxy(name string, proxyType string) { 33 | m.proxyCount.WithLabelValues(proxyType).Inc() 34 | } 35 | 36 | func (m *serverMetrics) CloseProxy(name string, proxyType string) { 37 | m.proxyCount.WithLabelValues(proxyType).Dec() 38 | } 39 | 40 | func (m *serverMetrics) OpenConnection(name string, proxyType string) { 41 | m.connectionCount.WithLabelValues(name, proxyType).Inc() 42 | } 43 | 44 | func (m *serverMetrics) CloseConnection(name string, proxyType string) { 45 | m.connectionCount.WithLabelValues(name, proxyType).Dec() 46 | } 47 | 48 | func (m *serverMetrics) AddTrafficIn(name string, proxyType string, trafficBytes int64) { 49 | m.trafficIn.WithLabelValues(name, proxyType).Add(float64(trafficBytes)) 50 | } 51 | 52 | func (m *serverMetrics) AddTrafficOut(name string, proxyType string, trafficBytes int64) { 53 | m.trafficOut.WithLabelValues(name, proxyType).Add(float64(trafficBytes)) 54 | } 55 | 56 | func newServerMetrics() *serverMetrics { 57 | m := &serverMetrics{ 58 | clientCount: prometheus.NewGauge(prometheus.GaugeOpts{ 59 | Namespace: namespace, 60 | Subsystem: serverSubsystem, 61 | Name: "client_counts", 62 | Help: "The current client counts of frps", 63 | }), 64 | proxyCount: prometheus.NewGaugeVec(prometheus.GaugeOpts{ 65 | Namespace: namespace, 66 | Subsystem: serverSubsystem, 67 | Name: "proxy_counts", 68 | Help: "The current proxy counts", 69 | }, []string{"type"}), 70 | connectionCount: prometheus.NewGaugeVec(prometheus.GaugeOpts{ 71 | Namespace: namespace, 72 | Subsystem: serverSubsystem, 73 | Name: "connection_counts", 74 | Help: "The current connection counts", 75 | }, []string{"name", "type"}), 76 | trafficIn: prometheus.NewCounterVec(prometheus.CounterOpts{ 77 | Namespace: namespace, 78 | Subsystem: serverSubsystem, 79 | Name: "traffic_in", 80 | Help: "The total in traffic", 81 | }, []string{"name", "type"}), 82 | trafficOut: prometheus.NewCounterVec(prometheus.CounterOpts{ 83 | Namespace: namespace, 84 | Subsystem: serverSubsystem, 85 | Name: "traffic_out", 86 | Help: "The total out traffic", 87 | }, []string{"name", "type"}), 88 | } 89 | prometheus.MustRegister(m.clientCount) 90 | prometheus.MustRegister(m.proxyCount) 91 | prometheus.MustRegister(m.connectionCount) 92 | prometheus.MustRegister(m.trafficIn) 93 | prometheus.MustRegister(m.trafficOut) 94 | return m 95 | } 96 | -------------------------------------------------------------------------------- /frp-dev/pkg/msg/ctl.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package msg 16 | 17 | import ( 18 | "io" 19 | 20 | jsonMsg "github.com/fatedier/golib/msg/json" 21 | ) 22 | 23 | type Message = jsonMsg.Message 24 | 25 | var msgCtl *jsonMsg.MsgCtl 26 | 27 | func init() { 28 | msgCtl = jsonMsg.NewMsgCtl() 29 | for typeByte, msg := range msgTypeMap { 30 | msgCtl.RegisterMsg(typeByte, msg) 31 | } 32 | } 33 | 34 | func ReadMsg(c io.Reader) (msg Message, err error) { 35 | return msgCtl.ReadMsg(c) 36 | } 37 | 38 | func ReadMsgInto(c io.Reader, msg Message) (err error) { 39 | return msgCtl.ReadMsgInto(c, msg) 40 | } 41 | 42 | func WriteMsg(c io.Writer, msg interface{}) (err error) { 43 | return msgCtl.WriteMsg(c, msg) 44 | } 45 | -------------------------------------------------------------------------------- /frp-dev/pkg/plugin/client/http2https.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package plugin 16 | 17 | import ( 18 | "crypto/tls" 19 | "fmt" 20 | "io" 21 | "net" 22 | "net/http" 23 | "net/http/httputil" 24 | "strings" 25 | 26 | frpNet "github.com/fatedier/frp/pkg/util/net" 27 | ) 28 | 29 | const PluginHTTP2HTTPS = "http2https" 30 | 31 | func init() { 32 | Register(PluginHTTP2HTTPS, NewHTTP2HTTPSPlugin) 33 | } 34 | 35 | type HTTP2HTTPSPlugin struct { 36 | hostHeaderRewrite string 37 | localAddr string 38 | headers map[string]string 39 | 40 | l *Listener 41 | s *http.Server 42 | } 43 | 44 | func NewHTTP2HTTPSPlugin(params map[string]string) (Plugin, error) { 45 | localAddr := params["plugin_local_addr"] 46 | hostHeaderRewrite := params["plugin_host_header_rewrite"] 47 | headers := make(map[string]string) 48 | for k, v := range params { 49 | if !strings.HasPrefix(k, "plugin_header_") { 50 | continue 51 | } 52 | if k = strings.TrimPrefix(k, "plugin_header_"); k != "" { 53 | headers[k] = v 54 | } 55 | } 56 | 57 | if localAddr == "" { 58 | return nil, fmt.Errorf("plugin_local_addr is required") 59 | } 60 | 61 | listener := NewProxyListener() 62 | 63 | p := &HTTP2HTTPSPlugin{ 64 | localAddr: localAddr, 65 | hostHeaderRewrite: hostHeaderRewrite, 66 | headers: headers, 67 | l: listener, 68 | } 69 | 70 | tr := &http.Transport{ 71 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 72 | } 73 | 74 | rp := &httputil.ReverseProxy{ 75 | Director: func(req *http.Request) { 76 | req.URL.Scheme = "https" 77 | req.URL.Host = p.localAddr 78 | if p.hostHeaderRewrite != "" { 79 | req.Host = p.hostHeaderRewrite 80 | } 81 | for k, v := range p.headers { 82 | req.Header.Set(k, v) 83 | } 84 | }, 85 | Transport: tr, 86 | } 87 | 88 | p.s = &http.Server{ 89 | Handler: rp, 90 | ReadHeaderTimeout: 0, 91 | } 92 | 93 | go func() { 94 | _ = p.s.Serve(listener) 95 | }() 96 | 97 | return p, nil 98 | } 99 | 100 | func (p *HTTP2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) { 101 | wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn) 102 | _ = p.l.PutConn(wrapConn) 103 | } 104 | 105 | func (p *HTTP2HTTPSPlugin) Name() string { 106 | return PluginHTTP2HTTPS 107 | } 108 | 109 | func (p *HTTP2HTTPSPlugin) Close() error { 110 | if err := p.s.Close(); err != nil { 111 | return err 112 | } 113 | return nil 114 | } 115 | -------------------------------------------------------------------------------- /frp-dev/pkg/plugin/client/plugin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package plugin 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "net" 21 | "sync" 22 | 23 | "github.com/fatedier/golib/errors" 24 | ) 25 | 26 | // Creators is used for create plugins to handle connections. 27 | var creators = make(map[string]CreatorFn) 28 | 29 | // params has prefix "plugin_" 30 | type CreatorFn func(params map[string]string) (Plugin, error) 31 | 32 | func Register(name string, fn CreatorFn) { 33 | creators[name] = fn 34 | } 35 | 36 | func Create(name string, params map[string]string) (p Plugin, err error) { 37 | if fn, ok := creators[name]; ok { 38 | p, err = fn(params) 39 | } else { 40 | err = fmt.Errorf("plugin [%s] is not registered", name) 41 | } 42 | return 43 | } 44 | 45 | type Plugin interface { 46 | Name() string 47 | 48 | // extraBufToLocal will send to local connection first, then join conn with local connection 49 | Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) 50 | Close() error 51 | } 52 | 53 | type Listener struct { 54 | conns chan net.Conn 55 | closed bool 56 | mu sync.Mutex 57 | } 58 | 59 | func NewProxyListener() *Listener { 60 | return &Listener{ 61 | conns: make(chan net.Conn, 64), 62 | } 63 | } 64 | 65 | func (l *Listener) Accept() (net.Conn, error) { 66 | conn, ok := <-l.conns 67 | if !ok { 68 | return nil, fmt.Errorf("listener closed") 69 | } 70 | return conn, nil 71 | } 72 | 73 | func (l *Listener) PutConn(conn net.Conn) error { 74 | err := errors.PanicToError(func() { 75 | l.conns <- conn 76 | }) 77 | return err 78 | } 79 | 80 | func (l *Listener) Close() error { 81 | l.mu.Lock() 82 | defer l.mu.Unlock() 83 | if !l.closed { 84 | close(l.conns) 85 | l.closed = true 86 | } 87 | return nil 88 | } 89 | 90 | func (l *Listener) Addr() net.Addr { 91 | return (*net.TCPAddr)(nil) 92 | } 93 | -------------------------------------------------------------------------------- /frp-dev/pkg/plugin/client/socks5.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package plugin 16 | 17 | import ( 18 | "io" 19 | "log" 20 | "net" 21 | 22 | gosocks5 "github.com/armon/go-socks5" 23 | 24 | frpNet "github.com/fatedier/frp/pkg/util/net" 25 | ) 26 | 27 | const PluginSocks5 = "socks5" 28 | 29 | func init() { 30 | Register(PluginSocks5, NewSocks5Plugin) 31 | } 32 | 33 | type Socks5Plugin struct { 34 | Server *gosocks5.Server 35 | } 36 | 37 | func NewSocks5Plugin(params map[string]string) (p Plugin, err error) { 38 | user := params["plugin_user"] 39 | passwd := params["plugin_passwd"] 40 | 41 | cfg := &gosocks5.Config{ 42 | Logger: log.New(io.Discard, "", log.LstdFlags), 43 | } 44 | if user != "" || passwd != "" { 45 | cfg.Credentials = gosocks5.StaticCredentials(map[string]string{user: passwd}) 46 | } 47 | sp := &Socks5Plugin{} 48 | sp.Server, err = gosocks5.New(cfg) 49 | p = sp 50 | return 51 | } 52 | 53 | func (sp *Socks5Plugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) { 54 | defer conn.Close() 55 | wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn) 56 | _ = sp.Server.ServeConn(wrapConn) 57 | } 58 | 59 | func (sp *Socks5Plugin) Name() string { 60 | return PluginSocks5 61 | } 62 | 63 | func (sp *Socks5Plugin) Close() error { 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /frp-dev/pkg/plugin/client/static_file.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package plugin 16 | 17 | import ( 18 | "io" 19 | "net" 20 | "net/http" 21 | 22 | "github.com/gorilla/mux" 23 | 24 | frpNet "github.com/fatedier/frp/pkg/util/net" 25 | ) 26 | 27 | const PluginStaticFile = "static_file" 28 | 29 | func init() { 30 | Register(PluginStaticFile, NewStaticFilePlugin) 31 | } 32 | 33 | type StaticFilePlugin struct { 34 | localPath string 35 | stripPrefix string 36 | httpUser string 37 | httpPasswd string 38 | 39 | l *Listener 40 | s *http.Server 41 | } 42 | 43 | func NewStaticFilePlugin(params map[string]string) (Plugin, error) { 44 | localPath := params["plugin_local_path"] 45 | stripPrefix := params["plugin_strip_prefix"] 46 | httpUser := params["plugin_http_user"] 47 | httpPasswd := params["plugin_http_passwd"] 48 | 49 | listener := NewProxyListener() 50 | 51 | sp := &StaticFilePlugin{ 52 | localPath: localPath, 53 | stripPrefix: stripPrefix, 54 | httpUser: httpUser, 55 | httpPasswd: httpPasswd, 56 | 57 | l: listener, 58 | } 59 | var prefix string 60 | if stripPrefix != "" { 61 | prefix = "/" + stripPrefix + "/" 62 | } else { 63 | prefix = "/" 64 | } 65 | 66 | router := mux.NewRouter() 67 | router.Use(frpNet.NewHTTPAuthMiddleware(httpUser, httpPasswd).Middleware) 68 | router.PathPrefix(prefix).Handler(frpNet.MakeHTTPGzipHandler(http.StripPrefix(prefix, http.FileServer(http.Dir(localPath))))).Methods("GET") 69 | sp.s = &http.Server{ 70 | Handler: router, 71 | } 72 | go func() { 73 | _ = sp.s.Serve(listener) 74 | }() 75 | return sp, nil 76 | } 77 | 78 | func (sp *StaticFilePlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) { 79 | wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn) 80 | _ = sp.l.PutConn(wrapConn) 81 | } 82 | 83 | func (sp *StaticFilePlugin) Name() string { 84 | return PluginStaticFile 85 | } 86 | 87 | func (sp *StaticFilePlugin) Close() error { 88 | sp.s.Close() 89 | sp.l.Close() 90 | return nil 91 | } 92 | -------------------------------------------------------------------------------- /frp-dev/pkg/plugin/client/unix_domain_socket.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package plugin 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "net" 21 | 22 | frpIo "github.com/fatedier/golib/io" 23 | ) 24 | 25 | const PluginUnixDomainSocket = "unix_domain_socket" 26 | 27 | func init() { 28 | Register(PluginUnixDomainSocket, NewUnixDomainSocketPlugin) 29 | } 30 | 31 | type UnixDomainSocketPlugin struct { 32 | UnixAddr *net.UnixAddr 33 | } 34 | 35 | func NewUnixDomainSocketPlugin(params map[string]string) (p Plugin, err error) { 36 | unixPath, ok := params["plugin_unix_path"] 37 | if !ok { 38 | err = fmt.Errorf("plugin_unix_path not found") 39 | return 40 | } 41 | 42 | unixAddr, errRet := net.ResolveUnixAddr("unix", unixPath) 43 | if errRet != nil { 44 | err = errRet 45 | return 46 | } 47 | 48 | p = &UnixDomainSocketPlugin{ 49 | UnixAddr: unixAddr, 50 | } 51 | return 52 | } 53 | 54 | func (uds *UnixDomainSocketPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) { 55 | localConn, err := net.DialUnix("unix", nil, uds.UnixAddr) 56 | if err != nil { 57 | return 58 | } 59 | if len(extraBufToLocal) > 0 { 60 | if _, err := localConn.Write(extraBufToLocal); err != nil { 61 | return 62 | } 63 | } 64 | 65 | frpIo.Join(localConn, conn) 66 | } 67 | 68 | func (uds *UnixDomainSocketPlugin) Name() string { 69 | return PluginUnixDomainSocket 70 | } 71 | 72 | func (uds *UnixDomainSocketPlugin) Close() error { 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /frp-dev/pkg/plugin/server/plugin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package plugin 16 | 17 | import ( 18 | "context" 19 | ) 20 | 21 | const ( 22 | APIVersion = "0.1.0" 23 | 24 | OpLogin = "Login" 25 | OpNewProxy = "NewProxy" 26 | OpCloseProxy = "CloseProxy" 27 | OpPing = "Ping" 28 | OpNewWorkConn = "NewWorkConn" 29 | OpNewUserConn = "NewUserConn" 30 | ) 31 | 32 | type Plugin interface { 33 | Name() string 34 | IsSupport(op string) bool 35 | Handle(ctx context.Context, op string, content interface{}) (res *Response, retContent interface{}, err error) 36 | } 37 | -------------------------------------------------------------------------------- /frp-dev/pkg/plugin/server/tracer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package plugin 16 | 17 | import ( 18 | "context" 19 | ) 20 | 21 | type key int 22 | 23 | const ( 24 | reqidKey key = 0 25 | ) 26 | 27 | func NewReqidContext(ctx context.Context, reqid string) context.Context { 28 | return context.WithValue(ctx, reqidKey, reqid) 29 | } 30 | 31 | func GetReqidFromContext(ctx context.Context) string { 32 | ret, _ := ctx.Value(reqidKey).(string) 33 | return ret 34 | } 35 | -------------------------------------------------------------------------------- /frp-dev/pkg/plugin/server/types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package plugin 16 | 17 | import ( 18 | "github.com/fatedier/frp/pkg/msg" 19 | ) 20 | 21 | type Request struct { 22 | Version string `json:"version"` 23 | Op string `json:"op"` 24 | Content interface{} `json:"content"` 25 | } 26 | 27 | type Response struct { 28 | Reject bool `json:"reject"` 29 | RejectReason string `json:"reject_reason"` 30 | Unchange bool `json:"unchange"` 31 | Content interface{} `json:"content"` 32 | } 33 | 34 | type LoginContent struct { 35 | msg.Login 36 | 37 | ClientAddress string `json:"client_address,omitempty"` 38 | } 39 | 40 | type UserInfo struct { 41 | User string `json:"user"` 42 | Metas map[string]string `json:"metas"` 43 | RunID string `json:"run_id"` 44 | } 45 | 46 | type NewProxyContent struct { 47 | User UserInfo `json:"user"` 48 | msg.NewProxy 49 | } 50 | 51 | type CloseProxyContent struct { 52 | User UserInfo `json:"user"` 53 | msg.CloseProxy 54 | } 55 | 56 | type PingContent struct { 57 | User UserInfo `json:"user"` 58 | msg.Ping 59 | } 60 | 61 | type NewWorkConnContent struct { 62 | User UserInfo `json:"user"` 63 | msg.NewWorkConn 64 | } 65 | 66 | type NewUserConnContent struct { 67 | User UserInfo `json:"user"` 68 | ProxyName string `json:"proxy_name"` 69 | ProxyType string `json:"proxy_type"` 70 | RemoteAddr string `json:"remote_addr"` 71 | } 72 | -------------------------------------------------------------------------------- /frp-dev/pkg/proto/udp/udp_test.go: -------------------------------------------------------------------------------- 1 | package udp 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestUdpPacket(t *testing.T) { 10 | assert := assert.New(t) 11 | 12 | buf := []byte("hello world") 13 | udpMsg := NewUDPPacket(buf, nil, nil) 14 | 15 | newBuf, err := GetContent(udpMsg) 16 | assert.NoError(err) 17 | assert.EqualValues(buf, newBuf) 18 | } 19 | -------------------------------------------------------------------------------- /frp-dev/pkg/transport/tls.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/rsa" 6 | "crypto/tls" 7 | "crypto/x509" 8 | "encoding/pem" 9 | "math/big" 10 | "os" 11 | ) 12 | 13 | func newCustomTLSKeyPair(certfile, keyfile string) (*tls.Certificate, error) { 14 | tlsCert, err := tls.LoadX509KeyPair(certfile, keyfile) 15 | if err != nil { 16 | return nil, err 17 | } 18 | return &tlsCert, nil 19 | } 20 | 21 | func newRandomTLSKeyPair() *tls.Certificate { 22 | key, err := rsa.GenerateKey(rand.Reader, 2048) 23 | if err != nil { 24 | panic(err) 25 | } 26 | template := x509.Certificate{SerialNumber: big.NewInt(1)} 27 | certDER, err := x509.CreateCertificate( 28 | rand.Reader, 29 | &template, 30 | &template, 31 | &key.PublicKey, 32 | key) 33 | if err != nil { 34 | panic(err) 35 | } 36 | keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}) 37 | certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) 38 | 39 | tlsCert, err := tls.X509KeyPair(certPEM, keyPEM) 40 | if err != nil { 41 | panic(err) 42 | } 43 | return &tlsCert 44 | } 45 | 46 | // Only support one ca file to add 47 | func newCertPool(caPath string) (*x509.CertPool, error) { 48 | pool := x509.NewCertPool() 49 | 50 | caCrt, err := os.ReadFile(caPath) 51 | if err != nil { 52 | return nil, err 53 | } 54 | 55 | pool.AppendCertsFromPEM(caCrt) 56 | 57 | return pool, nil 58 | } 59 | 60 | func NewServerTLSConfig(certPath, keyPath, caPath string) (*tls.Config, error) { 61 | base := &tls.Config{} 62 | 63 | if certPath == "" || keyPath == "" { 64 | // server will generate tls conf by itself 65 | cert := newRandomTLSKeyPair() 66 | base.Certificates = []tls.Certificate{*cert} 67 | } else { 68 | cert, err := newCustomTLSKeyPair(certPath, keyPath) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | base.Certificates = []tls.Certificate{*cert} 74 | } 75 | 76 | if caPath != "" { 77 | pool, err := newCertPool(caPath) 78 | if err != nil { 79 | return nil, err 80 | } 81 | 82 | base.ClientAuth = tls.RequireAndVerifyClientCert 83 | base.ClientCAs = pool 84 | } 85 | 86 | return base, nil 87 | } 88 | 89 | func NewClientTLSConfig(certPath, keyPath, caPath, serverName string) (*tls.Config, error) { 90 | base := &tls.Config{} 91 | 92 | if certPath == "" || keyPath == "" { 93 | // client will not generate tls conf by itself 94 | } else { 95 | cert, err := newCustomTLSKeyPair(certPath, keyPath) 96 | if err != nil { 97 | return nil, err 98 | } 99 | 100 | base.Certificates = []tls.Certificate{*cert} 101 | } 102 | 103 | base.ServerName = serverName 104 | 105 | if caPath != "" { 106 | pool, err := newCertPool(caPath) 107 | if err != nil { 108 | return nil, err 109 | } 110 | 111 | base.RootCAs = pool 112 | base.InsecureSkipVerify = false 113 | } else { 114 | base.InsecureSkipVerify = true 115 | } 116 | 117 | return base, nil 118 | } 119 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/limit/reader.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package limit 16 | 17 | import ( 18 | "context" 19 | "io" 20 | 21 | "golang.org/x/time/rate" 22 | ) 23 | 24 | type Reader struct { 25 | r io.Reader 26 | limiter *rate.Limiter 27 | } 28 | 29 | func NewReader(r io.Reader, limiter *rate.Limiter) *Reader { 30 | return &Reader{ 31 | r: r, 32 | limiter: limiter, 33 | } 34 | } 35 | 36 | func (r *Reader) Read(p []byte) (n int, err error) { 37 | b := r.limiter.Burst() 38 | if b < len(p) { 39 | p = p[:b] 40 | } 41 | n, err = r.r.Read(p) 42 | if err != nil { 43 | return 44 | } 45 | 46 | err = r.limiter.WaitN(context.Background(), n) 47 | if err != nil { 48 | return 49 | } 50 | return 51 | } 52 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/limit/writer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package limit 16 | 17 | import ( 18 | "context" 19 | "io" 20 | 21 | "golang.org/x/time/rate" 22 | ) 23 | 24 | type Writer struct { 25 | w io.Writer 26 | limiter *rate.Limiter 27 | } 28 | 29 | func NewWriter(w io.Writer, limiter *rate.Limiter) *Writer { 30 | return &Writer{ 31 | w: w, 32 | limiter: limiter, 33 | } 34 | } 35 | 36 | func (w *Writer) Write(p []byte) (n int, err error) { 37 | var nn int 38 | b := w.limiter.Burst() 39 | for { 40 | end := len(p) 41 | if end == 0 { 42 | break 43 | } 44 | if b < len(p) { 45 | end = b 46 | } 47 | err = w.limiter.WaitN(context.Background(), end) 48 | if err != nil { 49 | return 50 | } 51 | 52 | nn, err = w.w.Write(p[:end]) 53 | n += nn 54 | if err != nil { 55 | return 56 | } 57 | p = p[end:] 58 | } 59 | return 60 | } 61 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/log/log.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package log 16 | 17 | import ( 18 | "fmt" 19 | 20 | "github.com/fatedier/beego/logs" 21 | ) 22 | 23 | // Log is the under log object 24 | var Log *logs.BeeLogger 25 | 26 | func init() { 27 | Log = logs.NewLogger(200) 28 | Log.EnableFuncCallDepth(true) 29 | Log.SetLogFuncCallDepth(Log.GetLogFuncCallDepth() + 1) 30 | } 31 | 32 | func InitLog(logWay string, logFile string, logLevel string, maxdays int64, disableLogColor bool) { 33 | SetLogFile(logWay, logFile, maxdays, disableLogColor) 34 | SetLogLevel(logLevel) 35 | } 36 | 37 | // SetLogFile to configure log params 38 | // logWay: file or console 39 | func SetLogFile(logWay string, logFile string, maxdays int64, disableLogColor bool) { 40 | if logWay == "console" { 41 | params := "" 42 | if disableLogColor { 43 | params = `{"color": false}` 44 | } 45 | _ = Log.SetLogger("console", params) 46 | } else { 47 | params := fmt.Sprintf(`{"filename": "%s", "maxdays": %d}`, logFile, maxdays) 48 | _ = Log.SetLogger("file", params) 49 | } 50 | } 51 | 52 | // SetLogLevel set log level, default is warning 53 | // value: error, warning, info, debug, trace 54 | func SetLogLevel(logLevel string) { 55 | var level int 56 | switch logLevel { 57 | case "error": 58 | level = 3 59 | case "warn": 60 | level = 4 61 | case "info": 62 | level = 6 63 | case "debug": 64 | level = 7 65 | case "trace": 66 | level = 8 67 | default: 68 | level = 4 // warning 69 | } 70 | Log.SetLevel(level) 71 | } 72 | 73 | // wrap log 74 | 75 | func Error(format string, v ...interface{}) { 76 | Log.Error(format, v...) 77 | } 78 | 79 | func Warn(format string, v ...interface{}) { 80 | Log.Warn(format, v...) 81 | } 82 | 83 | func Info(format string, v ...interface{}) { 84 | Log.Info(format, v...) 85 | } 86 | 87 | func Debug(format string, v ...interface{}) { 88 | Log.Debug(format, v...) 89 | } 90 | 91 | func Trace(format string, v ...interface{}) { 92 | Log.Trace(format, v...) 93 | } 94 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/metric/counter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metric 16 | 17 | import ( 18 | "sync/atomic" 19 | ) 20 | 21 | type Counter interface { 22 | Count() int32 23 | Inc(int32) 24 | Dec(int32) 25 | Snapshot() Counter 26 | Clear() 27 | } 28 | 29 | func NewCounter() Counter { 30 | return &StandardCounter{ 31 | count: 0, 32 | } 33 | } 34 | 35 | type StandardCounter struct { 36 | count int32 37 | } 38 | 39 | func (c *StandardCounter) Count() int32 { 40 | return atomic.LoadInt32(&c.count) 41 | } 42 | 43 | func (c *StandardCounter) Inc(count int32) { 44 | atomic.AddInt32(&c.count, count) 45 | } 46 | 47 | func (c *StandardCounter) Dec(count int32) { 48 | atomic.AddInt32(&c.count, -count) 49 | } 50 | 51 | func (c *StandardCounter) Snapshot() Counter { 52 | tmp := &StandardCounter{ 53 | count: atomic.LoadInt32(&c.count), 54 | } 55 | return tmp 56 | } 57 | 58 | func (c *StandardCounter) Clear() { 59 | atomic.StoreInt32(&c.count, 0) 60 | } 61 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/metric/counter_test.go: -------------------------------------------------------------------------------- 1 | package metric 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestCounter(t *testing.T) { 10 | assert := assert.New(t) 11 | c := NewCounter() 12 | c.Inc(10) 13 | assert.EqualValues(10, c.Count()) 14 | 15 | c.Dec(5) 16 | assert.EqualValues(5, c.Count()) 17 | 18 | cTmp := c.Snapshot() 19 | assert.EqualValues(5, cTmp.Count()) 20 | 21 | c.Clear() 22 | assert.EqualValues(0, c.Count()) 23 | } 24 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/metric/date_counter_test.go: -------------------------------------------------------------------------------- 1 | package metric 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestDateCounter(t *testing.T) { 10 | assert := assert.New(t) 11 | 12 | dc := NewDateCounter(3) 13 | dc.Inc(10) 14 | assert.EqualValues(10, dc.TodayCount()) 15 | 16 | dc.Dec(5) 17 | assert.EqualValues(5, dc.TodayCount()) 18 | 19 | counts := dc.GetLastDaysCount(3) 20 | assert.EqualValues(3, len(counts)) 21 | assert.EqualValues(5, counts[0]) 22 | assert.EqualValues(0, counts[1]) 23 | assert.EqualValues(0, counts[2]) 24 | 25 | dcTmp := dc.Snapshot() 26 | assert.EqualValues(5, dcTmp.TodayCount()) 27 | } 28 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/metric/metrics.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package metric 16 | 17 | // GaugeMetric represents a single numerical value that can arbitrarily go up 18 | // and down. 19 | type GaugeMetric interface { 20 | Inc() 21 | Dec() 22 | Set(float64) 23 | } 24 | 25 | // CounterMetric represents a single numerical value that only ever 26 | // goes up. 27 | type CounterMetric interface { 28 | Inc() 29 | } 30 | 31 | // HistogramMetric counts individual observations. 32 | type HistogramMetric interface { 33 | Observe(float64) 34 | } 35 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/net/dial.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "net/url" 7 | 8 | libdial "github.com/fatedier/golib/net/dial" 9 | "golang.org/x/net/websocket" 10 | ) 11 | 12 | func DialHookCustomTLSHeadByte(enableTLS bool, disableCustomTLSHeadByte bool) libdial.AfterHookFunc { 13 | return func(ctx context.Context, c net.Conn, addr string) (context.Context, net.Conn, error) { 14 | if enableTLS && !disableCustomTLSHeadByte { 15 | _, err := c.Write([]byte{byte(FRPTLSHeadByte)}) 16 | if err != nil { 17 | return nil, nil, err 18 | } 19 | } 20 | return ctx, c, nil 21 | } 22 | } 23 | 24 | func DialHookWebsocket() libdial.AfterHookFunc { 25 | return func(ctx context.Context, c net.Conn, addr string) (context.Context, net.Conn, error) { 26 | addr = "ws://" + addr + FrpWebsocketPath 27 | uri, err := url.Parse(addr) 28 | if err != nil { 29 | return nil, nil, err 30 | } 31 | 32 | origin := "http://" + uri.Host 33 | cfg, err := websocket.NewConfig(addr, origin) 34 | if err != nil { 35 | return nil, nil, err 36 | } 37 | 38 | conn, err := websocket.NewClient(cfg, c) 39 | if err != nil { 40 | return nil, nil, err 41 | } 42 | return ctx, conn, nil 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/net/kcp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package net 16 | 17 | import ( 18 | "fmt" 19 | "net" 20 | 21 | kcp "github.com/fatedier/kcp-go" 22 | ) 23 | 24 | type KCPListener struct { 25 | listener net.Listener 26 | acceptCh chan net.Conn 27 | closeFlag bool 28 | } 29 | 30 | func ListenKcp(address string) (l *KCPListener, err error) { 31 | listener, err := kcp.ListenWithOptions(address, nil, 10, 3) 32 | if err != nil { 33 | return l, err 34 | } 35 | _ = listener.SetReadBuffer(4194304) 36 | _ = listener.SetWriteBuffer(4194304) 37 | 38 | l = &KCPListener{ 39 | listener: listener, 40 | acceptCh: make(chan net.Conn), 41 | closeFlag: false, 42 | } 43 | 44 | go func() { 45 | for { 46 | conn, err := listener.AcceptKCP() 47 | if err != nil { 48 | if l.closeFlag { 49 | close(l.acceptCh) 50 | return 51 | } 52 | continue 53 | } 54 | conn.SetStreamMode(true) 55 | conn.SetWriteDelay(true) 56 | conn.SetNoDelay(1, 20, 2, 1) 57 | conn.SetMtu(1350) 58 | conn.SetWindowSize(1024, 1024) 59 | conn.SetACKNoDelay(false) 60 | 61 | l.acceptCh <- conn 62 | } 63 | }() 64 | return l, err 65 | } 66 | 67 | func (l *KCPListener) Accept() (net.Conn, error) { 68 | conn, ok := <-l.acceptCh 69 | if !ok { 70 | return conn, fmt.Errorf("channel for kcp listener closed") 71 | } 72 | return conn, nil 73 | } 74 | 75 | func (l *KCPListener) Close() error { 76 | if !l.closeFlag { 77 | l.closeFlag = true 78 | l.listener.Close() 79 | } 80 | return nil 81 | } 82 | 83 | func (l *KCPListener) Addr() net.Addr { 84 | return l.listener.Addr() 85 | } 86 | 87 | func NewKCPConnFromUDP(conn *net.UDPConn, connected bool, raddr string) (net.Conn, error) { 88 | kcpConn, err := kcp.NewConnEx(1, connected, raddr, nil, 10, 3, conn) 89 | if err != nil { 90 | return nil, err 91 | } 92 | kcpConn.SetStreamMode(true) 93 | kcpConn.SetWriteDelay(true) 94 | kcpConn.SetNoDelay(1, 20, 2, 1) 95 | kcpConn.SetMtu(1350) 96 | kcpConn.SetWindowSize(1024, 1024) 97 | kcpConn.SetACKNoDelay(false) 98 | return kcpConn, nil 99 | } 100 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/net/listener.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package net 16 | 17 | import ( 18 | "fmt" 19 | "net" 20 | "sync" 21 | 22 | "github.com/fatedier/golib/errors" 23 | ) 24 | 25 | // Custom listener 26 | type CustomListener struct { 27 | acceptCh chan net.Conn 28 | closed bool 29 | mu sync.Mutex 30 | } 31 | 32 | func NewCustomListener() *CustomListener { 33 | return &CustomListener{ 34 | acceptCh: make(chan net.Conn, 64), 35 | } 36 | } 37 | 38 | func (l *CustomListener) Accept() (net.Conn, error) { 39 | conn, ok := <-l.acceptCh 40 | if !ok { 41 | return nil, fmt.Errorf("listener closed") 42 | } 43 | return conn, nil 44 | } 45 | 46 | func (l *CustomListener) PutConn(conn net.Conn) error { 47 | err := errors.PanicToError(func() { 48 | select { 49 | case l.acceptCh <- conn: 50 | default: 51 | conn.Close() 52 | } 53 | }) 54 | return err 55 | } 56 | 57 | func (l *CustomListener) Close() error { 58 | l.mu.Lock() 59 | defer l.mu.Unlock() 60 | if !l.closed { 61 | close(l.acceptCh) 62 | l.closed = true 63 | } 64 | return nil 65 | } 66 | 67 | func (l *CustomListener) Addr() net.Addr { 68 | return (*net.TCPAddr)(nil) 69 | } 70 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/net/tls.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package net 16 | 17 | import ( 18 | "crypto/tls" 19 | "fmt" 20 | "net" 21 | "time" 22 | 23 | gnet "github.com/fatedier/golib/net" 24 | ) 25 | 26 | var FRPTLSHeadByte = 0x17 27 | 28 | func CheckAndEnableTLSServerConnWithTimeout( 29 | c net.Conn, tlsConfig *tls.Config, tlsOnly bool, timeout time.Duration, 30 | ) (out net.Conn, isTLS bool, custom bool, err error) { 31 | sc, r := gnet.NewSharedConnSize(c, 2) 32 | buf := make([]byte, 1) 33 | var n int 34 | _ = c.SetReadDeadline(time.Now().Add(timeout)) 35 | n, err = r.Read(buf) 36 | _ = c.SetReadDeadline(time.Time{}) 37 | if err != nil { 38 | return 39 | } 40 | 41 | switch { 42 | case n == 1 && int(buf[0]) == FRPTLSHeadByte: 43 | out = tls.Server(c, tlsConfig) 44 | isTLS = true 45 | custom = true 46 | case n == 1 && int(buf[0]) == 0x16: 47 | out = tls.Server(sc, tlsConfig) 48 | isTLS = true 49 | default: 50 | if tlsOnly { 51 | err = fmt.Errorf("non-TLS connection received on a TlsOnly server") 52 | return 53 | } 54 | out = sc 55 | } 56 | return 57 | } 58 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/net/websocket.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | "net/http" 7 | "strconv" 8 | 9 | "golang.org/x/net/websocket" 10 | ) 11 | 12 | var ErrWebsocketListenerClosed = errors.New("websocket listener closed") 13 | 14 | const ( 15 | FrpWebsocketPath = "/fwssp" 16 | ) 17 | 18 | type WebsocketListener struct { 19 | ln net.Listener 20 | acceptCh chan net.Conn 21 | 22 | server *http.Server 23 | } 24 | 25 | // NewWebsocketListener to handle websocket connections 26 | // ln: tcp listener for websocket connections 27 | func NewWebsocketListener(ln net.Listener) (wl *WebsocketListener) { 28 | wl = &WebsocketListener{ 29 | acceptCh: make(chan net.Conn), 30 | } 31 | 32 | muxer := http.NewServeMux() 33 | muxer.Handle(FrpWebsocketPath, websocket.Handler(func(c *websocket.Conn) { 34 | notifyCh := make(chan struct{}) 35 | conn := WrapCloseNotifyConn(c, func() { 36 | close(notifyCh) 37 | }) 38 | wl.acceptCh <- conn 39 | <-notifyCh 40 | })) 41 | 42 | wl.server = &http.Server{ 43 | Addr: ln.Addr().String(), 44 | Handler: muxer, 45 | } 46 | 47 | go func() { 48 | _ = wl.server.Serve(ln) 49 | }() 50 | return 51 | } 52 | 53 | func ListenWebsocket(bindAddr string, bindPort int) (*WebsocketListener, error) { 54 | tcpLn, err := net.Listen("tcp", net.JoinHostPort(bindAddr, strconv.Itoa(bindPort))) 55 | if err != nil { 56 | return nil, err 57 | } 58 | l := NewWebsocketListener(tcpLn) 59 | return l, nil 60 | } 61 | 62 | func (p *WebsocketListener) Accept() (net.Conn, error) { 63 | c, ok := <-p.acceptCh 64 | if !ok { 65 | return nil, ErrWebsocketListenerClosed 66 | } 67 | return c, nil 68 | } 69 | 70 | func (p *WebsocketListener) Close() error { 71 | return p.server.Close() 72 | } 73 | 74 | func (p *WebsocketListener) Addr() net.Addr { 75 | return p.ln.Addr() 76 | } 77 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/util/http.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 guylewin, guy@lewin.co.il 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "encoding/base64" 19 | "net" 20 | "net/http" 21 | "strings" 22 | ) 23 | 24 | func OkResponse() *http.Response { 25 | header := make(http.Header) 26 | 27 | res := &http.Response{ 28 | Status: "OK", 29 | StatusCode: 200, 30 | Proto: "HTTP/1.1", 31 | ProtoMajor: 1, 32 | ProtoMinor: 1, 33 | Header: header, 34 | } 35 | return res 36 | } 37 | 38 | func ProxyUnauthorizedResponse() *http.Response { 39 | header := make(http.Header) 40 | header.Set("Proxy-Authenticate", `Basic realm="Restricted"`) 41 | res := &http.Response{ 42 | Status: "Proxy Authentication Required", 43 | StatusCode: 407, 44 | Proto: "HTTP/1.1", 45 | ProtoMajor: 1, 46 | ProtoMinor: 1, 47 | Header: header, 48 | } 49 | return res 50 | } 51 | 52 | // canonicalHost strips port from host if present and returns the canonicalized 53 | // host name. 54 | func CanonicalHost(host string) (string, error) { 55 | var err error 56 | host = strings.ToLower(host) 57 | if hasPort(host) { 58 | host, _, err = net.SplitHostPort(host) 59 | if err != nil { 60 | return "", err 61 | } 62 | } 63 | // Strip trailing dot from fully qualified domain names. 64 | host = strings.TrimSuffix(host, ".") 65 | return host, nil 66 | } 67 | 68 | // hasPort reports whether host contains a port number. host may be a host 69 | // name, an IPv4 or an IPv6 address. 70 | func hasPort(host string) bool { 71 | colons := strings.Count(host, ":") 72 | if colons == 0 { 73 | return false 74 | } 75 | if colons == 1 { 76 | return true 77 | } 78 | return host[0] == '[' && strings.Contains(host, "]:") 79 | } 80 | 81 | func ParseBasicAuth(auth string) (username, password string, ok bool) { 82 | const prefix = "Basic " 83 | // Case insensitive prefix match. See Issue 22736. 84 | if len(auth) < len(prefix) || !strings.EqualFold(auth[:len(prefix)], prefix) { 85 | return 86 | } 87 | c, err := base64.StdEncoding.DecodeString(auth[len(prefix):]) 88 | if err != nil { 89 | return 90 | } 91 | cs := string(c) 92 | s := strings.IndexByte(cs, ':') 93 | if s < 0 { 94 | return 95 | } 96 | return cs[:s], cs[s+1:], true 97 | } 98 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/util/util_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestRandId(t *testing.T) { 10 | assert := assert.New(t) 11 | id, err := RandID() 12 | assert.NoError(err) 13 | t.Log(id) 14 | assert.Equal(16, len(id)) 15 | } 16 | 17 | func TestGetAuthKey(t *testing.T) { 18 | assert := assert.New(t) 19 | key := GetAuthKey("1234", 1488720000) 20 | t.Log(key) 21 | assert.Equal("6df41a43725f0c770fd56379e12acf8c", key) 22 | } 23 | 24 | func TestParseRangeNumbers(t *testing.T) { 25 | assert := assert.New(t) 26 | numbers, err := ParseRangeNumbers("2-5") 27 | if assert.NoError(err) { 28 | assert.Equal([]int64{2, 3, 4, 5}, numbers) 29 | } 30 | 31 | numbers, err = ParseRangeNumbers("1") 32 | if assert.NoError(err) { 33 | assert.Equal([]int64{1}, numbers) 34 | } 35 | 36 | numbers, err = ParseRangeNumbers("3-5,8") 37 | if assert.NoError(err) { 38 | assert.Equal([]int64{3, 4, 5, 8}, numbers) 39 | } 40 | 41 | numbers, err = ParseRangeNumbers(" 3-5,8, 10-12 ") 42 | if assert.NoError(err) { 43 | assert.Equal([]int64{3, 4, 5, 8, 10, 11, 12}, numbers) 44 | } 45 | 46 | _, err = ParseRangeNumbers("3-a") 47 | assert.Error(err) 48 | } 49 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/version/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package version 16 | 17 | import ( 18 | "strconv" 19 | "strings" 20 | ) 21 | 22 | var version = "0.44.0" 23 | 24 | func Full() string { 25 | return version 26 | } 27 | 28 | func getSubVersion(v string, position int) int64 { 29 | arr := strings.Split(v, ".") 30 | if len(arr) < 3 { 31 | return 0 32 | } 33 | res, _ := strconv.ParseInt(arr[position], 10, 64) 34 | return res 35 | } 36 | 37 | func Proto(v string) int64 { 38 | return getSubVersion(v, 0) 39 | } 40 | 41 | func Major(v string) int64 { 42 | return getSubVersion(v, 1) 43 | } 44 | 45 | func Minor(v string) int64 { 46 | return getSubVersion(v, 2) 47 | } 48 | 49 | // add every case there if server will not accept client's protocol and return false 50 | func Compat(client string) (ok bool, msg string) { 51 | if LessThan(client, "0.18.0") { 52 | return false, "Please upgrade your frpc version to at least 0.18.0" 53 | } 54 | return true, "" 55 | } 56 | 57 | func LessThan(client string, server string) bool { 58 | vc := Proto(client) 59 | vs := Proto(server) 60 | if vc > vs { 61 | return false 62 | } else if vc < vs { 63 | return true 64 | } 65 | 66 | vc = Major(client) 67 | vs = Major(server) 68 | if vc > vs { 69 | return false 70 | } else if vc < vs { 71 | return true 72 | } 73 | 74 | vc = Minor(client) 75 | vs = Minor(server) 76 | if vc > vs { 77 | return false 78 | } else if vc < vs { 79 | return true 80 | } 81 | return false 82 | } 83 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/version/version_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package version 16 | 17 | import ( 18 | "fmt" 19 | "strconv" 20 | "strings" 21 | "testing" 22 | 23 | "github.com/stretchr/testify/assert" 24 | ) 25 | 26 | func TestFull(t *testing.T) { 27 | assert := assert.New(t) 28 | version := Full() 29 | arr := strings.Split(version, ".") 30 | assert.Equal(3, len(arr)) 31 | 32 | proto, err := strconv.ParseInt(arr[0], 10, 64) 33 | assert.NoError(err) 34 | assert.True(proto >= 0) 35 | 36 | major, err := strconv.ParseInt(arr[1], 10, 64) 37 | assert.NoError(err) 38 | assert.True(major >= 0) 39 | 40 | minor, err := strconv.ParseInt(arr[2], 10, 64) 41 | assert.NoError(err) 42 | assert.True(minor >= 0) 43 | } 44 | 45 | func TestVersion(t *testing.T) { 46 | assert := assert.New(t) 47 | proto := Proto(Full()) 48 | major := Major(Full()) 49 | minor := Minor(Full()) 50 | parseVerion := fmt.Sprintf("%d.%d.%d", proto, major, minor) 51 | version := Full() 52 | assert.Equal(parseVerion, version) 53 | } 54 | 55 | func TestCompact(t *testing.T) { 56 | assert := assert.New(t) 57 | ok, _ := Compat("0.9.0") 58 | assert.False(ok) 59 | 60 | ok, _ = Compat("10.0.0") 61 | assert.True(ok) 62 | 63 | ok, _ = Compat("0.10.0") 64 | assert.False(ok) 65 | } 66 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/vhost/https.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package vhost 16 | 17 | import ( 18 | "crypto/tls" 19 | "io" 20 | "net" 21 | "time" 22 | 23 | gnet "github.com/fatedier/golib/net" 24 | ) 25 | 26 | type HTTPSMuxer struct { 27 | *Muxer 28 | } 29 | 30 | func NewHTTPSMuxer(listener net.Listener, timeout time.Duration) (*HTTPSMuxer, error) { 31 | mux, err := NewMuxer(listener, GetHTTPSHostname, nil, nil, nil, timeout) 32 | return &HTTPSMuxer{mux}, err 33 | } 34 | 35 | func GetHTTPSHostname(c net.Conn) (_ net.Conn, _ map[string]string, err error) { 36 | reqInfoMap := make(map[string]string, 0) 37 | sc, rd := gnet.NewSharedConn(c) 38 | 39 | clientHello, err := readClientHello(rd) 40 | if err != nil { 41 | return nil, reqInfoMap, err 42 | } 43 | 44 | reqInfoMap["Host"] = clientHello.ServerName 45 | reqInfoMap["Scheme"] = "https" 46 | return sc, reqInfoMap, nil 47 | } 48 | 49 | func readClientHello(reader io.Reader) (*tls.ClientHelloInfo, error) { 50 | var hello *tls.ClientHelloInfo 51 | 52 | // Note that Handshake always fails because the readOnlyConn is not a real connection. 53 | // As long as the Client Hello is successfully read, the failure should only happen after GetConfigForClient is called, 54 | // so we only care about the error if hello was never set. 55 | err := tls.Server(readOnlyConn{reader: reader}, &tls.Config{ 56 | GetConfigForClient: func(argHello *tls.ClientHelloInfo) (*tls.Config, error) { 57 | hello = &tls.ClientHelloInfo{} 58 | *hello = *argHello 59 | return nil, nil 60 | }, 61 | }).Handshake() 62 | 63 | if hello == nil { 64 | return nil, err 65 | } 66 | return hello, nil 67 | } 68 | 69 | type readOnlyConn struct { 70 | reader io.Reader 71 | } 72 | 73 | func (conn readOnlyConn) Read(p []byte) (int, error) { return conn.reader.Read(p) } 74 | func (conn readOnlyConn) Write(p []byte) (int, error) { return 0, io.ErrClosedPipe } 75 | func (conn readOnlyConn) Close() error { return nil } 76 | func (conn readOnlyConn) LocalAddr() net.Addr { return nil } 77 | func (conn readOnlyConn) RemoteAddr() net.Addr { return nil } 78 | func (conn readOnlyConn) SetDeadline(t time.Time) error { return nil } 79 | func (conn readOnlyConn) SetReadDeadline(t time.Time) error { return nil } 80 | func (conn readOnlyConn) SetWriteDeadline(t time.Time) error { return nil } 81 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/vhost/https_test.go: -------------------------------------------------------------------------------- 1 | package vhost 2 | 3 | import ( 4 | "crypto/tls" 5 | "net" 6 | "testing" 7 | "time" 8 | 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestGetHTTPSHostname(t *testing.T) { 13 | require := require.New(t) 14 | 15 | l, err := net.Listen("tcp", ":") 16 | require.NoError(err) 17 | defer l.Close() 18 | 19 | var conn net.Conn 20 | go func() { 21 | conn, _ = l.Accept() 22 | require.NotNil(conn) 23 | }() 24 | 25 | go func() { 26 | time.Sleep(100 * time.Millisecond) 27 | tls.Dial("tcp", l.Addr().String(), &tls.Config{ 28 | InsecureSkipVerify: true, 29 | ServerName: "example.com", 30 | }) 31 | }() 32 | 33 | time.Sleep(200 * time.Millisecond) 34 | _, infos, err := GetHTTPSHostname(conn) 35 | require.NoError(err) 36 | require.Equal("example.com", infos["Host"]) 37 | require.Equal("https", infos["Scheme"]) 38 | } 39 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/vhost/resource.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package vhost 16 | 17 | import ( 18 | "bytes" 19 | "io" 20 | "net/http" 21 | "os" 22 | 23 | frpLog "github.com/fatedier/frp/pkg/util/log" 24 | "github.com/fatedier/frp/pkg/util/version" 25 | ) 26 | 27 | var NotFoundPagePath = "" 28 | 29 | const ( 30 | NotFound = ` 31 | 32 | 33 | Not Found 34 | 41 | 42 | 43 |

The page you requested was not found.

44 |

Sorry, the page you are looking for is currently unavailable.
45 | Please try again later.

46 |

The server is powered by frp.

47 |

Faithfully yours, frp.

48 | 49 | 50 | ` 51 | ) 52 | 53 | func getNotFoundPageContent() []byte { 54 | var ( 55 | buf []byte 56 | err error 57 | ) 58 | if NotFoundPagePath != "" { 59 | buf, err = os.ReadFile(NotFoundPagePath) 60 | if err != nil { 61 | frpLog.Warn("read custom 404 page error: %v", err) 62 | buf = []byte(NotFound) 63 | } 64 | } else { 65 | buf = []byte(NotFound) 66 | } 67 | return buf 68 | } 69 | 70 | func notFoundResponse() *http.Response { 71 | header := make(http.Header) 72 | header.Set("server", "frp/"+version.Full()) 73 | header.Set("Content-Type", "text/html") 74 | 75 | res := &http.Response{ 76 | Status: "Not Found", 77 | StatusCode: 404, 78 | Proto: "HTTP/1.0", 79 | ProtoMajor: 1, 80 | ProtoMinor: 0, 81 | Header: header, 82 | Body: io.NopCloser(bytes.NewReader(getNotFoundPageContent())), 83 | } 84 | return res 85 | } 86 | 87 | func noAuthResponse() *http.Response { 88 | header := make(map[string][]string) 89 | header["WWW-Authenticate"] = []string{`Basic realm="Restricted"`} 90 | res := &http.Response{ 91 | Status: "401 Not authorized", 92 | StatusCode: 401, 93 | Proto: "HTTP/1.1", 94 | ProtoMajor: 1, 95 | ProtoMinor: 1, 96 | Header: header, 97 | } 98 | return res 99 | } 100 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/xlog/ctx.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package xlog 16 | 17 | import ( 18 | "context" 19 | ) 20 | 21 | type key int 22 | 23 | const ( 24 | xlogKey key = 0 25 | ) 26 | 27 | func NewContext(ctx context.Context, xl *Logger) context.Context { 28 | return context.WithValue(ctx, xlogKey, xl) 29 | } 30 | 31 | func FromContext(ctx context.Context) (xl *Logger, ok bool) { 32 | xl, ok = ctx.Value(xlogKey).(*Logger) 33 | return 34 | } 35 | 36 | func FromContextSafe(ctx context.Context) *Logger { 37 | xl, ok := ctx.Value(xlogKey).(*Logger) 38 | if !ok { 39 | xl = New() 40 | } 41 | return xl 42 | } 43 | -------------------------------------------------------------------------------- /frp-dev/pkg/util/xlog/xlog.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package xlog 16 | 17 | import ( 18 | "github.com/fatedier/frp/pkg/util/log" 19 | ) 20 | 21 | // Logger is not thread safety for operations on prefix 22 | type Logger struct { 23 | prefixes []string 24 | 25 | prefixString string 26 | } 27 | 28 | func New() *Logger { 29 | return &Logger{ 30 | prefixes: make([]string, 0), 31 | } 32 | } 33 | 34 | func (l *Logger) ResetPrefixes() (old []string) { 35 | old = l.prefixes 36 | l.prefixes = make([]string, 0) 37 | l.prefixString = "" 38 | return 39 | } 40 | 41 | func (l *Logger) AppendPrefix(prefix string) *Logger { 42 | l.prefixes = append(l.prefixes, prefix) 43 | l.prefixString += "[" + prefix + "] " 44 | return l 45 | } 46 | 47 | func (l *Logger) Spawn() *Logger { 48 | nl := New() 49 | for _, v := range l.prefixes { 50 | nl.AppendPrefix(v) 51 | } 52 | return nl 53 | } 54 | 55 | func (l *Logger) Error(format string, v ...interface{}) { 56 | log.Log.Error(l.prefixString+format, v...) 57 | } 58 | 59 | func (l *Logger) Warn(format string, v ...interface{}) { 60 | log.Log.Warn(l.prefixString+format, v...) 61 | } 62 | 63 | func (l *Logger) Info(format string, v ...interface{}) { 64 | log.Log.Info(l.prefixString+format, v...) 65 | } 66 | 67 | func (l *Logger) Debug(format string, v ...interface{}) { 68 | log.Log.Debug(l.prefixString+format, v...) 69 | } 70 | 71 | func (l *Logger) Trace(format string, v ...interface{}) { 72 | log.Log.Trace(l.prefixString+format, v...) 73 | } 74 | -------------------------------------------------------------------------------- /frp-dev/server/controller/resource.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package controller 16 | 17 | import ( 18 | "github.com/fatedier/frp/pkg/nathole" 19 | plugin "github.com/fatedier/frp/pkg/plugin/server" 20 | "github.com/fatedier/frp/pkg/util/tcpmux" 21 | "github.com/fatedier/frp/pkg/util/vhost" 22 | "github.com/fatedier/frp/server/group" 23 | "github.com/fatedier/frp/server/ports" 24 | "github.com/fatedier/frp/server/visitor" 25 | ) 26 | 27 | // All resource managers and controllers 28 | type ResourceController struct { 29 | // Manage all visitor listeners 30 | VisitorManager *visitor.Manager 31 | 32 | // TCP Group Controller 33 | TCPGroupCtl *group.TCPGroupCtl 34 | 35 | // HTTP Group Controller 36 | HTTPGroupCtl *group.HTTPGroupController 37 | 38 | // TCP Mux Group Controller 39 | TCPMuxGroupCtl *group.TCPMuxGroupCtl 40 | 41 | // Manage all TCP ports 42 | TCPPortManager *ports.Manager 43 | 44 | // Manage all UDP ports 45 | UDPPortManager *ports.Manager 46 | 47 | // For HTTP proxies, forwarding HTTP requests 48 | HTTPReverseProxy *vhost.HTTPReverseProxy 49 | 50 | // For HTTPS proxies, route requests to different clients by hostname and other information 51 | VhostHTTPSMuxer *vhost.HTTPSMuxer 52 | 53 | // Controller for nat hole connections 54 | NatHoleController *nathole.Controller 55 | 56 | // TCPMux HTTP CONNECT multiplexer 57 | TCPMuxHTTPConnectMuxer *tcpmux.HTTPConnectTCPMuxer 58 | 59 | // All server manager plugin 60 | PluginManager *plugin.Manager 61 | } 62 | -------------------------------------------------------------------------------- /frp-dev/server/group/group.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package group 16 | 17 | import ( 18 | "errors" 19 | ) 20 | 21 | var ( 22 | ErrGroupAuthFailed = errors.New("group auth failed") 23 | ErrGroupParamsInvalid = errors.New("group params invalid") 24 | ErrListenerClosed = errors.New("group listener closed") 25 | ErrGroupDifferentPort = errors.New("group should have same remote port") 26 | ErrProxyRepeated = errors.New("group proxy repeated") 27 | ) 28 | -------------------------------------------------------------------------------- /frp-dev/server/metrics/metrics.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | type ServerMetrics interface { 8 | NewClient() 9 | CloseClient() 10 | NewProxy(name string, proxyType string) 11 | CloseProxy(name string, proxyType string) 12 | OpenConnection(name string, proxyType string) 13 | CloseConnection(name string, proxyType string) 14 | AddTrafficIn(name string, proxyType string, trafficBytes int64) 15 | AddTrafficOut(name string, proxyType string, trafficBytes int64) 16 | } 17 | 18 | var Server ServerMetrics = noopServerMetrics{} 19 | 20 | var registerMetrics sync.Once 21 | 22 | func Register(m ServerMetrics) { 23 | registerMetrics.Do(func() { 24 | Server = m 25 | }) 26 | } 27 | 28 | type noopServerMetrics struct{} 29 | 30 | func (noopServerMetrics) NewClient() {} 31 | func (noopServerMetrics) CloseClient() {} 32 | func (noopServerMetrics) NewProxy(name string, proxyType string) {} 33 | func (noopServerMetrics) CloseProxy(name string, proxyType string) {} 34 | func (noopServerMetrics) OpenConnection(name string, proxyType string) {} 35 | func (noopServerMetrics) CloseConnection(name string, proxyType string) {} 36 | func (noopServerMetrics) AddTrafficIn(name string, proxyType string, trafficBytes int64) {} 37 | func (noopServerMetrics) AddTrafficOut(name string, proxyType string, trafficBytes int64) {} 38 | -------------------------------------------------------------------------------- /frp-dev/server/proxy/https.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package proxy 16 | 17 | import ( 18 | "strings" 19 | 20 | "github.com/fatedier/frp/pkg/config" 21 | "github.com/fatedier/frp/pkg/util/util" 22 | "github.com/fatedier/frp/pkg/util/vhost" 23 | ) 24 | 25 | type HTTPSProxy struct { 26 | *BaseProxy 27 | cfg *config.HTTPSProxyConf 28 | } 29 | 30 | func (pxy *HTTPSProxy) Run() (remoteAddr string, err error) { 31 | xl := pxy.xl 32 | routeConfig := &vhost.RouteConfig{} 33 | 34 | defer func() { 35 | if err != nil { 36 | pxy.Close() 37 | } 38 | }() 39 | addrs := make([]string, 0) 40 | for _, domain := range pxy.cfg.CustomDomains { 41 | if domain == "" { 42 | continue 43 | } 44 | 45 | routeConfig.Domain = domain 46 | l, errRet := pxy.rc.VhostHTTPSMuxer.Listen(pxy.ctx, routeConfig) 47 | if errRet != nil { 48 | err = errRet 49 | return 50 | } 51 | xl.Info("https proxy listen for host [%s]", routeConfig.Domain) 52 | pxy.listeners = append(pxy.listeners, l) 53 | addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.VhostHTTPSPort)) 54 | } 55 | 56 | if pxy.cfg.SubDomain != "" { 57 | routeConfig.Domain = pxy.cfg.SubDomain + "." + pxy.serverCfg.SubDomainHost 58 | l, errRet := pxy.rc.VhostHTTPSMuxer.Listen(pxy.ctx, routeConfig) 59 | if errRet != nil { 60 | err = errRet 61 | return 62 | } 63 | xl.Info("https proxy listen for host [%s]", routeConfig.Domain) 64 | pxy.listeners = append(pxy.listeners, l) 65 | addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.VhostHTTPSPort)) 66 | } 67 | 68 | pxy.startListenHandler(pxy, HandleUserTCPConnection) 69 | remoteAddr = strings.Join(addrs, ",") 70 | return 71 | } 72 | 73 | func (pxy *HTTPSProxy) GetConf() config.ProxyConf { 74 | return pxy.cfg 75 | } 76 | 77 | func (pxy *HTTPSProxy) Close() { 78 | pxy.BaseProxy.Close() 79 | } 80 | -------------------------------------------------------------------------------- /frp-dev/server/proxy/stcp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package proxy 16 | 17 | import ( 18 | "github.com/fatedier/frp/pkg/config" 19 | ) 20 | 21 | type STCPProxy struct { 22 | *BaseProxy 23 | cfg *config.STCPProxyConf 24 | } 25 | 26 | func (pxy *STCPProxy) Run() (remoteAddr string, err error) { 27 | xl := pxy.xl 28 | listener, errRet := pxy.rc.VisitorManager.Listen(pxy.GetName(), pxy.cfg.Sk) 29 | if errRet != nil { 30 | err = errRet 31 | return 32 | } 33 | pxy.listeners = append(pxy.listeners, listener) 34 | xl.Info("stcp proxy custom listen success") 35 | 36 | pxy.startListenHandler(pxy, HandleUserTCPConnection) 37 | return 38 | } 39 | 40 | func (pxy *STCPProxy) GetConf() config.ProxyConf { 41 | return pxy.cfg 42 | } 43 | 44 | func (pxy *STCPProxy) Close() { 45 | pxy.BaseProxy.Close() 46 | pxy.rc.VisitorManager.CloseListener(pxy.GetName()) 47 | } 48 | -------------------------------------------------------------------------------- /frp-dev/server/proxy/sudp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package proxy 16 | 17 | import ( 18 | "github.com/fatedier/frp/pkg/config" 19 | ) 20 | 21 | type SUDPProxy struct { 22 | *BaseProxy 23 | cfg *config.SUDPProxyConf 24 | } 25 | 26 | func (pxy *SUDPProxy) Run() (remoteAddr string, err error) { 27 | xl := pxy.xl 28 | 29 | listener, errRet := pxy.rc.VisitorManager.Listen(pxy.GetName(), pxy.cfg.Sk) 30 | if errRet != nil { 31 | err = errRet 32 | return 33 | } 34 | pxy.listeners = append(pxy.listeners, listener) 35 | xl.Info("sudp proxy custom listen success") 36 | 37 | pxy.startListenHandler(pxy, HandleUserTCPConnection) 38 | return 39 | } 40 | 41 | func (pxy *SUDPProxy) GetConf() config.ProxyConf { 42 | return pxy.cfg 43 | } 44 | 45 | func (pxy *SUDPProxy) Close() { 46 | pxy.BaseProxy.Close() 47 | pxy.rc.VisitorManager.CloseListener(pxy.GetName()) 48 | } 49 | -------------------------------------------------------------------------------- /frp-dev/server/proxy/tcp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package proxy 16 | 17 | import ( 18 | "fmt" 19 | "net" 20 | "strconv" 21 | 22 | "github.com/fatedier/frp/pkg/config" 23 | ) 24 | 25 | type TCPProxy struct { 26 | *BaseProxy 27 | cfg *config.TCPProxyConf 28 | 29 | realPort int 30 | } 31 | 32 | func (pxy *TCPProxy) Run() (remoteAddr string, err error) { 33 | xl := pxy.xl 34 | if pxy.cfg.Group != "" { 35 | l, realPort, errRet := pxy.rc.TCPGroupCtl.Listen(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, pxy.serverCfg.ProxyBindAddr, pxy.cfg.RemotePort) 36 | if errRet != nil { 37 | err = errRet 38 | return 39 | } 40 | defer func() { 41 | if err != nil { 42 | l.Close() 43 | } 44 | }() 45 | pxy.realPort = realPort 46 | pxy.listeners = append(pxy.listeners, l) 47 | xl.Info("tcp proxy listen port [%d] in group [%s]", pxy.cfg.RemotePort, pxy.cfg.Group) 48 | } else { 49 | pxy.realPort, err = pxy.rc.TCPPortManager.Acquire(pxy.name, pxy.cfg.RemotePort) 50 | if err != nil { 51 | return 52 | } 53 | defer func() { 54 | if err != nil { 55 | pxy.rc.TCPPortManager.Release(pxy.realPort) 56 | } 57 | }() 58 | listener, errRet := net.Listen("tcp", net.JoinHostPort(pxy.serverCfg.ProxyBindAddr, strconv.Itoa(pxy.realPort))) 59 | if errRet != nil { 60 | err = errRet 61 | return 62 | } 63 | pxy.listeners = append(pxy.listeners, listener) 64 | xl.Info("tcp proxy listen port [%d]", pxy.cfg.RemotePort) 65 | } 66 | 67 | pxy.cfg.RemotePort = pxy.realPort 68 | remoteAddr = fmt.Sprintf(":%d", pxy.realPort) 69 | pxy.startListenHandler(pxy, HandleUserTCPConnection) 70 | return 71 | } 72 | 73 | func (pxy *TCPProxy) GetConf() config.ProxyConf { 74 | return pxy.cfg 75 | } 76 | 77 | func (pxy *TCPProxy) Close() { 78 | pxy.BaseProxy.Close() 79 | if pxy.cfg.Group == "" { 80 | pxy.rc.TCPPortManager.Release(pxy.realPort) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /frp-dev/server/proxy/xtcp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package proxy 16 | 17 | import ( 18 | "fmt" 19 | 20 | "github.com/fatedier/golib/errors" 21 | 22 | "github.com/fatedier/frp/pkg/config" 23 | "github.com/fatedier/frp/pkg/msg" 24 | ) 25 | 26 | type XTCPProxy struct { 27 | *BaseProxy 28 | cfg *config.XTCPProxyConf 29 | 30 | closeCh chan struct{} 31 | } 32 | 33 | func (pxy *XTCPProxy) Run() (remoteAddr string, err error) { 34 | xl := pxy.xl 35 | 36 | if pxy.rc.NatHoleController == nil { 37 | xl.Error("udp port for xtcp is not specified.") 38 | err = fmt.Errorf("xtcp is not supported in frps") 39 | return 40 | } 41 | sidCh := pxy.rc.NatHoleController.ListenClient(pxy.GetName(), pxy.cfg.Sk) 42 | go func() { 43 | for { 44 | select { 45 | case <-pxy.closeCh: 46 | break 47 | case sidRequest := <-sidCh: 48 | sr := sidRequest 49 | workConn, errRet := pxy.GetWorkConnFromPool(nil, nil) 50 | if errRet != nil { 51 | continue 52 | } 53 | m := &msg.NatHoleSid{ 54 | Sid: sr.Sid, 55 | } 56 | errRet = msg.WriteMsg(workConn, m) 57 | if errRet != nil { 58 | xl.Warn("write nat hole sid package error, %v", errRet) 59 | workConn.Close() 60 | break 61 | } 62 | 63 | go func() { 64 | raw, errRet := msg.ReadMsg(workConn) 65 | if errRet != nil { 66 | xl.Warn("read nat hole client ok package error: %v", errRet) 67 | workConn.Close() 68 | return 69 | } 70 | if _, ok := raw.(*msg.NatHoleClientDetectOK); !ok { 71 | xl.Warn("read nat hole client ok package format error") 72 | workConn.Close() 73 | return 74 | } 75 | 76 | select { 77 | case sr.NotifyCh <- struct{}{}: 78 | default: 79 | } 80 | }() 81 | } 82 | } 83 | }() 84 | return 85 | } 86 | 87 | func (pxy *XTCPProxy) GetConf() config.ProxyConf { 88 | return pxy.cfg 89 | } 90 | 91 | func (pxy *XTCPProxy) Close() { 92 | pxy.BaseProxy.Close() 93 | pxy.rc.NatHoleController.CloseClient(pxy.GetName()) 94 | _ = errors.PanicToError(func() { 95 | close(pxy.closeCh) 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /frp-dev/server/visitor/visitor.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package visitor 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "net" 21 | "sync" 22 | 23 | frpIo "github.com/fatedier/golib/io" 24 | 25 | frpNet "github.com/fatedier/frp/pkg/util/net" 26 | "github.com/fatedier/frp/pkg/util/util" 27 | ) 28 | 29 | // Manager for visitor listeners. 30 | type Manager struct { 31 | visitorListeners map[string]*frpNet.CustomListener 32 | skMap map[string]string 33 | 34 | mu sync.RWMutex 35 | } 36 | 37 | func NewManager() *Manager { 38 | return &Manager{ 39 | visitorListeners: make(map[string]*frpNet.CustomListener), 40 | skMap: make(map[string]string), 41 | } 42 | } 43 | 44 | func (vm *Manager) Listen(name string, sk string) (l *frpNet.CustomListener, err error) { 45 | vm.mu.Lock() 46 | defer vm.mu.Unlock() 47 | 48 | if _, ok := vm.visitorListeners[name]; ok { 49 | err = fmt.Errorf("custom listener for [%s] is repeated", name) 50 | return 51 | } 52 | 53 | l = frpNet.NewCustomListener() 54 | vm.visitorListeners[name] = l 55 | vm.skMap[name] = sk 56 | return 57 | } 58 | 59 | func (vm *Manager) NewConn(name string, conn net.Conn, timestamp int64, signKey string, 60 | useEncryption bool, useCompression bool, 61 | ) (err error) { 62 | vm.mu.RLock() 63 | defer vm.mu.RUnlock() 64 | 65 | if l, ok := vm.visitorListeners[name]; ok { 66 | var sk string 67 | if sk = vm.skMap[name]; util.GetAuthKey(sk, timestamp) != signKey { 68 | err = fmt.Errorf("visitor connection of [%s] auth failed", name) 69 | return 70 | } 71 | 72 | var rwc io.ReadWriteCloser = conn 73 | if useEncryption { 74 | if rwc, err = frpIo.WithEncryption(rwc, []byte(sk)); err != nil { 75 | err = fmt.Errorf("create encryption connection failed: %v", err) 76 | return 77 | } 78 | } 79 | if useCompression { 80 | rwc = frpIo.WithCompression(rwc) 81 | } 82 | err = l.PutConn(frpNet.WrapReadWriteCloserToConn(rwc, conn)) 83 | } else { 84 | err = fmt.Errorf("custom listener for [%s] doesn't exist", name) 85 | return 86 | } 87 | return 88 | } 89 | 90 | func (vm *Manager) CloseListener(name string) { 91 | vm.mu.Lock() 92 | defer vm.mu.Unlock() 93 | 94 | delete(vm.visitorListeners, name) 95 | delete(vm.skMap, name) 96 | } 97 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/basic/config.go: -------------------------------------------------------------------------------- 1 | package basic 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/onsi/ginkgo" 7 | 8 | "github.com/fatedier/frp/test/e2e/framework" 9 | "github.com/fatedier/frp/test/e2e/framework/consts" 10 | "github.com/fatedier/frp/test/e2e/pkg/port" 11 | ) 12 | 13 | var _ = ginkgo.Describe("[Feature: Config]", func() { 14 | f := framework.NewDefaultFramework() 15 | 16 | ginkgo.Describe("Template", func() { 17 | ginkgo.It("render by env", func() { 18 | serverConf := consts.DefaultServerConfig 19 | clientConf := consts.DefaultClientConfig 20 | 21 | portName := port.GenName("TCP") 22 | serverConf += fmt.Sprintf(` 23 | token = {{ %s{{ .Envs.FRP_TOKEN }}%s }} 24 | `, "`", "`") 25 | 26 | clientConf += fmt.Sprintf(` 27 | token = {{ %s{{ .Envs.FRP_TOKEN }}%s }} 28 | 29 | [tcp] 30 | type = tcp 31 | local_port = {{ .%s }} 32 | remote_port = {{ .%s }} 33 | `, "`", "`", framework.TCPEchoServerPort, portName) 34 | 35 | f.SetEnvs([]string{"FRP_TOKEN=123"}) 36 | f.RunProcesses([]string{serverConf}, []string{clientConf}) 37 | 38 | framework.NewRequestExpect(f).PortName(portName).Ensure() 39 | }) 40 | }) 41 | 42 | ginkgo.Describe("Includes", func() { 43 | ginkgo.It("split tcp proxies into different files", func() { 44 | serverPort := f.AllocPort() 45 | serverConfigPath := f.GenerateConfigFile(fmt.Sprintf(` 46 | [common] 47 | bind_addr = 0.0.0.0 48 | bind_port = %d 49 | `, serverPort)) 50 | 51 | remotePort := f.AllocPort() 52 | proxyConfigPath := f.GenerateConfigFile(fmt.Sprintf(` 53 | [tcp] 54 | type = tcp 55 | local_port = %d 56 | remote_port = %d 57 | `, f.PortByName(framework.TCPEchoServerPort), remotePort)) 58 | 59 | remotePort2 := f.AllocPort() 60 | proxyConfigPath2 := f.GenerateConfigFile(fmt.Sprintf(` 61 | [tcp2] 62 | type = tcp 63 | local_port = %d 64 | remote_port = %d 65 | `, f.PortByName(framework.TCPEchoServerPort), remotePort2)) 66 | 67 | clientConfigPath := f.GenerateConfigFile(fmt.Sprintf(` 68 | [common] 69 | server_port = %d 70 | includes = %s,%s 71 | `, serverPort, proxyConfigPath, proxyConfigPath2)) 72 | 73 | _, _, err := f.RunFrps("-c", serverConfigPath) 74 | framework.ExpectNoError(err) 75 | 76 | _, _, err = f.RunFrpc("-c", clientConfigPath) 77 | framework.ExpectNoError(err) 78 | 79 | framework.NewRequestExpect(f).Port(remotePort).Ensure() 80 | framework.NewRequestExpect(f).Port(remotePort2).Ensure() 81 | }) 82 | }) 83 | }) 84 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/e2e.go: -------------------------------------------------------------------------------- 1 | package e2e 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/onsi/ginkgo" 7 | "github.com/onsi/ginkgo/config" 8 | "github.com/onsi/gomega" 9 | 10 | "github.com/fatedier/frp/pkg/util/log" 11 | "github.com/fatedier/frp/test/e2e/framework" 12 | ) 13 | 14 | var _ = ginkgo.SynchronizedBeforeSuite(func() []byte { 15 | setupSuite() 16 | return nil 17 | }, func(data []byte) { 18 | // Run on all Ginkgo nodes 19 | setupSuitePerGinkgoNode() 20 | }) 21 | 22 | var _ = ginkgo.SynchronizedAfterSuite(func() { 23 | CleanupSuite() 24 | }, func() { 25 | AfterSuiteActions() 26 | }) 27 | 28 | // RunE2ETests checks configuration parameters (specified through flags) and then runs 29 | // E2E tests using the Ginkgo runner. 30 | // If a "report directory" is specified, one or more JUnit test reports will be 31 | // generated in this directory, and cluster logs will also be saved. 32 | // This function is called on each Ginkgo node in parallel mode. 33 | func RunE2ETests(t *testing.T) { 34 | gomega.RegisterFailHandler(framework.Fail) 35 | 36 | log.Info("Starting e2e run %q on Ginkgo node %d of total %d", 37 | framework.RunID, config.GinkgoConfig.ParallelNode, config.GinkgoConfig.ParallelTotal) 38 | ginkgo.RunSpecs(t, "frp e2e suite") 39 | } 40 | 41 | // setupSuite is the boilerplate that can be used to setup ginkgo test suites, on the SynchronizedBeforeSuite step. 42 | // There are certain operations we only want to run once per overall test invocation 43 | // (such as deleting old namespaces, or verifying that all system pods are running. 44 | // Because of the way Ginkgo runs tests in parallel, we must use SynchronizedBeforeSuite 45 | // to ensure that these operations only run on the first parallel Ginkgo node. 46 | // 47 | // This function takes two parameters: one function which runs on only the first Ginkgo node, 48 | // returning an opaque byte array, and then a second function which runs on all Ginkgo nodes, 49 | // accepting the byte array. 50 | func setupSuite() { 51 | // Run only on Ginkgo node 1 52 | } 53 | 54 | // setupSuitePerGinkgoNode is the boilerplate that can be used to setup ginkgo test suites, on the SynchronizedBeforeSuite step. 55 | // There are certain operations we only want to run once per overall test invocation on each Ginkgo node 56 | // such as making some global variables accessible to all parallel executions 57 | // Because of the way Ginkgo runs tests in parallel, we must use SynchronizedBeforeSuite 58 | // Ref: https://onsi.github.io/ginkgo/#parallel-specs 59 | func setupSuitePerGinkgoNode() { 60 | // config.GinkgoConfig.ParallelNode 61 | } 62 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/e2e_test.go: -------------------------------------------------------------------------------- 1 | package e2e 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "testing" 8 | 9 | _ "github.com/onsi/ginkgo" 10 | 11 | "github.com/fatedier/frp/pkg/util/log" 12 | // test source 13 | _ "github.com/fatedier/frp/test/e2e/basic" 14 | _ "github.com/fatedier/frp/test/e2e/features" 15 | "github.com/fatedier/frp/test/e2e/framework" 16 | _ "github.com/fatedier/frp/test/e2e/plugin" 17 | ) 18 | 19 | // handleFlags sets up all flags and parses the command line. 20 | func handleFlags() { 21 | framework.RegisterCommonFlags(flag.CommandLine) 22 | flag.Parse() 23 | } 24 | 25 | func TestMain(m *testing.M) { 26 | // Register test flags, then parse flags. 27 | handleFlags() 28 | 29 | if err := framework.ValidateTestContext(&framework.TestContext); err != nil { 30 | fmt.Println(err) 31 | os.Exit(1) 32 | } 33 | 34 | log.InitLog("console", "", framework.TestContext.LogLevel, 0, true) 35 | os.Exit(m.Run()) 36 | } 37 | 38 | func TestE2E(t *testing.T) { 39 | RunE2ETests(t) 40 | } 41 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/examples.go: -------------------------------------------------------------------------------- 1 | package e2e 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/onsi/ginkgo" 7 | 8 | "github.com/fatedier/frp/test/e2e/framework" 9 | "github.com/fatedier/frp/test/e2e/framework/consts" 10 | ) 11 | 12 | var _ = ginkgo.Describe("[Feature: Example]", func() { 13 | f := framework.NewDefaultFramework() 14 | 15 | ginkgo.Describe("TCP", func() { 16 | ginkgo.It("Expose a TCP echo server", func() { 17 | serverConf := consts.DefaultServerConfig 18 | clientConf := consts.DefaultClientConfig 19 | 20 | remotePort := f.AllocPort() 21 | clientConf += fmt.Sprintf(` 22 | [tcp] 23 | type = tcp 24 | local_port = {{ .%s }} 25 | remote_port = %d 26 | `, framework.TCPEchoServerPort, remotePort) 27 | 28 | f.RunProcesses([]string{serverConf}, []string{clientConf}) 29 | 30 | framework.NewRequestExpect(f).Port(remotePort).Ensure() 31 | }) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/features/bandwidth_limit.go: -------------------------------------------------------------------------------- 1 | package features 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | 8 | "github.com/onsi/ginkgo" 9 | 10 | "github.com/fatedier/frp/test/e2e/framework" 11 | "github.com/fatedier/frp/test/e2e/framework/consts" 12 | "github.com/fatedier/frp/test/e2e/mock/server/streamserver" 13 | "github.com/fatedier/frp/test/e2e/pkg/request" 14 | ) 15 | 16 | var _ = ginkgo.Describe("[Feature: Bandwidth Limit]", func() { 17 | f := framework.NewDefaultFramework() 18 | 19 | ginkgo.It("Proxy Bandwidth Limit", func() { 20 | serverConf := consts.DefaultServerConfig 21 | clientConf := consts.DefaultClientConfig 22 | 23 | localPort := f.AllocPort() 24 | localServer := streamserver.New(streamserver.TCP, streamserver.WithBindPort(localPort)) 25 | f.RunServer("", localServer) 26 | 27 | remotePort := f.AllocPort() 28 | clientConf += fmt.Sprintf(` 29 | [tcp] 30 | type = tcp 31 | local_port = %d 32 | remote_port = %d 33 | bandwidth_limit = 10KB 34 | `, localPort, remotePort) 35 | 36 | f.RunProcesses([]string{serverConf}, []string{clientConf}) 37 | 38 | content := strings.Repeat("a", 50*1024) // 5KB 39 | start := time.Now() 40 | framework.NewRequestExpect(f).Port(remotePort).RequestModify(func(r *request.Request) { 41 | r.Body([]byte(content)).Timeout(30 * time.Second) 42 | }).ExpectResp([]byte(content)).Ensure() 43 | duration := time.Since(start) 44 | 45 | framework.ExpectTrue(duration.Seconds() > 7, "100Kb with 10KB limit, want > 7 seconds, but got %d seconds", duration.Seconds()) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/features/chaos.go: -------------------------------------------------------------------------------- 1 | package features 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/onsi/ginkgo" 8 | 9 | "github.com/fatedier/frp/test/e2e/framework" 10 | ) 11 | 12 | var _ = ginkgo.Describe("[Feature: Chaos]", func() { 13 | f := framework.NewDefaultFramework() 14 | 15 | ginkgo.It("reconnect after frps restart", func() { 16 | serverPort := f.AllocPort() 17 | serverConfigPath := f.GenerateConfigFile(fmt.Sprintf(` 18 | [common] 19 | bind_addr = 0.0.0.0 20 | bind_port = %d 21 | `, serverPort)) 22 | 23 | remotePort := f.AllocPort() 24 | clientConfigPath := f.GenerateConfigFile(fmt.Sprintf(` 25 | [common] 26 | server_port = %d 27 | log_level = trace 28 | 29 | [tcp] 30 | type = tcp 31 | local_port = %d 32 | remote_port = %d 33 | `, serverPort, f.PortByName(framework.TCPEchoServerPort), remotePort)) 34 | 35 | // 1. start frps and frpc, expect request success 36 | ps, _, err := f.RunFrps("-c", serverConfigPath) 37 | framework.ExpectNoError(err) 38 | 39 | pc, _, err := f.RunFrpc("-c", clientConfigPath) 40 | framework.ExpectNoError(err) 41 | framework.NewRequestExpect(f).Port(remotePort).Ensure() 42 | 43 | // 2. stop frps, expect request failed 44 | _ = ps.Stop() 45 | time.Sleep(200 * time.Millisecond) 46 | framework.NewRequestExpect(f).Port(remotePort).ExpectError(true).Ensure() 47 | 48 | // 3. restart frps, expect request success 49 | _, _, err = f.RunFrps("-c", serverConfigPath) 50 | framework.ExpectNoError(err) 51 | time.Sleep(2 * time.Second) 52 | framework.NewRequestExpect(f).Port(remotePort).Ensure() 53 | 54 | // 4. stop frpc, expect request failed 55 | _ = pc.Stop() 56 | time.Sleep(200 * time.Millisecond) 57 | framework.NewRequestExpect(f).Port(remotePort).ExpectError(true).Ensure() 58 | 59 | // 5. restart frpc, expect request success 60 | _, _, err = f.RunFrpc("-c", clientConfigPath) 61 | framework.ExpectNoError(err) 62 | time.Sleep(time.Second) 63 | framework.NewRequestExpect(f).Port(remotePort).Ensure() 64 | }) 65 | }) 66 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/features/heartbeat.go: -------------------------------------------------------------------------------- 1 | package features 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/onsi/ginkgo" 8 | 9 | "github.com/fatedier/frp/test/e2e/framework" 10 | ) 11 | 12 | var _ = ginkgo.Describe("[Feature: Heartbeat]", func() { 13 | f := framework.NewDefaultFramework() 14 | 15 | ginkgo.It("disable application layer heartbeat", func() { 16 | serverPort := f.AllocPort() 17 | serverConf := fmt.Sprintf(` 18 | [common] 19 | bind_addr = 0.0.0.0 20 | bind_port = %d 21 | heartbeat_timeout = -1 22 | tcp_mux_keepalive_interval = 2 23 | `, serverPort) 24 | 25 | remotePort := f.AllocPort() 26 | clientConf := fmt.Sprintf(` 27 | [common] 28 | server_port = %d 29 | log_level = trace 30 | heartbeat_interval = -1 31 | heartbeat_timeout = -1 32 | tcp_mux_keepalive_interval = 2 33 | 34 | [tcp] 35 | type = tcp 36 | local_port = %d 37 | remote_port = %d 38 | `, serverPort, f.PortByName(framework.TCPEchoServerPort), remotePort) 39 | 40 | // run frps and frpc 41 | f.RunProcesses([]string{serverConf}, []string{clientConf}) 42 | 43 | framework.NewRequestExpect(f).Protocol("tcp").Port(remotePort).Ensure() 44 | 45 | time.Sleep(5 * time.Second) 46 | framework.NewRequestExpect(f).Protocol("tcp").Port(remotePort).Ensure() 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/features/monitor.go: -------------------------------------------------------------------------------- 1 | package features 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | 8 | "github.com/onsi/ginkgo" 9 | 10 | "github.com/fatedier/frp/pkg/util/log" 11 | "github.com/fatedier/frp/test/e2e/framework" 12 | "github.com/fatedier/frp/test/e2e/framework/consts" 13 | "github.com/fatedier/frp/test/e2e/pkg/request" 14 | ) 15 | 16 | var _ = ginkgo.Describe("[Feature: Monitor]", func() { 17 | f := framework.NewDefaultFramework() 18 | 19 | ginkgo.It("Prometheus metrics", func() { 20 | dashboardPort := f.AllocPort() 21 | serverConf := consts.DefaultServerConfig + fmt.Sprintf(` 22 | enable_prometheus = true 23 | dashboard_addr = 0.0.0.0 24 | dashboard_port = %d 25 | `, dashboardPort) 26 | 27 | clientConf := consts.DefaultClientConfig 28 | remotePort := f.AllocPort() 29 | clientConf += fmt.Sprintf(` 30 | [tcp] 31 | type = tcp 32 | local_port = {{ .%s }} 33 | remote_port = %d 34 | `, framework.TCPEchoServerPort, remotePort) 35 | 36 | f.RunProcesses([]string{serverConf}, []string{clientConf}) 37 | 38 | framework.NewRequestExpect(f).Port(remotePort).Ensure() 39 | time.Sleep(500 * time.Millisecond) 40 | 41 | framework.NewRequestExpect(f).RequestModify(func(r *request.Request) { 42 | r.HTTP().Port(dashboardPort).HTTPPath("/metrics") 43 | }).Ensure(func(resp *request.Response) bool { 44 | log.Trace("prometheus metrics response: \n%s", resp.Content) 45 | if resp.Code != 200 { 46 | return false 47 | } 48 | if !strings.Contains(string(resp.Content), "traffic_in") { 49 | return false 50 | } 51 | return true 52 | }) 53 | }) 54 | }) 55 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/framework/cleanup.go: -------------------------------------------------------------------------------- 1 | package framework 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | // CleanupActionHandle is an integer pointer type for handling cleanup action 8 | type CleanupActionHandle *int 9 | 10 | type cleanupFuncHandle struct { 11 | actionHandle CleanupActionHandle 12 | actionHook func() 13 | } 14 | 15 | var ( 16 | cleanupActionsLock sync.Mutex 17 | cleanupHookList = []cleanupFuncHandle{} 18 | ) 19 | 20 | // AddCleanupAction installs a function that will be called in the event of the 21 | // whole test being terminated. This allows arbitrary pieces of the overall 22 | // test to hook into SynchronizedAfterSuite(). 23 | // The hooks are called in last-in-first-out order. 24 | func AddCleanupAction(fn func()) CleanupActionHandle { 25 | p := CleanupActionHandle(new(int)) 26 | cleanupActionsLock.Lock() 27 | defer cleanupActionsLock.Unlock() 28 | c := cleanupFuncHandle{actionHandle: p, actionHook: fn} 29 | cleanupHookList = append([]cleanupFuncHandle{c}, cleanupHookList...) 30 | return p 31 | } 32 | 33 | // RemoveCleanupAction removes a function that was installed by 34 | // AddCleanupAction. 35 | func RemoveCleanupAction(p CleanupActionHandle) { 36 | cleanupActionsLock.Lock() 37 | defer cleanupActionsLock.Unlock() 38 | for i, item := range cleanupHookList { 39 | if item.actionHandle == p { 40 | cleanupHookList = append(cleanupHookList[:i], cleanupHookList[i+1:]...) 41 | break 42 | } 43 | } 44 | } 45 | 46 | // RunCleanupActions runs all functions installed by AddCleanupAction. It does 47 | // not remove them (see RemoveCleanupAction) but it does run unlocked, so they 48 | // may remove themselves. 49 | func RunCleanupActions() { 50 | list := []func(){} 51 | func() { 52 | cleanupActionsLock.Lock() 53 | defer cleanupActionsLock.Unlock() 54 | for _, p := range cleanupHookList { 55 | list = append(list, p.actionHook) 56 | } 57 | }() 58 | // Run unlocked. 59 | for _, fn := range list { 60 | fn() 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/framework/client.go: -------------------------------------------------------------------------------- 1 | package framework 2 | 3 | type FRPClient struct { 4 | port int 5 | } 6 | 7 | func (f *Framework) FRPClient(port int) *FRPClient { 8 | return &FRPClient{ 9 | port: port, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/framework/consts/consts.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/fatedier/frp/test/e2e/pkg/port" 8 | ) 9 | 10 | const ( 11 | TestString = "frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet." 12 | 13 | DefaultTimeout = 2 * time.Second 14 | ) 15 | 16 | var ( 17 | PortServerName string 18 | PortClientAdmin string 19 | 20 | DefaultServerConfig = ` 21 | [common] 22 | bind_port = {{ .%s }} 23 | log_level = trace 24 | ` 25 | 26 | DefaultClientConfig = ` 27 | [common] 28 | server_port = {{ .%s }} 29 | log_level = trace 30 | ` 31 | ) 32 | 33 | func init() { 34 | PortServerName = port.GenName("Server") 35 | PortClientAdmin = port.GenName("ClientAdmin") 36 | DefaultServerConfig = fmt.Sprintf(DefaultServerConfig, port.GenName("Server")) 37 | DefaultClientConfig = fmt.Sprintf(DefaultClientConfig, port.GenName("Server")) 38 | } 39 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/framework/ginkgowrapper/wrapper.go: -------------------------------------------------------------------------------- 1 | // Package ginkgowrapper wraps Ginkgo Fail and Skip functions to panic 2 | // with structured data instead of a constant string. 3 | package ginkgowrapper 4 | 5 | import ( 6 | "bufio" 7 | "bytes" 8 | "regexp" 9 | "runtime" 10 | "runtime/debug" 11 | "strings" 12 | 13 | "github.com/onsi/ginkgo" 14 | ) 15 | 16 | // FailurePanic is the value that will be panicked from Fail. 17 | type FailurePanic struct { 18 | Message string // The failure message passed to Fail 19 | Filename string // The filename that is the source of the failure 20 | Line int // The line number of the filename that is the source of the failure 21 | FullStackTrace string // A full stack trace starting at the source of the failure 22 | } 23 | 24 | // String makes FailurePanic look like the old Ginkgo panic when printed. 25 | func (FailurePanic) String() string { return ginkgo.GINKGO_PANIC } 26 | 27 | // Fail wraps ginkgo.Fail so that it panics with more useful 28 | // information about the failure. This function will panic with a 29 | // FailurePanic. 30 | func Fail(message string, callerSkip ...int) { 31 | skip := 1 32 | if len(callerSkip) > 0 { 33 | skip += callerSkip[0] 34 | } 35 | 36 | _, file, line, _ := runtime.Caller(skip) 37 | fp := FailurePanic{ 38 | Message: message, 39 | Filename: file, 40 | Line: line, 41 | FullStackTrace: pruneStack(skip), 42 | } 43 | 44 | defer func() { 45 | e := recover() 46 | if e != nil { 47 | panic(fp) 48 | } 49 | }() 50 | 51 | ginkgo.Fail(message, skip) 52 | } 53 | 54 | // ginkgo adds a lot of test running infrastructure to the stack, so 55 | // we filter those out 56 | var stackSkipPattern = regexp.MustCompile(`onsi/ginkgo`) 57 | 58 | func pruneStack(skip int) string { 59 | skip += 2 // one for pruneStack and one for debug.Stack 60 | stack := debug.Stack() 61 | scanner := bufio.NewScanner(bytes.NewBuffer(stack)) 62 | var prunedStack []string 63 | 64 | // skip the top of the stack 65 | for i := 0; i < 2*skip+1; i++ { 66 | scanner.Scan() 67 | } 68 | 69 | for scanner.Scan() { 70 | if stackSkipPattern.Match(scanner.Bytes()) { 71 | scanner.Scan() // these come in pairs 72 | } else { 73 | prunedStack = append(prunedStack, scanner.Text()) 74 | scanner.Scan() // these come in pairs 75 | prunedStack = append(prunedStack, scanner.Text()) 76 | } 77 | } 78 | 79 | return strings.Join(prunedStack, "\n") 80 | } 81 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/framework/mockservers.go: -------------------------------------------------------------------------------- 1 | package framework 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | 8 | "github.com/fatedier/frp/test/e2e/framework/consts" 9 | "github.com/fatedier/frp/test/e2e/mock/server" 10 | "github.com/fatedier/frp/test/e2e/mock/server/httpserver" 11 | "github.com/fatedier/frp/test/e2e/mock/server/streamserver" 12 | "github.com/fatedier/frp/test/e2e/pkg/port" 13 | ) 14 | 15 | const ( 16 | TCPEchoServerPort = "TCPEchoServerPort" 17 | UDPEchoServerPort = "UDPEchoServerPort" 18 | UDSEchoServerAddr = "UDSEchoServerAddr" 19 | HTTPSimpleServerPort = "HTTPSimpleServerPort" 20 | ) 21 | 22 | type MockServers struct { 23 | tcpEchoServer server.Server 24 | udpEchoServer server.Server 25 | udsEchoServer server.Server 26 | httpSimpleServer server.Server 27 | } 28 | 29 | func NewMockServers(portAllocator *port.Allocator) *MockServers { 30 | s := &MockServers{} 31 | tcpPort := portAllocator.Get() 32 | udpPort := portAllocator.Get() 33 | httpPort := portAllocator.Get() 34 | s.tcpEchoServer = streamserver.New(streamserver.TCP, streamserver.WithBindPort(tcpPort)) 35 | s.udpEchoServer = streamserver.New(streamserver.UDP, streamserver.WithBindPort(udpPort)) 36 | s.httpSimpleServer = httpserver.New(httpserver.WithBindPort(httpPort), 37 | httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 38 | _, _ = w.Write([]byte(consts.TestString)) 39 | })), 40 | ) 41 | 42 | udsIndex := portAllocator.Get() 43 | udsAddr := fmt.Sprintf("%s/frp_echo_server_%d.sock", os.TempDir(), udsIndex) 44 | os.Remove(udsAddr) 45 | s.udsEchoServer = streamserver.New(streamserver.Unix, streamserver.WithBindAddr(udsAddr)) 46 | return s 47 | } 48 | 49 | func (m *MockServers) Run() error { 50 | if err := m.tcpEchoServer.Run(); err != nil { 51 | return err 52 | } 53 | if err := m.udpEchoServer.Run(); err != nil { 54 | return err 55 | } 56 | if err := m.udsEchoServer.Run(); err != nil { 57 | return err 58 | } 59 | if err := m.httpSimpleServer.Run(); err != nil { 60 | return err 61 | } 62 | return nil 63 | } 64 | 65 | func (m *MockServers) Close() { 66 | m.tcpEchoServer.Close() 67 | m.udpEchoServer.Close() 68 | m.udsEchoServer.Close() 69 | m.httpSimpleServer.Close() 70 | os.Remove(m.udsEchoServer.BindAddr()) 71 | } 72 | 73 | func (m *MockServers) GetTemplateParams() map[string]interface{} { 74 | ret := make(map[string]interface{}) 75 | ret[TCPEchoServerPort] = m.tcpEchoServer.BindPort() 76 | ret[UDPEchoServerPort] = m.udpEchoServer.BindPort() 77 | ret[UDSEchoServerAddr] = m.udsEchoServer.BindAddr() 78 | ret[HTTPSimpleServerPort] = m.httpSimpleServer.BindPort() 79 | return ret 80 | } 81 | 82 | func (m *MockServers) GetParam(key string) interface{} { 83 | params := m.GetTemplateParams() 84 | if v, ok := params[key]; ok { 85 | return v 86 | } 87 | return nil 88 | } 89 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/framework/test_context.go: -------------------------------------------------------------------------------- 1 | package framework 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/onsi/ginkgo/config" 9 | ) 10 | 11 | type TestContextType struct { 12 | FRPClientPath string 13 | FRPServerPath string 14 | LogLevel string 15 | Debug bool 16 | } 17 | 18 | var TestContext TestContextType 19 | 20 | // RegisterCommonFlags registers flags common to all e2e test suites. 21 | // The flag set can be flag.CommandLine (if desired) or a custom 22 | // flag set that then gets passed to viperconfig.ViperizeFlags. 23 | // 24 | // The other Register*Flags methods below can be used to add more 25 | // test-specific flags. However, those settings then get added 26 | // regardless whether the test is actually in the test suite. 27 | func RegisterCommonFlags(flags *flag.FlagSet) { 28 | // Turn on EmitSpecProgress to get spec progress (especially on interrupt) 29 | config.GinkgoConfig.EmitSpecProgress = true 30 | 31 | // Randomize specs as well as suites 32 | config.GinkgoConfig.RandomizeAllSpecs = true 33 | 34 | flags.StringVar(&TestContext.FRPClientPath, "frpc-path", "../../bin/frpc", "The frp client binary to use.") 35 | flags.StringVar(&TestContext.FRPServerPath, "frps-path", "../../bin/frps", "The frp server binary to use.") 36 | flags.StringVar(&TestContext.LogLevel, "log-level", "debug", "Log level.") 37 | flags.BoolVar(&TestContext.Debug, "debug", false, "Enable debug mode to print detail info.") 38 | } 39 | 40 | func ValidateTestContext(t *TestContextType) error { 41 | if t.FRPClientPath == "" || t.FRPServerPath == "" { 42 | return fmt.Errorf("frpc and frps binary path can't be empty") 43 | } 44 | if _, err := os.Stat(t.FRPClientPath); err != nil { 45 | return fmt.Errorf("load frpc-path error: %v", err) 46 | } 47 | if _, err := os.Stat(t.FRPServerPath); err != nil { 48 | return fmt.Errorf("load frps-path error: %v", err) 49 | } 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/framework/util.go: -------------------------------------------------------------------------------- 1 | package framework 2 | 3 | import ( 4 | "github.com/google/uuid" 5 | ) 6 | 7 | // RunID is a unique identifier of the e2e run. 8 | // Beware that this ID is not the same for all tests in the e2e run, because each Ginkgo node creates it separately. 9 | var RunID string 10 | 11 | func init() { 12 | RunID = uuid.NewString() 13 | } 14 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/mock/server/httpserver/server.go: -------------------------------------------------------------------------------- 1 | package httpserver 2 | 3 | import ( 4 | "crypto/tls" 5 | "net" 6 | "net/http" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | type Server struct { 12 | bindAddr string 13 | bindPort int 14 | handler http.Handler 15 | 16 | l net.Listener 17 | tlsConfig *tls.Config 18 | hs *http.Server 19 | } 20 | 21 | type Option func(*Server) *Server 22 | 23 | func New(options ...Option) *Server { 24 | s := &Server{ 25 | bindAddr: "127.0.0.1", 26 | } 27 | 28 | for _, option := range options { 29 | s = option(s) 30 | } 31 | return s 32 | } 33 | 34 | func WithBindAddr(addr string) Option { 35 | return func(s *Server) *Server { 36 | s.bindAddr = addr 37 | return s 38 | } 39 | } 40 | 41 | func WithBindPort(port int) Option { 42 | return func(s *Server) *Server { 43 | s.bindPort = port 44 | return s 45 | } 46 | } 47 | 48 | func WithTLSConfig(tlsConfig *tls.Config) Option { 49 | return func(s *Server) *Server { 50 | s.tlsConfig = tlsConfig 51 | return s 52 | } 53 | } 54 | 55 | func WithHandler(h http.Handler) Option { 56 | return func(s *Server) *Server { 57 | s.handler = h 58 | return s 59 | } 60 | } 61 | 62 | func WithResponse(resp []byte) Option { 63 | return func(s *Server) *Server { 64 | s.handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 65 | _, _ = w.Write(resp) 66 | }) 67 | return s 68 | } 69 | } 70 | 71 | func (s *Server) Run() error { 72 | if err := s.initListener(); err != nil { 73 | return err 74 | } 75 | 76 | addr := net.JoinHostPort(s.bindAddr, strconv.Itoa(s.bindPort)) 77 | hs := &http.Server{ 78 | Addr: addr, 79 | Handler: s.handler, 80 | TLSConfig: s.tlsConfig, 81 | ReadHeaderTimeout: time.Minute, 82 | } 83 | 84 | s.hs = hs 85 | if s.tlsConfig == nil { 86 | go func() { 87 | _ = hs.Serve(s.l) 88 | }() 89 | } else { 90 | go func() { 91 | _ = hs.ServeTLS(s.l, "", "") 92 | }() 93 | } 94 | return nil 95 | } 96 | 97 | func (s *Server) Close() error { 98 | if s.hs != nil { 99 | return s.hs.Close() 100 | } 101 | return nil 102 | } 103 | 104 | func (s *Server) initListener() (err error) { 105 | s.l, err = net.Listen("tcp", net.JoinHostPort(s.bindAddr, strconv.Itoa(s.bindPort))) 106 | return 107 | } 108 | 109 | func (s *Server) BindAddr() string { 110 | return s.bindAddr 111 | } 112 | 113 | func (s *Server) BindPort() int { 114 | return s.bindPort 115 | } 116 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/mock/server/interface.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | type Server interface { 4 | Run() error 5 | Close() error 6 | BindAddr() string 7 | BindPort() int 8 | } 9 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/mock/server/streamserver/server.go: -------------------------------------------------------------------------------- 1 | package streamserver 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "net" 8 | "strconv" 9 | 10 | libnet "github.com/fatedier/frp/pkg/util/net" 11 | "github.com/fatedier/frp/test/e2e/pkg/rpc" 12 | ) 13 | 14 | type Type string 15 | 16 | const ( 17 | TCP Type = "tcp" 18 | UDP Type = "udp" 19 | Unix Type = "unix" 20 | ) 21 | 22 | type Server struct { 23 | netType Type 24 | bindAddr string 25 | bindPort int 26 | respContent []byte 27 | 28 | handler func(net.Conn) 29 | 30 | l net.Listener 31 | } 32 | 33 | type Option func(*Server) *Server 34 | 35 | func New(netType Type, options ...Option) *Server { 36 | s := &Server{ 37 | netType: netType, 38 | bindAddr: "127.0.0.1", 39 | } 40 | s.handler = s.handle 41 | 42 | for _, option := range options { 43 | s = option(s) 44 | } 45 | return s 46 | } 47 | 48 | func WithBindAddr(addr string) Option { 49 | return func(s *Server) *Server { 50 | s.bindAddr = addr 51 | return s 52 | } 53 | } 54 | 55 | func WithBindPort(port int) Option { 56 | return func(s *Server) *Server { 57 | s.bindPort = port 58 | return s 59 | } 60 | } 61 | 62 | func WithRespContent(content []byte) Option { 63 | return func(s *Server) *Server { 64 | s.respContent = content 65 | return s 66 | } 67 | } 68 | 69 | func WithCustomHandler(handler func(net.Conn)) Option { 70 | return func(s *Server) *Server { 71 | s.handler = handler 72 | return s 73 | } 74 | } 75 | 76 | func (s *Server) Run() error { 77 | if err := s.initListener(); err != nil { 78 | return err 79 | } 80 | 81 | go func() { 82 | for { 83 | c, err := s.l.Accept() 84 | if err != nil { 85 | return 86 | } 87 | go s.handler(c) 88 | } 89 | }() 90 | return nil 91 | } 92 | 93 | func (s *Server) Close() error { 94 | if s.l != nil { 95 | return s.l.Close() 96 | } 97 | return nil 98 | } 99 | 100 | func (s *Server) initListener() (err error) { 101 | switch s.netType { 102 | case TCP: 103 | s.l, err = net.Listen("tcp", net.JoinHostPort(s.bindAddr, strconv.Itoa(s.bindPort))) 104 | case UDP: 105 | s.l, err = libnet.ListenUDP(s.bindAddr, s.bindPort) 106 | case Unix: 107 | s.l, err = net.Listen("unix", s.bindAddr) 108 | default: 109 | return fmt.Errorf("unknown server type: %s", s.netType) 110 | } 111 | return err 112 | } 113 | 114 | func (s *Server) handle(c net.Conn) { 115 | defer c.Close() 116 | 117 | var reader io.Reader = c 118 | if s.netType == UDP { 119 | reader = bufio.NewReader(c) 120 | } 121 | for { 122 | buf, err := rpc.ReadBytes(reader) 123 | if err != nil { 124 | return 125 | } 126 | 127 | if len(s.respContent) > 0 { 128 | buf = s.respContent 129 | } 130 | _, _ = rpc.WriteBytes(c, buf) 131 | } 132 | } 133 | 134 | func (s *Server) BindAddr() string { 135 | return s.bindAddr 136 | } 137 | 138 | func (s *Server) BindPort() int { 139 | return s.bindPort 140 | } 141 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/pkg/cert/generator.go: -------------------------------------------------------------------------------- 1 | package cert 2 | 3 | import ( 4 | "crypto/tls" 5 | "crypto/x509" 6 | "encoding/pem" 7 | "time" 8 | ) 9 | 10 | // Artifacts hosts a private key, its corresponding serving certificate and 11 | // the CA certificate that signs the serving certificate. 12 | type Artifacts struct { 13 | // PEM encoded private key 14 | Key []byte 15 | // PEM encoded serving certificate 16 | Cert []byte 17 | // PEM encoded CA private key 18 | CAKey []byte 19 | // PEM encoded CA certificate 20 | CACert []byte 21 | // Resource version of the certs 22 | ResourceVersion string 23 | } 24 | 25 | // Generator is an interface to provision the serving certificate. 26 | type Generator interface { 27 | // Generate returns a Artifacts struct. 28 | Generate(CommonName string) (*Artifacts, error) 29 | // SetCA sets the PEM-encoded CA private key and CA cert for signing the generated serving cert. 30 | SetCA(caKey, caCert []byte) 31 | } 32 | 33 | // ValidCACert think cert and key are valid if they meet the following requirements: 34 | // - key and cert are valid pair 35 | // - caCert is the root ca of cert 36 | // - cert is for dnsName 37 | // - cert won't expire before time 38 | func ValidCACert(key, cert, caCert []byte, dnsName string, time time.Time) bool { 39 | if len(key) == 0 || len(cert) == 0 || len(caCert) == 0 { 40 | return false 41 | } 42 | // Verify key and cert are valid pair 43 | _, err := tls.X509KeyPair(cert, key) 44 | if err != nil { 45 | return false 46 | } 47 | 48 | // Verify cert is valid for at least 1 year. 49 | pool := x509.NewCertPool() 50 | if !pool.AppendCertsFromPEM(caCert) { 51 | return false 52 | } 53 | block, _ := pem.Decode(cert) 54 | if block == nil { 55 | return false 56 | } 57 | c, err := x509.ParseCertificate(block.Bytes) 58 | if err != nil { 59 | return false 60 | } 61 | ops := x509.VerifyOptions{ 62 | DNSName: dnsName, 63 | Roots: pool, 64 | CurrentTime: time, 65 | } 66 | _, err = c.Verify(ops) 67 | return err == nil 68 | } 69 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/pkg/port/port.go: -------------------------------------------------------------------------------- 1 | package port 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "strconv" 7 | "sync" 8 | 9 | "k8s.io/apimachinery/pkg/util/sets" 10 | ) 11 | 12 | type Allocator struct { 13 | reserved sets.Int 14 | used sets.Int 15 | mu sync.Mutex 16 | } 17 | 18 | // NewAllocator return a port allocator for testing. 19 | // Example: from: 10, to: 20, mod 4, index 1 20 | // Reserved ports: 13, 17 21 | func NewAllocator(from int, to int, mod int, index int) *Allocator { 22 | pa := &Allocator{ 23 | reserved: sets.NewInt(), 24 | used: sets.NewInt(), 25 | } 26 | 27 | for i := from; i <= to; i++ { 28 | if i%mod == index { 29 | pa.reserved.Insert(i) 30 | } 31 | } 32 | return pa 33 | } 34 | 35 | func (pa *Allocator) Get() int { 36 | return pa.GetByName("") 37 | } 38 | 39 | func (pa *Allocator) GetByName(portName string) int { 40 | var builder *nameBuilder 41 | if portName == "" { 42 | builder = &nameBuilder{} 43 | } else { 44 | var err error 45 | builder, err = unmarshalFromName(portName) 46 | if err != nil { 47 | fmt.Println(err, portName) 48 | return 0 49 | } 50 | } 51 | 52 | pa.mu.Lock() 53 | defer pa.mu.Unlock() 54 | 55 | for i := 0; i < 20; i++ { 56 | port := pa.getByRange(builder.rangePortFrom, builder.rangePortTo) 57 | if port == 0 { 58 | return 0 59 | } 60 | 61 | l, err := net.Listen("tcp", net.JoinHostPort("127.0.0.1", strconv.Itoa(port))) 62 | if err != nil { 63 | // Maybe not controlled by us, mark it used. 64 | pa.used.Insert(port) 65 | continue 66 | } 67 | l.Close() 68 | 69 | udpAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort("127.0.0.1", strconv.Itoa(port))) 70 | if err != nil { 71 | continue 72 | } 73 | udpConn, err := net.ListenUDP("udp", udpAddr) 74 | if err != nil { 75 | // Maybe not controlled by us, mark it used. 76 | pa.used.Insert(port) 77 | continue 78 | } 79 | udpConn.Close() 80 | 81 | pa.used.Insert(port) 82 | return port 83 | } 84 | return 0 85 | } 86 | 87 | func (pa *Allocator) getByRange(from, to int) int { 88 | if from <= 0 { 89 | port, _ := pa.reserved.PopAny() 90 | return port 91 | } 92 | 93 | // choose a random port between from - to 94 | ports := pa.reserved.UnsortedList() 95 | for _, port := range ports { 96 | if port >= from && port <= to { 97 | return port 98 | } 99 | } 100 | return 0 101 | } 102 | 103 | func (pa *Allocator) Release(port int) { 104 | if port <= 0 { 105 | return 106 | } 107 | 108 | pa.mu.Lock() 109 | defer pa.mu.Unlock() 110 | 111 | if pa.used.Has(port) { 112 | pa.used.Delete(port) 113 | pa.reserved.Insert(port) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/pkg/port/util.go: -------------------------------------------------------------------------------- 1 | package port 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | const ( 10 | NameDelimiter = "_" 11 | ) 12 | 13 | type NameOption func(*nameBuilder) *nameBuilder 14 | 15 | type nameBuilder struct { 16 | name string 17 | rangePortFrom int 18 | rangePortTo int 19 | } 20 | 21 | func unmarshalFromName(name string) (*nameBuilder, error) { 22 | var builder nameBuilder 23 | arrs := strings.Split(name, NameDelimiter) 24 | switch len(arrs) { 25 | case 2: 26 | builder.name = arrs[1] 27 | case 4: 28 | builder.name = arrs[1] 29 | fromPort, err := strconv.Atoi(arrs[2]) 30 | if err != nil { 31 | return nil, fmt.Errorf("error range port from") 32 | } 33 | builder.rangePortFrom = fromPort 34 | 35 | toPort, err := strconv.Atoi(arrs[3]) 36 | if err != nil { 37 | return nil, fmt.Errorf("error range port to") 38 | } 39 | builder.rangePortTo = toPort 40 | default: 41 | return nil, fmt.Errorf("error port name format") 42 | } 43 | return &builder, nil 44 | } 45 | 46 | func (builder *nameBuilder) String() string { 47 | name := fmt.Sprintf("Port%s%s", NameDelimiter, builder.name) 48 | if builder.rangePortFrom > 0 && builder.rangePortTo > 0 && builder.rangePortTo > builder.rangePortFrom { 49 | name += fmt.Sprintf("%s%d%s%d", NameDelimiter, builder.rangePortFrom, NameDelimiter, builder.rangePortTo) 50 | } 51 | return name 52 | } 53 | 54 | func WithRangePorts(from, to int) NameOption { 55 | return func(builder *nameBuilder) *nameBuilder { 56 | builder.rangePortFrom = from 57 | builder.rangePortTo = to 58 | return builder 59 | } 60 | } 61 | 62 | func GenName(name string, options ...NameOption) string { 63 | name = strings.ReplaceAll(name, "-", "") 64 | name = strings.ReplaceAll(name, "_", "") 65 | builder := &nameBuilder{name: name} 66 | for _, option := range options { 67 | builder = option(builder) 68 | } 69 | return builder.String() 70 | } 71 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/pkg/process/process.go: -------------------------------------------------------------------------------- 1 | package process 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "os/exec" 7 | ) 8 | 9 | type Process struct { 10 | cmd *exec.Cmd 11 | cancel context.CancelFunc 12 | errorOutput *bytes.Buffer 13 | stdOutput *bytes.Buffer 14 | 15 | beforeStopHandler func() 16 | stopped bool 17 | } 18 | 19 | func New(path string, params []string) *Process { 20 | return NewWithEnvs(path, params, nil) 21 | } 22 | 23 | func NewWithEnvs(path string, params []string, envs []string) *Process { 24 | ctx, cancel := context.WithCancel(context.Background()) 25 | cmd := exec.CommandContext(ctx, path, params...) 26 | cmd.Env = envs 27 | p := &Process{ 28 | cmd: cmd, 29 | cancel: cancel, 30 | } 31 | p.errorOutput = bytes.NewBufferString("") 32 | p.stdOutput = bytes.NewBufferString("") 33 | cmd.Stderr = p.errorOutput 34 | cmd.Stdout = p.stdOutput 35 | return p 36 | } 37 | 38 | func (p *Process) Start() error { 39 | return p.cmd.Start() 40 | } 41 | 42 | func (p *Process) Stop() error { 43 | if p.stopped { 44 | return nil 45 | } 46 | defer func() { 47 | p.stopped = true 48 | }() 49 | if p.beforeStopHandler != nil { 50 | p.beforeStopHandler() 51 | } 52 | p.cancel() 53 | return p.cmd.Wait() 54 | } 55 | 56 | func (p *Process) ErrorOutput() string { 57 | return p.errorOutput.String() 58 | } 59 | 60 | func (p *Process) StdOutput() string { 61 | return p.stdOutput.String() 62 | } 63 | 64 | func (p *Process) SetBeforeStopHandler(fn func()) { 65 | p.beforeStopHandler = fn 66 | } 67 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/pkg/rpc/rpc.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "io" 8 | ) 9 | 10 | func WriteBytes(w io.Writer, buf []byte) (int, error) { 11 | out := bytes.NewBuffer(nil) 12 | if err := binary.Write(out, binary.BigEndian, int64(len(buf))); err != nil { 13 | return 0, err 14 | } 15 | 16 | out.Write(buf) 17 | return w.Write(out.Bytes()) 18 | } 19 | 20 | func ReadBytes(r io.Reader) ([]byte, error) { 21 | var length int64 22 | if err := binary.Read(r, binary.BigEndian, &length); err != nil { 23 | return nil, err 24 | } 25 | buffer := make([]byte, length) 26 | n, err := io.ReadFull(r, buffer) 27 | if err != nil { 28 | return nil, err 29 | } 30 | if int64(n) != length { 31 | return nil, errors.New("invalid length") 32 | } 33 | return buffer, nil 34 | } 35 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/pkg/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/base64" 5 | ) 6 | 7 | func BasicAuth(username, passwd string) string { 8 | auth := username + ":" + passwd 9 | return "Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) 10 | } 11 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/plugin/utils.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "crypto/tls" 5 | "encoding/json" 6 | "io" 7 | "net/http" 8 | 9 | plugin "github.com/fatedier/frp/pkg/plugin/server" 10 | "github.com/fatedier/frp/pkg/util/log" 11 | "github.com/fatedier/frp/test/e2e/mock/server/httpserver" 12 | ) 13 | 14 | type Handler func(req *plugin.Request) *plugin.Response 15 | 16 | type NewPluginRequest func() *plugin.Request 17 | 18 | func NewHTTPPluginServer(port int, newFunc NewPluginRequest, handler Handler, tlsConfig *tls.Config) *httpserver.Server { 19 | return httpserver.New( 20 | httpserver.WithBindPort(port), 21 | httpserver.WithTLSConfig(tlsConfig), 22 | httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 23 | r := newFunc() 24 | buf, err := io.ReadAll(req.Body) 25 | if err != nil { 26 | w.WriteHeader(500) 27 | return 28 | } 29 | log.Trace("plugin request: %s", string(buf)) 30 | err = json.Unmarshal(buf, &r) 31 | if err != nil { 32 | w.WriteHeader(500) 33 | return 34 | } 35 | resp := handler(r) 36 | buf, _ = json.Marshal(resp) 37 | log.Trace("plugin response: %s", string(buf)) 38 | _, _ = w.Write(buf) 39 | })), 40 | ) 41 | } 42 | -------------------------------------------------------------------------------- /frp-dev/test/e2e/suites.go: -------------------------------------------------------------------------------- 1 | package e2e 2 | 3 | // CleanupSuite is the boilerplate that can be used after tests on ginkgo were run, on the SynchronizedAfterSuite step. 4 | // Similar to SynchronizedBeforeSuite, we want to run some operations only once (such as collecting cluster logs). 5 | // Here, the order of functions is reversed; first, the function which runs everywhere, 6 | // and then the function that only runs on the first Ginkgo node. 7 | func CleanupSuite() { 8 | // Run on all Ginkgo nodes 9 | } 10 | 11 | // AfterSuiteActions are actions that are run on ginkgo's SynchronizedAfterSuite 12 | func AfterSuiteActions() { 13 | // Run only Ginkgo on node 1 14 | } 15 | -------------------------------------------------------------------------------- /frp-dev/web/frpc/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { "modules": false }] 4 | ], 5 | "plugins": [ 6 | [ 7 | "component", 8 | { 9 | "libraryName": "element-ui", 10 | "styleLibraryName": "theme-chalk" 11 | } 12 | ] 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /frp-dev/web/frpc/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | .idea 6 | .vscode/settings.json 7 | -------------------------------------------------------------------------------- /frp-dev/web/frpc/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: dist build 2 | build: 3 | @npm run build 4 | 5 | dev: 6 | @npm run dev 7 | -------------------------------------------------------------------------------- /frp-dev/web/frpc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frpc-web", 3 | "description": "An admin web ui for frp client.", 4 | "author": "fatedier", 5 | "private": true, 6 | "scripts": { 7 | "dev": "webpack-dev-server -d --inline --hot --env.dev", 8 | "build": "rimraf dist && webpack -p --progress --hide-modules" 9 | }, 10 | "dependencies": { 11 | "element-ui": "^2.5.3", 12 | "vue": "^2.5.22", 13 | "vue-resource": "^1.5.1", 14 | "vue-router": "^3.0.2", 15 | "whatwg-fetch": "^3.0.0" 16 | }, 17 | "engines": { 18 | "node": ">=6" 19 | }, 20 | "devDependencies": { 21 | "autoprefixer": "^9.4.7", 22 | "babel-core": "^6.26.3", 23 | "babel-eslint": "^10.0.1", 24 | "babel-loader": "^7.1.5", 25 | "babel-plugin-component": "^1.1.1", 26 | "babel-preset-es2015": "^6.24.1", 27 | "css-loader": "^2.1.0", 28 | "eslint": "^5.12.1", 29 | "eslint-config-enough": "^0.3.4", 30 | "eslint-loader": "^2.1.1", 31 | "file-loader": "^3.0.1", 32 | "html-loader": "^0.5.5", 33 | "html-webpack-plugin": "^2.24.1", 34 | "less": "^3.9.0", 35 | "less-loader": "^4.1.0", 36 | "postcss-loader": "^3.0.0", 37 | "rimraf": "^2.6.3", 38 | "style-loader": "^0.23.1", 39 | "url-loader": "^1.1.2", 40 | "vue-loader": "^15.6.2", 41 | "vue-template-compiler": "^2.5.22", 42 | "webpack": "^2.7.0", 43 | "webpack-cli": "^3.2.1", 44 | "webpack-dev-server": "^3.1.14" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /frp-dev/web/frpc/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('autoprefixer')() 4 | ] 5 | } -------------------------------------------------------------------------------- /frp-dev/web/frpc/src/App.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 40 | 41 | 74 | -------------------------------------------------------------------------------- /frp-dev/web/frpc/src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M1r0ku/frp-Modify/ca899c3e75b9916bec5b124c5829afb2250f7603/frp-dev/web/frpc/src/assets/favicon.ico -------------------------------------------------------------------------------- /frp-dev/web/frpc/src/components/Overview.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 73 | 74 | 76 | -------------------------------------------------------------------------------- /frp-dev/web/frpc/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | frp client admin UI 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /frp-dev/web/frpc/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | // import ElementUI from 'element-ui' 3 | import { 4 | Button, 5 | Form, 6 | FormItem, 7 | Row, 8 | Col, 9 | Table, 10 | TableColumn, 11 | Menu, 12 | MenuItem, 13 | MessageBox, 14 | Message, 15 | Input 16 | } from 'element-ui' 17 | import lang from 'element-ui/lib/locale/lang/en' 18 | import locale from 'element-ui/lib/locale' 19 | import 'element-ui/lib/theme-chalk/index.css' 20 | import './utils/less/custom.less' 21 | 22 | import App from './App.vue' 23 | import router from './router' 24 | import 'whatwg-fetch' 25 | 26 | locale.use(lang) 27 | 28 | Vue.use(Button) 29 | Vue.use(Form) 30 | Vue.use(FormItem) 31 | Vue.use(Row) 32 | Vue.use(Col) 33 | Vue.use(Table) 34 | Vue.use(TableColumn) 35 | Vue.use(Menu) 36 | Vue.use(MenuItem) 37 | Vue.use(Input) 38 | 39 | Vue.prototype.$msgbox = MessageBox; 40 | Vue.prototype.$confirm = MessageBox.confirm 41 | Vue.prototype.$message = Message 42 | 43 | //Vue.use(ElementUI) 44 | 45 | Vue.config.productionTip = false 46 | 47 | new Vue({ 48 | el: '#app', 49 | router, 50 | template: '', 51 | components: { App } 52 | }) 53 | -------------------------------------------------------------------------------- /frp-dev/web/frpc/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Overview from '../components/Overview.vue' 4 | import Configure from '../components/Configure.vue' 5 | 6 | Vue.use(Router) 7 | 8 | export default new Router({ 9 | routes: [{ 10 | path: '/', 11 | name: 'Overview', 12 | component: Overview 13 | },{ 14 | path: '/configure', 15 | name: 'Configure', 16 | component: Configure, 17 | }] 18 | }) 19 | -------------------------------------------------------------------------------- /frp-dev/web/frpc/src/utils/less/custom.less: -------------------------------------------------------------------------------- 1 | @color: red; 2 | 3 | .el-form-item { 4 | span { 5 | margin-left: 15px; 6 | } 7 | } 8 | 9 | .demo-table-expand { 10 | font-size: 0; 11 | 12 | label { 13 | width: 90px; 14 | color: #99a9bf; 15 | } 16 | 17 | .el-form-item { 18 | margin-right: 0; 19 | margin-bottom: 0; 20 | width: 50%; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /frp-dev/web/frpc/src/utils/status.js: -------------------------------------------------------------------------------- 1 | class ProxyStatus { 2 | constructor(status) { 3 | this.name = status.name 4 | this.type = status.type 5 | this.status = status.status 6 | this.err = status.err 7 | this.local_addr = status.local_addr 8 | this.plugin = status.plugin 9 | this.remote_addr = status.remote_addr 10 | } 11 | } 12 | 13 | export {ProxyStatus} 14 | -------------------------------------------------------------------------------- /frp-dev/web/frps/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { "modules": false }] 4 | ], 5 | "plugins": [ 6 | [ 7 | "component", 8 | { 9 | "libraryName": "element-ui", 10 | "styleLibraryName": "theme-chalk" 11 | } 12 | ] 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /frp-dev/web/frps/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log 5 | .idea 6 | .vscode/settings.json 7 | -------------------------------------------------------------------------------- /frp-dev/web/frps/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: dist build 2 | 3 | build: 4 | @npm run build 5 | 6 | dev: install 7 | @npm run dev 8 | -------------------------------------------------------------------------------- /frp-dev/web/frps/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frps-dashboard", 3 | "description": "A dashboard for frp server.", 4 | "author": "fatedier", 5 | "private": true, 6 | "scripts": { 7 | "dev": "webpack-dev-server -d --inline --hot --env.dev", 8 | "build": "rimraf dist && webpack -p --progress --hide-modules" 9 | }, 10 | "dependencies": { 11 | "bootstrap": "^3.3.7", 12 | "echarts": "^3.5.0", 13 | "element-ui": "^2.3.8", 14 | "humanize-plus": "^1.8.2", 15 | "vue": "^2.5.16", 16 | "vue-resource": "^1.2.1", 17 | "vue-router": "^2.3.0", 18 | "whatwg-fetch": "^2.0.3" 19 | }, 20 | "engines": { 21 | "node": ">=6" 22 | }, 23 | "devDependencies": { 24 | "autoprefixer": "^6.6.0", 25 | "babel-core": "^6.21.0", 26 | "babel-eslint": "^7.1.1", 27 | "babel-loader": "^6.4.0", 28 | "babel-plugin-component": "^1.1.1", 29 | "babel-preset-es2015": "^6.13.2", 30 | "css-loader": "^0.27.0", 31 | "eslint": "^3.12.2", 32 | "eslint-config-enough": "^0.2.2", 33 | "eslint-loader": "^1.6.3", 34 | "file-loader": "^0.10.1", 35 | "html-loader": "^0.4.5", 36 | "html-webpack-plugin": "^2.24.1", 37 | "less": "^3.0.4", 38 | "less-loader": "^4.1.0", 39 | "postcss-loader": "^1.3.3", 40 | "rimraf": "^2.5.4", 41 | "style-loader": "^0.13.2", 42 | "url-loader": "^1.0.1", 43 | "vue-loader": "^15.0.10", 44 | "vue-template-compiler": "^2.1.8", 45 | "webpack": "^2.2.0-rc.4", 46 | "webpack-dev-server": "^3.1.4" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /frp-dev/web/frps/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('autoprefixer')() 4 | ] 5 | } -------------------------------------------------------------------------------- /frp-dev/web/frps/src/App.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 48 | 49 | 82 | -------------------------------------------------------------------------------- /frp-dev/web/frps/src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/M1r0ku/frp-Modify/ca899c3e75b9916bec5b124c5829afb2250f7603/frp-dev/web/frps/src/assets/favicon.ico -------------------------------------------------------------------------------- /frp-dev/web/frps/src/components/Traffic.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 34 | 35 | 37 | -------------------------------------------------------------------------------- /frp-dev/web/frps/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | frps dashboard 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /frp-dev/web/frps/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | //import ElementUI from 'element-ui' 3 | import { 4 | Button, 5 | Form, 6 | FormItem, 7 | Row, 8 | Col, 9 | Table, 10 | TableColumn, 11 | Popover, 12 | Menu, 13 | Submenu, 14 | MenuItem, 15 | Tag 16 | } from 'element-ui' 17 | import lang from 'element-ui/lib/locale/lang/en' 18 | import locale from 'element-ui/lib/locale' 19 | import 'element-ui/lib/theme-chalk/index.css' 20 | import './utils/less/custom.less' 21 | 22 | import App from './App.vue' 23 | import router from './router' 24 | import 'whatwg-fetch' 25 | 26 | locale.use(lang) 27 | 28 | Vue.use(Button) 29 | Vue.use(Form) 30 | Vue.use(FormItem) 31 | Vue.use(Row) 32 | Vue.use(Col) 33 | Vue.use(Table) 34 | Vue.use(TableColumn) 35 | Vue.use(Popover) 36 | Vue.use(Menu) 37 | Vue.use(Submenu) 38 | Vue.use(MenuItem) 39 | Vue.use(Tag) 40 | 41 | Vue.config.productionTip = false 42 | 43 | new Vue({ 44 | el: '#app', 45 | router, 46 | template: '', 47 | components: { App } 48 | }) 49 | -------------------------------------------------------------------------------- /frp-dev/web/frps/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import Overview from '../components/Overview.vue' 4 | import ProxiesTcp from '../components/ProxiesTcp.vue' 5 | import ProxiesUdp from '../components/ProxiesUdp.vue' 6 | import ProxiesHttp from '../components/ProxiesHttp.vue' 7 | import ProxiesHttps from '../components/ProxiesHttps.vue' 8 | import ProxiesStcp from '../components/ProxiesStcp.vue' 9 | import ProxiesSudp from '../components/ProxiesSudp.vue' 10 | 11 | Vue.use(Router) 12 | 13 | export default new Router({ 14 | routes: [{ 15 | path: '/', 16 | name: 'Overview', 17 | component: Overview 18 | }, { 19 | path: '/proxies/tcp', 20 | name: 'ProxiesTcp', 21 | component: ProxiesTcp 22 | }, { 23 | path: '/proxies/udp', 24 | name: 'ProxiesUdp', 25 | component: ProxiesUdp 26 | }, { 27 | path: '/proxies/http', 28 | name: 'ProxiesHttp', 29 | component: ProxiesHttp 30 | }, { 31 | path: '/proxies/https', 32 | name: 'ProxiesHttps', 33 | component: ProxiesHttps 34 | }, { 35 | path: '/proxies/stcp', 36 | name: 'ProxiesStcp', 37 | component: ProxiesStcp 38 | }, { 39 | path: '/proxies/sudp', 40 | name: 'ProxiesSudp', 41 | component: ProxiesSudp 42 | }] 43 | }) 44 | -------------------------------------------------------------------------------- /frp-dev/web/frps/src/utils/less/custom.less: -------------------------------------------------------------------------------- 1 | @color: red; 2 | 3 | .el-form-item { 4 | span { 5 | margin-left: 15px; 6 | } 7 | } 8 | 9 | .demo-table-expand { 10 | font-size: 0; 11 | 12 | label { 13 | width: 90px; 14 | color: #99a9bf; 15 | } 16 | 17 | .el-form-item { 18 | margin-right: 0; 19 | margin-bottom: 0; 20 | width: 50%; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /xor.py: -------------------------------------------------------------------------------- 1 | def str2xor(messages, key): 2 | res = "" 3 | for index, msg in enumerate(messages): 4 | res += chr( ord(msg) ^ ord(key[index % len(key)]) ) 5 | print(res) 6 | 7 | if __name__ == '__main__': 8 | ip = "192.168.111.1" # E\AZZSAZTBEET 9 | port = "7000" # CUCD 10 | 11 | key = "testkey" 12 | str2xor(ip, key) 13 | str2xor(port, key) 14 | --------------------------------------------------------------------------------