├── 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 | [](https://travis-ci.org/fatedier/frp)
4 | [](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 | 
90 |
91 | ### 支付宝扫码捐赠
92 |
93 | 
94 |
95 | ### 微信支付捐赠
96 |
97 | 
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 |
2 |
3 |
8 |
9 |
10 |
11 |
12 | Overview
13 | Configure
14 | Help
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
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 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
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 |
2 |
3 |
8 |
9 |
10 |
11 |
12 | Overview
13 |
14 | Proxies
15 | TCP
16 | UDP
17 | HTTP
18 | HTTPS
19 | STCP
20 | SUDP
21 |
22 | Help
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
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 |
2 |
3 |
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 |
--------------------------------------------------------------------------------