Compare commits

..

116 Commits

Author SHA1 Message Date
fatedier
a301046f3d Merge pull request #3147 from fatedier/dev
bump version
2022-10-26 23:18:40 +08:00
fatedier
34ab6b0e74 add release notes 2022-10-26 23:09:17 +08:00
fatedier
cf66ca10b4 improve http group load balancing (#3131) 2022-10-19 12:14:35 +08:00
fatedier
3fbe6b659e adjust sponsors (#3136) 2022-10-19 12:14:09 +08:00
fatedier
6a71d71e58 improve not found response (#3121) 2022-10-09 12:13:27 +08:00
fatedier
6ecc97c857 update deps (#3094) 2022-09-08 17:16:45 +08:00
Abirdcfly
ba492f07c3 chore: remove duplicate word in comments (#3081) 2022-08-29 12:25:36 +08:00
fatedier
9d077b02cf lint by golangci-lint (#3080) 2022-08-29 01:02:53 +08:00
Dingli Zhang
f4e4fbea62 Add support for riscv64 (#3071) 2022-08-23 10:10:04 +08:00
Yonatan Koren
3e721d122b Update README.md (#3062) 2022-08-16 20:47:56 +08:00
chenjiayao
1bc899ec12 [client] Remove redundant function parameters (#3016) 2022-07-14 15:31:32 +08:00
ChristLZS
6f2571980c [client] Fixed a bug where service loops reconnection after disconnection.service is shut down and can not exit goroutine (#3012)
Co-authored-by: lizhisheng <zhishengli@deepglint.com>
2022-07-12 18:43:58 +08:00
fatedier
8888610d83 Merge pull request #3010 from fatedier/dev
release v0.44.0
2022-07-11 00:10:43 +08:00
fatedier
fa7c05c617 release note for v0.44.0 2022-07-11 00:06:57 +08:00
EMRE ÇELİK
218b354f82 Server Dashboard SSL Support (#2982) 2022-06-27 10:08:02 +08:00
fatedier
c652b8ef07 fix ipv6 address parsing (#2978) 2022-06-14 14:24:34 +08:00
fatedier
5b8b145577 Use auto generated certificates if plugin_key_path and plugin_crt_path are empty for plugin https2https and https2http. (#2968) 2022-06-05 17:15:28 +08:00
fatedier
fe5fb0326b Merge pull request #2955 from fatedier/dev
bump version to v0.43.0
2022-05-27 16:27:19 +08:00
fatedier
0711295b0a release note for v0.43.0 (#2954) 2022-05-27 16:02:36 +08:00
fatedier
4af85da0c2 type http/tcpmux proxy support route_by_http_user, tcpmux support passthourgh mode (#2932) 2022-05-26 23:57:30 +08:00
fatedier
bd89eaba2f remove systemd files 2022-04-29 21:31:48 +08:00
fatedier
a72259c604 docker build&push: some adjustments 2022-04-29 01:15:42 +08:00
蓝云Reyes
44eb513f05 Update docker image build file (#2892)
* update docker image building
2022-04-29 01:12:07 +08:00
fatedier
eb1e19a821 Merge pull request #2906 from fatedier/dev
bump version
2022-04-22 11:32:27 +08:00
fatedier
6c658586f6 bump version to v0.42.0 2022-04-22 11:15:23 +08:00
fatedier
888ed25314 dependency: update github.com/pires/go-proxyproto to v0.6.2 (#2894) 2022-04-15 11:36:00 +08:00
fatedier
21240ed962 some improvements 2022-04-14 11:24:36 +08:00
Colin Adler
6481870d03 fix: data races when accessing github.com/fatedier/frp/client.(*Service).ctl (#2891)
* fix: data race in client/service.go

* review fixes
2022-04-14 11:14:19 +08:00
fatedier
a7a4ba270d fix error parsing env values (#2886) 2022-04-05 12:48:57 +08:00
cui fliter
915d9f4c09 fix some typos (#2882)
Signed-off-by: cuishuang <imcusg@gmail.com>
2022-04-02 17:35:51 +08:00
fatedier
18a2af4703 frpc: support multiple confs (#2873) 2022-03-28 12:12:35 +08:00
fatedier
305e40fa8a update .goreleaser.yml 2022-03-23 21:47:43 +08:00
fatedier
10f2620131 Merge pull request #2869 from fatedier/dev
bump version to v0.41.0
2022-03-23 21:19:59 +08:00
fatedier
4acae540c8 support go1.18 and remove go1.16 (#2868) 2022-03-23 21:15:01 +08:00
fatedier
11b13533a0 add release note (#2867) 2022-03-23 20:14:55 +08:00
fatedier
100d556336 support tcp keepalive params (#2863) 2022-03-22 19:29:30 +08:00
Blizard
452fe25cc6 feat: SUDP alway reconnect and print too much log when no data ready (#2844)
* feat: random sleep duration before reconnecting

* fix: bug
2022-03-17 12:03:20 +08:00
fatedier
63efa6b776 support pprof (#2849) 2022-03-17 11:42:59 +08:00
fatedier
37c27169ac workflows: update stale action (#2846) 2022-03-15 11:53:14 +08:00
fatedier
ce677820c6 Merge pull request #2834 from fatedier/dev
bump version
2022-03-11 19:51:32 +08:00
fatedier
1f88a7a0b8 bump version to v0.40.0 (#2833) 2022-03-11 19:45:34 +08:00
Johan Hernefeldt
eeea7602d9 bugfix: Issue #2831 - Cant connect to frps behind ingress with tls (#2832)
Co-authored-by: Johan Hernefeldt <johan.hernefeldt@moralis.io>
2022-03-11 14:51:47 +08:00
Harry Cheng
bf635c0e90 Notify server plugins when a proxy is closed (#2823)
* add close proxy op

* Move to actual closing routine

* Fix e2e tests for CloseProxy

* Add warning on resource exhaustion

* Add CloseProxy to manual close

* retuen errors to `CloseProxy` callers
2022-03-08 15:08:09 +08:00
Blizard
cd31359a27 feat: support add additional params for OIDC (#2814)
* feat: support add additional params and test access by auth0

* fix: config name

Co-authored-by: blizard863 <760076784@qq.com>
2022-03-07 14:23:49 +08:00
fatedier
19739ed31a random sleep duration before reconnecting (#2816) 2022-02-24 11:59:36 +08:00
fatedier
10100c28d9 client: add dial_server_timeout (#2805) 2022-02-19 16:49:21 +08:00
fatedier
88fcc079e8 Merge pull request #2792 from fatedier/dev
bump version
2022-02-09 16:11:20 +08:00
fatedier
ddc1e163c4 update README 2022-02-09 15:42:34 +08:00
fatedier
d20a6d3d75 update release note 2022-02-09 15:23:01 +08:00
fatedier
6194273615 use net.JoinHostPort instead of fmt.Sprintf (#2791) 2022-02-09 15:19:35 +08:00
fatedier
b2311e55e7 add new sponsor logo (#2785) 2022-01-28 15:29:43 +08:00
fatedier
07873d471f doc: update donation section (#2783) 2022-01-26 20:56:00 +08:00
fatedier
2dab5d0bca Merge pull request #2782 from fatedier/dev
bump version
2022-01-26 20:17:54 +08:00
fatedier
9ca2b586f8 update release note 2022-01-26 20:13:25 +08:00
fatedier
e59eacb8a2 version: bump to v0.39.0 2022-01-26 19:53:22 +08:00
Blizard
0db4fc07fb feat: support set local ip in client when connect server (#2774)
* feat: support set local ip in client when connect server

* fix: typo

Co-authored-by: blizard863 <760076784@qq.com>
2022-01-26 19:47:40 +08:00
fatedier
70f4caac23 move dial functions into golib (#2767) 2022-01-20 20:03:07 +08:00
fatedier
293003fcdb allow to disable application layer heartbeat to reduce traffic cost (#2758)
fix #2754
2022-01-13 14:26:07 +08:00
fatedier
4bfc89d988 update doc for Login operation 2022-01-11 16:36:56 +08:00
fatedier
22412851b4 server plugin: add client address in Login operation, fix #2742 (#2751) 2022-01-11 16:32:20 +08:00
bobo liu
e9775bd70f doc: no X-Real-IP in http proxy (#2743) 2022-01-09 23:47:28 +08:00
nitinSophos
ff7b8b0b62 ISSUE: 2730 Alpine version update for security fixes (#2731)
update docker file to pick alpine:3
2022-01-04 10:40:45 +08:00
fatedier
491c1d7dc4 add new sponsor (#2729) 2022-01-02 14:31:08 +08:00
Blizard
ea568e8a4f refactor: refine pkg net utils (#2720)
* refactor: refine pkg net utils

* fix: x

Co-authored-by: blizard863 <760076784@qq.com>
2021-12-28 21:14:57 +08:00
fatedier
0fb6aeef58 update sponsor content 2021-12-17 15:09:23 +08:00
fatedier
032f33fe5a update readme add sponsor pic (#2704) 2021-12-17 15:05:39 +08:00
fatedier
bbc8b438d5 doc: add local_port for tcpmux example (#2695) 2021-12-08 18:41:21 +08:00
fatedier
05b1ace21f remove authentication for healthz api (#2672) 2021-11-23 14:14:27 +08:00
fatedier
cbdd73b94f typo 2021-11-12 22:18:36 +08:00
fatedier
bf06e3b107 doc for v2 (#2648) 2021-11-09 14:18:40 +08:00
fatedier
143750901e Merge pull request #2638 from fatedier/dev
bump version to v0.38.0
2021-10-25 20:31:13 +08:00
fatedier
71489d194c e2e: add delay duration for monitor case 2021-10-25 20:27:22 +08:00
fatedier
85aa3df256 bump version 2021-10-25 20:14:21 +08:00
fatedier
f1a51eba18 client: lint 2021-10-19 15:02:45 +08:00
Blizard
1d26ea440b fix: kcp protocol cause delay release resource (#2621)
Co-authored-by: blizard863 <760076784@qq.com>
2021-10-19 14:57:26 +08:00
Blizard
998e678a7f fix: typo (#2614)
Co-authored-by: blizard863 <760076784@qq.com>
2021-10-11 12:11:59 +08:00
kekeimiku
0cee1877e3 refactor: move from io/ioutil to io and os package (#2592) 2021-09-29 10:33:57 +08:00
fatedier
72a7fd948e Update bug_report.yaml 2021-09-03 11:52:04 +08:00
fatedier
357c9b0dcb use github issue form (#2563) 2021-09-03 11:47:55 +08:00
fatedier
14bd0716d0 Create FUNDING.yml (#2558) 2021-08-31 20:28:13 +08:00
bobo liu
2f74f54f18 Let's get rid of ugly statik (#2255)
* Get rid of ugly statik

go1.16 introduced the embed package, it's the more graceful solution for embedding file into binary.
https://golang.org/pkg/embed/

* remove statik totally

* split go and static files in assets
2021-08-17 20:20:04 +08:00
fatedier
a62a9431b1 support go1.17 and remove go1.15 (#2532) 2021-08-17 15:26:43 +08:00
fatedier
42745a3da2 frpc: add disable_custom_tls_first_byte to not send first custom tls to frps (#2520) 2021-08-11 23:10:35 +08:00
fatedier
82f80a22be add healthz api (#2511) 2021-08-04 14:33:53 +08:00
fatedier
f570dcb307 update stale action 2021-08-04 10:51:18 +08:00
fatedier
997d406ec2 Merge pull request #2508 from fatedier/dev
bump version
2021-08-03 23:13:31 +08:00
fatedier
87e60683ed add release note for v0.37.1 (#2507) 2021-08-03 23:10:27 +08:00
fatedier
86b2e686a5 vhost: use new readClientHello function (#2504) 2021-08-03 22:58:03 +08:00
fatedier
09f39de74e add more e2e test (#2505) 2021-08-02 13:07:28 +08:00
fatedier
2a68c1152f dep: update github.com/hashicorp/yamux to latest (#2472) 2021-07-08 10:42:28 +08:00
Fishbone
df5859b5f7 Fix server-side proxy inappropriate quit when met accept: too many open files error (#2467) 2021-07-05 10:27:15 +08:00
Blizard
3dd888a9ea fix: stuct name typo (#2458)
Co-authored-by: tanghuafa <tanghuafa@bytedance.com>
2021-06-24 16:33:52 +08:00
fatedier
a51e221db3 add real ip test 2021-06-21 19:35:52 +08:00
fatedier
fe4e9b55f3 update github.com/pires/go-proxyproto to v0.5.0 2021-06-21 19:35:52 +08:00
fatedier
3f11b6a082 update k8s.io/apimachinery to v0.21.2 2021-06-21 19:35:52 +08:00
fatedier
8a333c2ae0 update github.com/stretchr/testify to v1.7.0 2021-06-21 19:35:52 +08:00
fatedier
1fd6ba2738 update github.com/spf13/cobra to v1.1.3 2021-06-21 19:35:52 +08:00
fatedier
a98a9616f6 update dependency github.com/rodaine/table to v1.0.1 2021-06-21 19:35:52 +08:00
fatedier
95cd9ab900 update dependency github.com/prometheus/client_golang to v1.11.0 2021-06-21 19:35:52 +08:00
fatedier
900454e58b more e2e test cases (#2450) 2021-06-18 16:48:36 +08:00
fatedier
c7d4637382 fix web js (#2444) 2021-06-09 11:53:39 +08:00
ztz
56925961df Typo: josn => json (#2429) 2021-06-04 11:21:52 +08:00
fatedier
cfd1a3128a Merge pull request #2426 from fatedier/dev
update workflow file
2021-06-03 00:59:21 +08:00
fatedier
2393923870 update build image go version 2021-06-03 00:56:07 +08:00
fatedier
5f594e9a71 fix goreleaser 2021-06-03 00:46:56 +08:00
fatedier
57577ea044 Merge pull request #2425 from fatedier/dev
bump version
2021-06-03 00:14:32 +08:00
fatedier
8637077d90 update version 2021-06-03 00:10:35 +08:00
fatedier
ccb85a9926 update README 2021-06-03 00:07:51 +08:00
fatedier
02b12df887 frpc: consider include configs for verify and reload command (#2424) 2021-06-02 23:54:22 +08:00
fatedier
c32a2ed140 frpc: support 'includes' in common section to include proxy configs in other files(#2421) 2021-06-02 00:39:05 +08:00
Wu Han
9ae322cccf readme: fix toc hyperlinx (#2401) 2021-05-17 17:29:13 +08:00
fatedier
9cebfccb39 cmd: add verify command to verify if config file syntax is valid (#2389) 2021-05-12 12:15:22 +08:00
fatedier
630dad50ed web: support sudp in dashboard (#2385) 2021-05-11 13:37:14 +08:00
fatedier
0d84da91d4 change default value of dashboard_user and dashboard_pwd to empty string (#2383) 2021-05-10 23:04:26 +08:00
fatedier
2408f1df04 frpc: fix that login_fail_exit invalid if protocol=kcp (#2363) 2021-04-22 18:33:10 +08:00
fatedier
fbaa5f866e add e2e tests (#2334) 2021-03-31 16:57:39 +08:00
211 changed files with 7758 additions and 5128 deletions

View File

@@ -1,17 +1,17 @@
version: 2 version: 2
jobs: jobs:
test1: go-version-latest:
docker: docker:
- image: circleci/golang:1.16-node - image: cimg/go:1.19-node
working_directory: /go/src/github.com/fatedier/frp resource_class: large
steps: steps:
- checkout - checkout
- run: make - run: make
- run: make alltest - run: make alltest
test2: go-version-last:
docker: docker:
- image: circleci/golang:1.15-node - image: cimg/go:1.18-node
working_directory: /go/src/github.com/fatedier/frp resource_class: large
steps: steps:
- checkout - checkout
- run: make - run: make
@@ -21,5 +21,5 @@ workflows:
version: 2 version: 2
build_and_test: build_and_test:
jobs: jobs:
- test1 - go-version-latest
- test2 - go-version-last

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
# These are supported funding model platforms
github: [fatedier]

View File

@@ -1,44 +0,0 @@
---
name: Bug Report
about: Bug Report for FRP
title: ''
labels: Requires Testing
assignees: ''
---
<!-- From Chinese to English by machine translation, welcome to revise and polish. -->
<!-- ⚠️⚠️ Incomplete reports will be marked as invalid, and closed, with few exceptions ⚠️⚠️ -->
<!-- in addition, please use search well so that the same solution can be found in the feedback, we will close it directly -->
<!-- for convenience of differentiation, use FRPS or FRPC to refer to the FRP server or client -->
**[REQUIRED] hat version of frp are you using**
<!-- Use ./frpc -v or ./frps -v -->
Version:
**[REQUIRED] What operating system and processor architecture are you using**
OS:
CPU architecture:
**[REQUIRED] description of errors**
**confile**
<!-- Please pay attention to hiding the token, server_addr and other privacy information -->
**log file**
<!-- If the file is too large, use Pastebin, for example https://pastebin.ubuntu.com/ -->
**Steps to reproduce the issue**
1.
2.
3.
**Supplementary information**
**Can you guess what caused this issue**
**Checklist**:
<!--- Make sure you've completed the following steps (put an "X" between of brackets): -->
- [] I included all information required in the sections above
- [] I made sure there are no duplicates of this report [(Use Search)](https://github.com/fatedier/frp/issues?q=is%3Aissue)

77
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file
View File

@@ -0,0 +1,77 @@
name: Bug report
description: Report a bug to help us improve frp
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: bug-description
attributes:
label: Bug Description
description: Tell us what issues you ran into
placeholder: Include information about what you tried, what you expected to happen, and what actually happened. The more details, the better!
validations:
required: true
- type: input
id: frpc-version
attributes:
label: frpc Version
description: Include the output of `frpc -v`
validations:
required: true
- type: input
id: frps-version
attributes:
label: frps Version
description: Include the output of `frps -v`
validations:
required: true
- type: input
id: system-architecture
attributes:
label: System Architecture
description: Include which architecture you used, such as `linux/amd64`, `windows/amd64`
validations:
required: true
- type: textarea
id: config
attributes:
label: Configurations
description: Include what configurrations you used and ran into this problem
placeholder: Pay attention to hiding the token and password in your output
validations:
required: true
- type: textarea
id: log
attributes:
label: Logs
description: Prefer you providing releated error logs here
placeholder: Pay attention to hiding your personal informations
- type: textarea
id: steps-to-reproduce
attributes:
label: Steps to reproduce
description: How to reproduce it? It's important for us to find the bug
value: |
1.
2.
3.
...
- type: checkboxes
id: area
attributes:
label: Affected area
options:
- label: "Docs"
- label: "Installation"
- label: "Performance and Scalability"
- label: "Security"
- label: "User Experience"
- label: "Test and Release"
- label: "Developer Infrastructure"
- label: "Client Plugin"
- label: "Server Plugin"
- label: "Extensions"
- label: "Others"

View File

@@ -1,5 +1 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links:
- name: DOCS
url: https://github.com/fatedier/frp
about: Here you can find out how to configure frp.

View File

@@ -1,22 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: "[+] Enhancement"
assignees: ''
---
<!-- From Chinese to English by machine translation, welcome to revise and polish. -->
**The solution you want**
<!--A clear and concise description of the solution you want. -->
**Alternatives considered**
<!--A clear and concise description of any alternative solutions or features you have considered. -->
**How to implement this function**
<!--Implementation steps for the solution you want. -->
**Application scenarios of this function**
<!--Make a clear and concise description of the application scenario of the solution you want. -->

View File

@@ -0,0 +1,36 @@
name: Feature Request
description: Suggest an idea to improve frp
title: "[Feature Request] "
body:
- type: markdown
attributes:
value: |
This is only used to request new product features.
- type: textarea
id: feature-request
attributes:
label: Describe the feature request
description: Tell us what's you want and why it should be added in frp.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Describe alternatives you've considered
- type: checkboxes
id: area
attributes:
label: Affected area
options:
- label: "Docs"
- label: "Installation"
- label: "Performance and Scalability"
- label: "Security"
- label: "User Experience"
- label: "Test and Release"
- label: "Developer Infrastructure"
- label: "Client Plugin"
- label: "Server Plugin"
- label: "Extensions"
- label: "Others"

View File

@@ -10,78 +10,45 @@ on:
required: true required: true
default: 'test' default: 'test'
jobs: jobs:
binary:
name: Build Golang project
runs-on: ubuntu-latest
steps:
-
name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: 1.15
-
run: go version
-
name: Check out code into the Go module directory
uses: actions/checkout@v2
-
name: Build
run: make build
-
name: Archive artifacts for frpc
uses: actions/upload-artifact@v1
with:
name: frpc
path: bin/frpc
-
name: Archive artifacts for frps
uses: actions/upload-artifact@v1
with:
name: frps
path: bin/frps
image: image:
name: Build Image from Dockerfile and binaries name: Build Image from Dockerfile and binaries
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: binary
steps: steps:
# environment # environment
- - name: Checkout
name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
fetch-depth: '0' fetch-depth: '0'
-
name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v1 uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1 uses: docker/setup-buildx-action@v1
# download binaries of frpc and frps
-
name: Download binary of frpc
uses: actions/download-artifact@v2
with:
name: frpc
path: bin/frpc
-
name: Download binary of frps
uses: actions/download-artifact@v2
with:
name: frps
path: bin/frps
# get image tag name # get image tag name
- - name: Get Image Tag Name
name: Get Image Tag Name
run: | run: |
if [ x${{ github.event.inputs.tag }} == x"" ]; then if [ x${{ github.event.inputs.tag }} == x"" ]; then
echo "TAG_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV echo "TAG_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
else else
echo "TAG_NAME=${{ github.event.inputs.tag }}" >> $GITHUB_ENV echo "TAG_NAME=${{ github.event.inputs.tag }}" >> $GITHUB_ENV
fi fi
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Login to the GPR
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GPR_TOKEN }}
# prepare image tags # prepare image tags
- - name: Prepare Image Tags
name: Prepare Image Tags
run: | run: |
echo "DOCKERFILE_FRPC_PATH=dockerfiles/Dockerfile-for-frpc" >> $GITHUB_ENV echo "DOCKERFILE_FRPC_PATH=dockerfiles/Dockerfile-for-frpc" >> $GITHUB_ENV
echo "DOCKERFILE_FRPS_PATH=dockerfiles/Dockerfile-for-frps" >> $GITHUB_ENV echo "DOCKERFILE_FRPS_PATH=dockerfiles/Dockerfile-for-frps" >> $GITHUB_ENV
@@ -89,27 +56,25 @@ jobs:
echo "TAG_FRPS=fatedier/frps:${{ env.TAG_NAME }}" >> $GITHUB_ENV echo "TAG_FRPS=fatedier/frps:${{ env.TAG_NAME }}" >> $GITHUB_ENV
echo "TAG_FRPC_GPR=ghcr.io/fatedier/frpc:${{ env.TAG_NAME }}" >> $GITHUB_ENV echo "TAG_FRPC_GPR=ghcr.io/fatedier/frpc:${{ env.TAG_NAME }}" >> $GITHUB_ENV
echo "TAG_FRPS_GPR=ghcr.io/fatedier/frps:${{ env.TAG_NAME }}" >> $GITHUB_ENV echo "TAG_FRPS_GPR=ghcr.io/fatedier/frps:${{ env.TAG_NAME }}" >> $GITHUB_ENV
# build images
- - name: Build and push frpc
name: Build Images uses: docker/build-push-action@v2
run: | with:
# for Docker hub context: .
docker build --file ${{ env.DOCKERFILE_FRPC_PATH }} --tag ${{ env.TAG_FRPC }} . file: ./dockerfiles/Dockerfile-for-frpc
docker build --file ${{ env.DOCKERFILE_FRPS_PATH }} --tag ${{ env.TAG_FRPS }} . platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le,linux/riscv64,linux/s390x
# for GPR push: true
docker build --file ${{ env.DOCKERFILE_FRPC_PATH }} --tag ${{ env.TAG_FRPC_GPR }} . tags: |
docker build --file ${{ env.DOCKERFILE_FRPS_PATH }} --tag ${{ env.TAG_FRPS_GPR }} . ${{ env.TAG_FRPC }}
# push to dockerhub ${{ env.TAG_FRPC_GPR }}
-
name: Publish to Dockerhub - name: Build and push frps
run: | uses: docker/build-push-action@v2
echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login --username ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin with:
docker push ${{ env.TAG_FRPC }} context: .
docker push ${{ env.TAG_FRPS }} file: ./dockerfiles/Dockerfile-for-frps
# push to gpr platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le,linux/riscv64,linux/s390x
- push: true
name: Publish to GPR tags: |
run: | ${{ env.TAG_FRPS }}
echo ${{ secrets.GPR_TOKEN }} | docker login ghcr.io --username ${{ github.repository_owner }} --password-stdin ${{ env.TAG_FRPS_GPR }}
docker push ${{ env.TAG_FRPC_GPR }}
docker push ${{ env.TAG_FRPS_GPR }}

41
.github/workflows/golangci-lint.yml vendored Normal file
View File

@@ -0,0 +1,41 @@
name: golangci-lint
on:
push:
branches:
- master
- dev
pull_request:
permissions:
contents: read
# Optional: allow read access to pull request. Use with `only-new-issues` option.
pull-requests: read
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v3
with:
go-version: 1.19
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
version: v1.49.0
# Optional: golangci-lint command line arguments.
# args: --issues-exit-code=0
# Optional: show only new issues if it's a pull request. The default value is `false`.
# only-new-issues: true
# Optional: if set to true then the all caching functionality will be complete disabled,
# takes precedence over all other caching options.
# skip-cache: true
# Optional: if set to true then the action don't cache or restore ~/go/pkg.
# skip-pkg-cache: true
# Optional: if set to true then the action don't cache or restore ~/.cache/go-build.
# skip-build-cache: true

View File

@@ -15,7 +15,11 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.16 go-version: 1.19
- run: |
# https://github.com/actions/setup-go/issues/107
cp -f `which go` /usr/bin/go
- name: Make All - name: Make All
run: | run: |

View File

@@ -12,15 +12,17 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v3 - uses: actions/stale@v5
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Issues go stale after 45d of inactivity. Stale issues rot after an additional 10d of inactivity and eventually close.' stale-issue-message: 'Issues go stale after 30d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.'
stale-pr-message: 'Issues go stale after 45d of inactivity. Stale issues rot after an additional 10d of inactivity and eventually close.' stale-pr-message: "PRs go stale after 30d of inactivity. Stale PRs rot after an additional 7d of inactivity and eventually close."
stale-issue-label: 'lifecycle/stale' stale-issue-label: 'lifecycle/stale'
exempt-issue-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned' exempt-issue-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned'
stale-pr-label: 'lifecycle/stale' stale-pr-label: 'lifecycle/stale'
exempt-pr-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned' exempt-pr-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned'
days-before-stale: 45 days-before-stale: 30
days-before-close: 10 days-before-close: 7
debug-only: ${{ github.event.inputs.debug-only }} debug-only: ${{ github.event.inputs.debug-only }}
exempt-all-pr-milestones: true
exempt-all-pr-assignees: true

140
.golangci.yml Normal file
View File

@@ -0,0 +1,140 @@
service:
# When updating this, also update the version stored in docker/build-tools/Dockerfile in the istio/tools repo.
golangci-lint-version: 1.49.x # use the fixed version to not introduce new linters unexpectedly
run:
concurrency: 4
# timeout for analysis, e.g. 30s, 5m, default is 1m
deadline: 20m
build-tags:
- integ
- integfuzz
# which dirs to skip: they won't be analyzed;
# can use regexp here: generated.*, regexp is applied on full path;
# default value is empty list, but next dirs are always skipped independently
# from this option's value:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
skip-dirs:
- genfiles$
- vendor$
- bin$
# which files to skip: they will be analyzed, but issues from them
# won't be reported. Default value is empty list, but there is
# no need to include all autogenerated files, we confidently recognize
# autogenerated files. If it's not please let us know.
skip-files:
- ".*\\.pb\\.go"
- ".*\\.gen\\.go"
linters:
disable-all: true
enable:
- unused
- errcheck
- exportloopref
- gocritic
- gofumpt
- goimports
- revive
- gosimple
- govet
- ineffassign
- lll
- misspell
- staticcheck
- stylecheck
- typecheck
- unconvert
- unparam
- gci
- gosec
- asciicheck
- prealloc
- predeclared
- makezero
fast: false
linters-settings:
errcheck:
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
# default is false: such cases aren't reported by default.
check-type-assertions: false
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
# default is false: such cases aren't reported by default.
check-blank: false
govet:
# report about shadowed variables
check-shadowing: false
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true
misspell:
# Correct spellings using locale preferences for US or UK.
# Default is to use a neutral variety of English.
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
locale: US
ignore-words:
- cancelled
- marshalled
lll:
# max line length, lines longer will be reported. Default is 120.
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
line-length: 160
# tab width in spaces. Default to 1.
tab-width: 1
gocritic:
disabled-checks:
- exitAfterDefer
unused:
check-exported: false
unparam:
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
check-exported: false
gci:
sections:
- standard
- default
- prefix(github.com/fatedier/frp/)
gosec:
severity: "low"
confidence: "low"
excludes:
- G102
- G112
- G306
- G401
- G402
- G404
- G501
issues:
# List of regexps of issue texts to exclude, empty list by default.
# But independently from this option we use default exclude patterns,
# it can be disabled by `exclude-use-default: false`. To list all
# excluded by default patterns execute `golangci-lint run --help`
# exclude:
# - composite literal uses unkeyed fields
exclude-rules:
# Exclude some linters from running on test files.
- path: _test\.go$|^tests/|^samples/
linters:
- errcheck
- maligned
# Independently from option `exclude` we use default exclude patterns,
# it can be disabled by this option. To list all
# excluded by default patterns execute `golangci-lint run --help`.
# Default value for this option is true.
exclude-use-default: true
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
max-per-linter: 0
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
max-same-issues: 0

View File

@@ -1,7 +1,10 @@
builds: builds:
- skip: true - skip: true
checksum: checksum:
name_template: 'checksums.txt' name_template: '{{ .ProjectName }}_sha256_checksums.txt'
algorithm: sha256
extra_files:
- glob: ./release/packages/*
release: release:
# Same as for github # Same as for github
# Note: it can only be one: either github, gitlab or gitea # Note: it can only be one: either github, gitlab or gitea

View File

@@ -12,13 +12,13 @@ file:
rm -rf ./assets/frpc/static/* rm -rf ./assets/frpc/static/*
cp -rf ./web/frps/dist/* ./assets/frps/static cp -rf ./web/frps/dist/* ./assets/frps/static
cp -rf ./web/frpc/dist/* ./assets/frpc/static cp -rf ./web/frpc/dist/* ./assets/frpc/static
rm -rf ./assets/frps/statik
rm -rf ./assets/frpc/statik
go generate ./assets/...
fmt: fmt:
go fmt ./... go fmt ./...
vet:
go vet ./...
frps: frps:
env CGO_ENABLED=0 go build -trimpath -ldflags "$(LDFLAGS)" -o bin/frps ./cmd/frps env CGO_ENABLED=0 go build -trimpath -ldflags "$(LDFLAGS)" -o bin/frps ./cmd/frps
@@ -34,13 +34,13 @@ gotest:
go test -v --cover ./server/... go test -v --cover ./server/...
go test -v --cover ./pkg/... go test -v --cover ./pkg/...
ci:
go test -count=1 -p=1 -v ./tests/...
e2e: e2e:
./hack/run-e2e.sh ./hack/run-e2e.sh
alltest: gotest ci e2e e2e-trace:
DEBUG=true LOG_LEVEL=trace ./hack/run-e2e.sh
alltest: vet gotest e2e
clean: clean:
rm -f ./bin/frpc rm -f ./bin/frpc

View File

@@ -2,7 +2,7 @@ export PATH := $(GOPATH)/bin:$(PATH)
export GO111MODULE=on export GO111MODULE=on
LDFLAGS := -s -w LDFLAGS := -s -w
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 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
all: build all: build

View File

@@ -6,6 +6,21 @@
[README](README.md) | [中文文档](README_zh.md) [README](README.md) | [中文文档](README_zh.md)
<h3 align="center">Gold Sponsors</h3>
<!--gold sponsors start-->
<p align="center">
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
<img width="300px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
</a>
</p>
<!--gold sponsors end-->
<h3 align="center">Silver Sponsors</h3>
* Sakura Frp - 欢迎点击 "加入我们"
## What is frp? ## What is frp?
frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the Internet. As of now, it supports **TCP** and **UDP**, as well as **HTTP** and **HTTPS** protocols, where requests can be forwarded to internal services by domain name. frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the Internet. As of now, it supports **TCP** and **UDP**, as well as **HTTP** and **HTTPS** protocols, where requests can be forwarded to internal services by domain name.
@@ -24,12 +39,13 @@ frp also has a P2P connect mode.
* [Forward DNS query request](#forward-dns-query-request) * [Forward DNS query request](#forward-dns-query-request)
* [Forward Unix domain socket](#forward-unix-domain-socket) * [Forward Unix domain socket](#forward-unix-domain-socket)
* [Expose a simple HTTP file server](#expose-a-simple-http-file-server) * [Expose a simple HTTP file server](#expose-a-simple-http-file-server)
* [Enable HTTPS for local HTTP service](#enable-https-for-local-http-service) * [Enable HTTPS for local HTTP(S) service](#enable-https-for-local-https-service)
* [Expose your service privately](#expose-your-service-privately) * [Expose your service privately](#expose-your-service-privately)
* [P2P Mode](#p2p-mode) * [P2P Mode](#p2p-mode)
* [Features](#features) * [Features](#features)
* [Configuration Files](#configuration-files) * [Configuration Files](#configuration-files)
* [Using Environment Variables](#using-environment-variables) * [Using Environment Variables](#using-environment-variables)
* [Split Configures Into Different Files](#split-configures-into-different-files)
* [Dashboard](#dashboard) * [Dashboard](#dashboard)
* [Admin UI](#admin-ui) * [Admin UI](#admin-ui)
* [Monitor](#monitor) * [Monitor](#monitor)
@@ -66,8 +82,7 @@ frp also has a P2P connect mode.
* [Development Plan](#development-plan) * [Development Plan](#development-plan)
* [Contributing](#contributing) * [Contributing](#contributing)
* [Donation](#donation) * [Donation](#donation)
* [AliPay](#alipay) * [GitHub Sponsors](#github-sponsors)
* [Wechat Pay](#wechat-pay)
* [PayPal](#paypal) * [PayPal](#paypal)
<!-- vim-markdown-toc --> <!-- vim-markdown-toc -->
@@ -76,7 +91,9 @@ frp also has a P2P connect mode.
frp is under development. Try the latest release version in the `master` branch, or use the `dev` branch for the version in development. frp is under development. Try the latest release version in the `master` branch, or use the `dev` branch for the version in development.
**The protocol might change at a release and we don't promise backwards compatibility. Please check the release log when upgrading the client and the server.** We are working on v2 version and trying to do some code refactor and improvements. It won't be compatible with v1.
We will switch v0 to v1 at the right time and only accept bug fixes and improvements instead of big feature requirements.
## Architecture ## Architecture
@@ -412,6 +429,27 @@ export FRP_SSH_REMOTE_PORT="6000"
`frpc` will render configuration file template using OS environment variables. Remember to prefix your reference with `.Envs`. `frpc` will render configuration file template using OS environment variables. Remember to prefix your reference with `.Envs`.
### Split Configures Into Different Files
You can split multiple proxy configs into different files and include them in the main file.
```ini
# frpc.ini
[common]
server_addr = x.x.x.x
server_port = 7000
includes=./confd/*.ini
```
```ini
# ./confd/test.ini
[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6000
```
### Dashboard ### Dashboard
Check frp's status and proxies' statistics information by Dashboard. Check frp's status and proxies' statistics information by Dashboard.
@@ -421,12 +459,27 @@ Configure a port for dashboard to enable this feature:
```ini ```ini
[common] [common]
dashboard_port = 7500 dashboard_port = 7500
# dashboard's username and password are both optionalif not set, default is admin. # dashboard's username and password are both optional
dashboard_user = admin dashboard_user = admin
dashboard_pwd = admin dashboard_pwd = admin
``` ```
Then visit `http://[server_addr]:7500` to see the dashboard, with username and password both being `admin` by default. Then visit `http://[server_addr]:7500` to see the dashboard, with username and password both being `admin`.
Additionally, you can use HTTPS port by using your domains wildcard or normal SSL certificate:
```ini
[common]
dashboard_port = 7500
# dashboard's username and password are both optional
dashboard_user = admin
dashboard_pwd = admin
dashboard_tls_mode = true
dashboard_tls_cert_file = server.crt
dashboard_tls_key_file = server.key
```
Then visit `https://[server_addr]:7500` to see the dashboard in secure HTTPS connection, with username and password both being `admin`.
![dashboard](/doc/pic/dashboard.png) ![dashboard](/doc/pic/dashboard.png)
@@ -444,7 +497,7 @@ admin_user = admin
admin_pwd = admin admin_pwd = admin
``` ```
Then visit `http://127.0.0.1:7400` to see admin UI, with username and password both being `admin` by default. Then visit `http://127.0.0.1:7400` to see admin UI, with username and password both being `admin`.
### Monitor ### Monitor
@@ -592,7 +645,7 @@ openssl req -new -sha256 -key server.key \
-config <(cat my-openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:localhost,IP:127.0.0.1,DNS:example.server.com")) \ -config <(cat my-openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:localhost,IP:127.0.0.1,DNS:example.server.com")) \
-out server.csr -out server.csr
openssl x509 -req -days 365 \ openssl x509 -req -days 365 -sha256 \
-in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \ -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-extfile <(printf "subjectAltName=DNS:localhost,IP:127.0.0.1,DNS:example.server.com") \ -extfile <(printf "subjectAltName=DNS:localhost,IP:127.0.0.1,DNS:example.server.com") \
-out server.crt -out server.crt
@@ -607,7 +660,7 @@ openssl req -new -sha256 -key client.key \
-config <(cat my-openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:client.com,DNS:example.client.com")) \ -config <(cat my-openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:client.com,DNS:example.client.com")) \
-out client.csr -out client.csr
openssl x509 -req -days 365 \ openssl x509 -req -days 365 -sha256 \
-in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \ -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-extfile <(printf "subjectAltName=DNS:client.com,DNS:example.client.com") \ -extfile <(printf "subjectAltName=DNS:client.com,DNS:example.client.com") \
-out client.crt -out client.crt
@@ -624,10 +677,12 @@ admin_addr = 127.0.0.1
admin_port = 7400 admin_port = 7400
``` ```
Then run command `frpc reload -c ./frpc.ini` and wait for about 10 seconds to let `frpc` create or update or delete proxies. Then run command `frpc reload -c ./frpc.ini` and wait for about 10 seconds to let `frpc` create or update or remove proxies.
**Note that parameters in [common] section won't be modified except 'start'.** **Note that parameters in [common] section won't be modified except 'start'.**
You can run command `frpc verify -c ./frpc.ini` before reloading to check if there are config errors.
### Get proxy status from client ### Get proxy status from client
Use `frpc status -c ./frpc.ini` to get status of all proxies. The `admin_addr` and `admin_port` fields are required for enabling HTTP API. Use `frpc status -c ./frpc.ini` to get status of all proxies. The `admin_addr` and `admin_port` fields are required for enabling HTTP API.
@@ -817,7 +872,7 @@ custom_domains = test.example.com
host_header_rewrite = dev.example.com host_header_rewrite = dev.example.com
``` ```
The HTTP request will have the the `Host` header rewritten to `Host: dev.example.com` when it reaches the actual web server, although the request from the browser probably has `Host: test.example.com`. The HTTP request will have the `Host` header rewritten to `Host: dev.example.com` when it reaches the actual web server, although the request from the browser probably has `Host: test.example.com`.
### Setting other HTTP Headers ### Setting other HTTP Headers
@@ -843,7 +898,7 @@ In this example, it will set header `X-From-Where: frp` in the HTTP request.
This feature is for http proxy only. This feature is for http proxy only.
You can get user's real IP from HTTP request headers `X-Forwarded-For` and `X-Real-IP`. You can get user's real IP from HTTP request headers `X-Forwarded-For`.
#### Proxy Protocol #### Proxy Protocol
@@ -959,11 +1014,13 @@ server_port = 7000
type = tcpmux type = tcpmux
multiplexer = httpconnect multiplexer = httpconnect
custom_domains = test1 custom_domains = test1
local_port = 80
[proxy2] [proxy2]
type = tcpmux type = tcpmux
multiplexer = httpconnect multiplexer = httpconnect
custom_domains = test2 custom_domains = test2
local_port = 8080
``` ```
In the above configuration - frps can be contacted on port 1337 with a HTTP CONNECT header such as: In the above configuration - frps can be contacted on port 1337 with a HTTP CONNECT header such as:
@@ -1049,15 +1106,11 @@ Interested in getting involved? We would like to help you!
If frp helps you a lot, you can support us by: If frp helps you a lot, you can support us by:
frp QQ group: 606194980 ### GitHub Sponsors
### AliPay Support us by [Github Sponsors](https://github.com/sponsors/fatedier).
![donation-alipay](/doc/pic/donate-alipay.png) You can have your company's logo placed on README file of this project.
### Wechat Pay
![donation-wechatpay](/doc/pic/donate-wechatpay.png)
### PayPal ### PayPal

View File

@@ -7,6 +7,21 @@
frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。 frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。
<h3 align="center">Gold Sponsors</h3>
<!--gold sponsors start-->
<p align="center">
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
<img width="300px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
</a>
</p>
<!--gold sponsors end-->
<h3 align="center">Silver Sponsors</h3>
* Sakura Frp - 欢迎点击 "加入我们"
## 为什么使用 frp ## 为什么使用 frp
通过在具有公网 IP 的节点上部署 frp 服务端,可以轻松地将内网服务穿透到公网,同时提供诸多专业的功能特性,这包括: 通过在具有公网 IP 的节点上部署 frp 服务端,可以轻松地将内网服务穿透到公网,同时提供诸多专业的功能特性,这包括:
@@ -25,6 +40,10 @@ frp 目前已被很多公司广泛用于测试、生产环境。
master 分支用于发布稳定版本dev 分支用于开发,您可以尝试下载最新的 release 版本进行测试。 master 分支用于发布稳定版本dev 分支用于开发,您可以尝试下载最新的 release 版本进行测试。
我们正在进行 v2 大版本的开发,将会尝试在各个方面进行重构和升级,且不会与 v1 版本进行兼容,预计会持续一段时间。
现在的 v0 版本将会在合适的时间切换为 v1 版本并且保证兼容性,后续只做 bug 修复和优化,不再进行大的功能性更新。
## 文档 ## 文档
完整文档已经迁移至 [https://gofrp.org](https://gofrp.org/docs)。 完整文档已经迁移至 [https://gofrp.org](https://gofrp.org/docs)。
@@ -46,6 +65,12 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进
如果您觉得 frp 对你有帮助,欢迎给予我们一定的捐助来维持项目的长期发展。 如果您觉得 frp 对你有帮助,欢迎给予我们一定的捐助来维持项目的长期发展。
### GitHub Sponsors
您可以通过 [GitHub Sponsors](https://github.com/sponsors/fatedier) 赞助我们。
企业赞助者可以将贵公司的 Logo 以及链接放置在项目 README 文件中。
### 知识星球 ### 知识星球
如果您想学习 frp 相关的知识和技术,或者寻求任何帮助及咨询,都可以通过微信扫描下方的二维码付费加入知识星球的官方社群: 如果您想学习 frp 相关的知识和技术,或者寻求任何帮助及咨询,都可以通过微信扫描下方的二维码付费加入知识星球的官方社群:
@@ -59,7 +84,3 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进
### 微信支付捐赠 ### 微信支付捐赠
![donate-wechatpay](/doc/pic/donate-wechatpay.png) ![donate-wechatpay](/doc/pic/donate-wechatpay.png)
### Paypal 捐赠
海外用户推荐通过 [Paypal](https://www.paypal.me/fatedier) 向我的账户 **fatedier@gmail.com** 进行捐赠。

View File

@@ -0,0 +1,3 @@
### Improve
* Adjust http group load balancing to forward requests to each frpc proxy round robin. Previous behavior is always forwarding requests to single proxy in the case of single concurrency.

View File

@@ -14,22 +14,15 @@
package assets package assets
//go:generate statik -src=./frps/static -dest=./frps
//go:generate statik -src=./frpc/static -dest=./frpc
//go:generate go fmt ./frps/statik/statik.go
//go:generate go fmt ./frpc/statik/statik.go
import ( import (
"io/ioutil" "io/fs"
"net/http" "net/http"
"os"
"path"
"github.com/rakyll/statik/fs"
) )
var ( var (
// store static files in memory by statik // read-only filesystem created by "embed" for embedded files
content fs.FS
FileSystem http.FileSystem FileSystem http.FileSystem
// if prefix is not empty, we get file content from disk // if prefix is not empty, we get file content from disk
@@ -38,40 +31,18 @@ var (
// if path is empty, load assets in memory // if path is empty, load assets in memory
// or set FileSystem using disk files // or set FileSystem using disk files
func Load(path string) (err error) { func Load(path string) {
prefixPath = path prefixPath = path
if prefixPath != "" { if prefixPath != "" {
FileSystem = http.Dir(prefixPath) FileSystem = http.Dir(prefixPath)
return nil
} else { } else {
FileSystem, err = fs.New() FileSystem = http.FS(content)
} }
return err
} }
func ReadFile(file string) (content string, err error) { func Register(fileSystem fs.FS) {
if prefixPath == "" { subFs, err := fs.Sub(fileSystem, "static")
file, err := FileSystem.Open(path.Join("/", file)) if err == nil {
if err != nil { content = subFs
return content, err
}
defer file.Close()
buf, err := ioutil.ReadAll(file)
if err != nil {
return content, err
}
content = string(buf)
} else {
file, err := os.Open(path.Join(prefixPath, file))
if err != nil {
return content, err
}
defer file.Close()
buf, err := ioutil.ReadAll(file)
if err != nil {
return content, err
}
content = string(buf)
} }
return content, err
} }

14
assets/frpc/embed.go Normal file
View File

@@ -0,0 +1,14 @@
package frpc
import (
"embed"
"github.com/fatedier/frp/assets"
)
//go:embed static/*
var content embed.FS
func init() {
assets.Register(content)
}

View File

@@ -1 +1 @@
<!doctype html> <html lang=en> <head> <meta charset=utf-8> <title>frp client admin UI</title> <link rel="shortcut icon" href="favicon.ico"></head> <body> <div id=app></div> <script type="text/javascript" src="manifest.js?f30e0e5ff7dbde4611e0"></script><script type="text/javascript" src="vendor.js?a82aed5fb0b844cbdb29"></script></body> </html> <!doctype html> <html lang=en> <head> <meta charset=utf-8> <title>frp client admin UI</title> <link rel="shortcut icon" href="favicon.ico"></head> <body> <div id=app></div> <script type="text/javascript" src="manifest.js?5d5774096cf5c1b4d5af"></script><script type="text/javascript" src="vendor.js?dc42700731a508d39009"></script></body> </html>

View File

@@ -1 +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<t.length;l++)a=t[l],o[a]&&s.push(o[a][0]),o[a]=0;for(i in c)Object.prototype.hasOwnProperty.call(c,i)&&(e[i]=c[i]);for(r&&r(t,c,u);s.length;)s.shift()();if(u)for(l=0;l<u.length;l++)f=n(n.s=u[l]);return f};var t={},o={1:0};n.e=function(e){function r(){i.onerror=i.onload=null,clearTimeout(a);var n=o[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),o[e]=void 0)}var t=o[e];if(0===t)return new Promise(function(e){e()});if(t)return t[2];var c=new Promise(function(n,r){t=o[e]=[n,r]});t[2]=c;var u=document.getElementsByTagName("head")[0],i=document.createElement("script");i.type="text/javascript",i.charset="utf-8",i.async=!0,i.timeout=12e4,n.nc&&i.setAttribute("nonce",n.nc),i.src=n.p+""+e+".js?"+{0:"a82aed5fb0b844cbdb29"}[e];var a=setTimeout(r,12e4);return i.onerror=i.onload=r,u.appendChild(i),c},n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n.oe=function(e){throw console.error(e),e}}([]); !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<t.length;l++)a=t[l],o[a]&&s.push(o[a][0]),o[a]=0;for(i in c)Object.prototype.hasOwnProperty.call(c,i)&&(e[i]=c[i]);for(r&&r(t,c,u);s.length;)s.shift()();if(u)for(l=0;l<u.length;l++)f=n(n.s=u[l]);return f};var t={},o={1:0};n.e=function(e){function r(){i.onerror=i.onload=null,clearTimeout(a);var n=o[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),o[e]=void 0)}var t=o[e];if(0===t)return new Promise(function(e){e()});if(t)return t[2];var c=new Promise(function(n,r){t=o[e]=[n,r]});t[2]=c;var u=document.getElementsByTagName("head")[0],i=document.createElement("script");i.type="text/javascript",i.charset="utf-8",i.async=!0,i.timeout=12e4,n.nc&&i.setAttribute("nonce",n.nc),i.src=n.p+""+e+".js?"+{0:"dc42700731a508d39009"}[e];var a=setTimeout(r,12e4);return i.onerror=i.onload=r,u.appendChild(i),c},n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n.oe=function(e){throw console.error(e),e}}([]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

14
assets/frps/embed.go Normal file
View File

@@ -0,0 +1,14 @@
package frpc
import (
"embed"
"github.com/fatedier/frp/assets"
)
//go:embed static/*
var content embed.FS
func init() {
assets.Register(content)
}

View File

@@ -1 +1 @@
<!doctype html> <html lang=en> <head> <meta charset=utf-8> <title>frps dashboard</title> <link rel="shortcut icon" href="favicon.ico"></head> <body> <div id=app></div> <script type="text/javascript" src="manifest.js?782d7b1b910e824ac986"></script><script type="text/javascript" src="vendor.js?7f899297af075fb3b085"></script></body> </html> <!doctype html> <html lang=en> <head> <meta charset=utf-8> <title>frps dashboard</title> <link rel="shortcut icon" href="favicon.ico"></head> <body> <div id=app></div> <script type="text/javascript" src="manifest.js?5d154ba4c6b342d8c0c3"></script><script type="text/javascript" src="vendor.js?ddbd1f69fb6e67be4b78"></script></body> </html>

View File

@@ -1 +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<t.length;l++)a=t[l],o[a]&&s.push(o[a][0]),o[a]=0;for(i in u)Object.prototype.hasOwnProperty.call(u,i)&&(e[i]=u[i]);for(r&&r(t,u,c);s.length;)s.shift()();if(c)for(l=0;l<c.length;l++)f=n(n.s=c[l]);return f};var t={},o={1:0};n.e=function(e){function r(){i.onerror=i.onload=null,clearTimeout(a);var n=o[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),o[e]=void 0)}var t=o[e];if(0===t)return new Promise(function(e){e()});if(t)return t[2];var u=new Promise(function(n,r){t=o[e]=[n,r]});t[2]=u;var c=document.getElementsByTagName("head")[0],i=document.createElement("script");i.type="text/javascript",i.charset="utf-8",i.async=!0,i.timeout=12e4,n.nc&&i.setAttribute("nonce",n.nc),i.src=n.p+""+e+".js?"+{0:"7f899297af075fb3b085"}[e];var a=setTimeout(r,12e4);return i.onerror=i.onload=r,c.appendChild(i),u},n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n.oe=function(e){throw console.error(e),e}}([]); !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<t.length;l++)a=t[l],o[a]&&s.push(o[a][0]),o[a]=0;for(i in u)Object.prototype.hasOwnProperty.call(u,i)&&(e[i]=u[i]);for(r&&r(t,u,c);s.length;)s.shift()();if(c)for(l=0;l<c.length;l++)f=n(n.s=c[l]);return f};var t={},o={1:0};n.e=function(e){function r(){i.onerror=i.onload=null,clearTimeout(a);var n=o[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),o[e]=void 0)}var t=o[e];if(0===t)return new Promise(function(e){e()});if(t)return t[2];var u=new Promise(function(n,r){t=o[e]=[n,r]});t[2]=u;var c=document.getElementsByTagName("head")[0],i=document.createElement("script");i.type="text/javascript",i.charset="utf-8",i.async=!0,i.timeout=12e4,n.nc&&i.setAttribute("nonce",n.nc),i.src=n.p+""+e+".js?"+{0:"ddbd1f69fb6e67be4b78"}[e];var a=setTimeout(r,12e4);return i.onerror=i.onload=r,c.appendChild(i),u},n.m=e,n.c=t,n.i=function(e){return e},n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:t})},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},n.p="",n.oe=function(e){throw console.error(e),e}}([]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -17,36 +17,49 @@ package client
import ( import (
"net" "net"
"net/http" "net/http"
"net/http/pprof"
"time" "time"
"github.com/gorilla/mux"
"github.com/fatedier/frp/assets" "github.com/fatedier/frp/assets"
frpNet "github.com/fatedier/frp/pkg/util/net" frpNet "github.com/fatedier/frp/pkg/util/net"
"github.com/gorilla/mux"
) )
var ( var (
httpServerReadTimeout = 10 * time.Second httpServerReadTimeout = 60 * time.Second
httpServerWriteTimeout = 10 * time.Second httpServerWriteTimeout = 60 * time.Second
) )
func (svr *Service) RunAdminServer(address string) (err error) { func (svr *Service) RunAdminServer(address string) (err error) {
// url router // url router
router := mux.NewRouter() router := mux.NewRouter()
user, passwd := svr.cfg.AdminUser, svr.cfg.AdminPwd router.HandleFunc("/healthz", svr.healthz)
router.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware)
// api, see dashboard_api.go // debug
router.HandleFunc("/api/reload", svr.apiReload).Methods("GET") if svr.cfg.PprofEnable {
router.HandleFunc("/api/status", svr.apiStatus).Methods("GET") router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
router.HandleFunc("/api/config", svr.apiGetConfig).Methods("GET") router.HandleFunc("/debug/pprof/profile", pprof.Profile)
router.HandleFunc("/api/config", svr.apiPutConfig).Methods("PUT") router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
router.HandleFunc("/debug/pprof/trace", pprof.Trace)
router.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
}
subRouter := router.NewRoute().Subrouter()
user, passwd := svr.cfg.AdminUser, svr.cfg.AdminPwd
subRouter.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware)
// api, see admin_api.go
subRouter.HandleFunc("/api/reload", svr.apiReload).Methods("GET")
subRouter.HandleFunc("/api/status", svr.apiStatus).Methods("GET")
subRouter.HandleFunc("/api/config", svr.apiGetConfig).Methods("GET")
subRouter.HandleFunc("/api/config", svr.apiPutConfig).Methods("PUT")
// view // view
router.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET") subRouter.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET")
router.PathPrefix("/static/").Handler(frpNet.MakeHTTPGzipHandler(http.StripPrefix("/static/", http.FileServer(assets.FileSystem)))).Methods("GET") subRouter.PathPrefix("/static/").Handler(frpNet.MakeHTTPGzipHandler(http.StripPrefix("/static/", http.FileServer(assets.FileSystem)))).Methods("GET")
router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { subRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/static/", http.StatusMovedPermanently) http.Redirect(w, r, "/static/", http.StatusMovedPermanently)
}) })
@@ -64,6 +77,8 @@ func (svr *Service) RunAdminServer(address string) (err error) {
return err return err
} }
go server.Serve(ln) go func() {
_ = server.Serve(ln)
}()
return return
} }

View File

@@ -17,9 +17,12 @@ package client
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net"
"net/http" "net/http"
"os"
"sort" "sort"
"strconv"
"strings" "strings"
"github.com/fatedier/frp/client/proxy" "github.com/fatedier/frp/client/proxy"
@@ -32,37 +35,25 @@ type GeneralResponse struct {
Msg string Msg string
} }
// GET api/reload // /healthz
func (svr *Service) healthz(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
}
// GET api/reload
func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) { func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
res := GeneralResponse{Code: 200} res := GeneralResponse{Code: 200}
log.Info("Http request [/api/reload]") log.Info("api request [/api/reload]")
defer func() { defer func() {
log.Info("Http response [/api/reload], code [%d]", res.Code) log.Info("api response [/api/reload], code [%d]", res.Code)
w.WriteHeader(res.Code) w.WriteHeader(res.Code)
if len(res.Msg) > 0 { if len(res.Msg) > 0 {
w.Write([]byte(res.Msg)) _, _ = w.Write([]byte(res.Msg))
} }
}() }()
content, err := config.GetRenderedConfFromFile(svr.cfgFile) _, pxyCfgs, visitorCfgs, err := config.ParseClientConfig(svr.cfgFile)
if err != nil {
res.Code = 400
res.Msg = err.Error()
log.Warn("reload frpc config file error: %s", res.Msg)
return
}
newCommonCfg, err := config.UnmarshalClientConfFromIni(content)
if err != nil {
res.Code = 400
res.Msg = err.Error()
log.Warn("reload frpc common section error: %s", res.Msg)
return
}
pxyCfgs, visitorCfgs, err := config.LoadAllProxyConfsFromIni(svr.cfg.User, content, newCommonCfg.Start)
if err != nil { if err != nil {
res.Code = 400 res.Code = 400
res.Msg = err.Error() res.Msg = err.Error()
@@ -70,15 +61,13 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
return return
} }
err = svr.ReloadConf(pxyCfgs, visitorCfgs) if err = svr.ReloadConf(pxyCfgs, visitorCfgs); err != nil {
if err != nil {
res.Code = 500 res.Code = 500
res.Msg = err.Error() res.Msg = err.Error()
log.Warn("reload frpc proxy config error: %s", res.Msg) log.Warn("reload frpc proxy config error: %s", res.Msg)
return return
} }
log.Info("success reload conf") log.Info("success reload conf")
return
} }
type StatusResp struct { type StatusResp struct {
@@ -117,48 +106,48 @@ func NewProxyStatusResp(status *proxy.WorkingStatus, serverAddr string) ProxySta
switch cfg := status.Cfg.(type) { switch cfg := status.Cfg.(type) {
case *config.TCPProxyConf: case *config.TCPProxyConf:
if cfg.LocalPort != 0 { if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort) psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
} }
psr.Plugin = cfg.Plugin psr.Plugin = cfg.Plugin
if status.Err != "" { if status.Err != "" {
psr.RemoteAddr = fmt.Sprintf("%s:%d", serverAddr, cfg.RemotePort) psr.RemoteAddr = net.JoinHostPort(serverAddr, strconv.Itoa(cfg.RemotePort))
} else { } else {
psr.RemoteAddr = serverAddr + status.RemoteAddr psr.RemoteAddr = serverAddr + status.RemoteAddr
} }
case *config.UDPProxyConf: case *config.UDPProxyConf:
if cfg.LocalPort != 0 { if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort) psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
} }
if status.Err != "" { if status.Err != "" {
psr.RemoteAddr = fmt.Sprintf("%s:%d", serverAddr, cfg.RemotePort) psr.RemoteAddr = net.JoinHostPort(serverAddr, strconv.Itoa(cfg.RemotePort))
} else { } else {
psr.RemoteAddr = serverAddr + status.RemoteAddr psr.RemoteAddr = serverAddr + status.RemoteAddr
} }
case *config.HTTPProxyConf: case *config.HTTPProxyConf:
if cfg.LocalPort != 0 { if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort) psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
} }
psr.Plugin = cfg.Plugin psr.Plugin = cfg.Plugin
psr.RemoteAddr = status.RemoteAddr psr.RemoteAddr = status.RemoteAddr
case *config.HTTPSProxyConf: case *config.HTTPSProxyConf:
if cfg.LocalPort != 0 { if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort) psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
} }
psr.Plugin = cfg.Plugin psr.Plugin = cfg.Plugin
psr.RemoteAddr = status.RemoteAddr psr.RemoteAddr = status.RemoteAddr
case *config.STCPProxyConf: case *config.STCPProxyConf:
if cfg.LocalPort != 0 { if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort) psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
} }
psr.Plugin = cfg.Plugin psr.Plugin = cfg.Plugin
case *config.XTCPProxyConf: case *config.XTCPProxyConf:
if cfg.LocalPort != 0 { if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort) psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
} }
psr.Plugin = cfg.Plugin psr.Plugin = cfg.Plugin
case *config.SUDPProxyConf: case *config.SUDPProxyConf:
if cfg.LocalPort != 0 { if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort) psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
} }
psr.Plugin = cfg.Plugin psr.Plugin = cfg.Plugin
} }
@@ -183,7 +172,7 @@ func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) {
defer func() { defer func() {
log.Info("Http response [/api/status]") log.Info("Http response [/api/status]")
buf, _ = json.Marshal(&res) buf, _ = json.Marshal(&res)
w.Write(buf) _, _ = w.Write(buf)
}() }()
ps := svr.ctl.pm.GetAllProxyStatus() ps := svr.ctl.pm.GetAllProxyStatus()
@@ -212,7 +201,6 @@ func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) {
sort.Sort(ByProxyStatusResp(res.STCP)) sort.Sort(ByProxyStatusResp(res.STCP))
sort.Sort(ByProxyStatusResp(res.XTCP)) sort.Sort(ByProxyStatusResp(res.XTCP))
sort.Sort(ByProxyStatusResp(res.SUDP)) sort.Sort(ByProxyStatusResp(res.SUDP))
return
} }
// GET api/config // GET api/config
@@ -224,7 +212,7 @@ func (svr *Service) apiGetConfig(w http.ResponseWriter, r *http.Request) {
log.Info("Http get response [/api/config], code [%d]", res.Code) log.Info("Http get response [/api/config], code [%d]", res.Code)
w.WriteHeader(res.Code) w.WriteHeader(res.Code)
if len(res.Msg) > 0 { if len(res.Msg) > 0 {
w.Write([]byte(res.Msg)) _, _ = w.Write([]byte(res.Msg))
} }
}() }()
@@ -264,12 +252,12 @@ func (svr *Service) apiPutConfig(w http.ResponseWriter, r *http.Request) {
log.Info("Http put response [/api/config], code [%d]", res.Code) log.Info("Http put response [/api/config], code [%d]", res.Code)
w.WriteHeader(res.Code) w.WriteHeader(res.Code)
if len(res.Msg) > 0 { if len(res.Msg) > 0 {
w.Write([]byte(res.Msg)) _, _ = w.Write([]byte(res.Msg))
} }
}() }()
// get new config content // get new config content
body, err := ioutil.ReadAll(r.Body) body, err := io.ReadAll(r.Body)
if err != nil { if err != nil {
res.Code = 400 res.Code = 400
res.Msg = fmt.Sprintf("read request body error: %v", err) res.Msg = fmt.Sprintf("read request body error: %v", err)
@@ -286,7 +274,7 @@ func (svr *Service) apiPutConfig(w http.ResponseWriter, r *http.Request) {
// get token from origin content // get token from origin content
token := "" token := ""
b, err := ioutil.ReadFile(svr.cfgFile) b, err := os.ReadFile(svr.cfgFile)
if err != nil { if err != nil {
res.Code = 400 res.Code = 400
res.Msg = err.Error() res.Msg = err.Error()
@@ -325,7 +313,7 @@ func (svr *Service) apiPutConfig(w http.ResponseWriter, r *http.Request) {
} }
content = strings.Join(newRows, "\n") content = strings.Join(newRows, "\n")
err = ioutil.WriteFile(svr.cfgFile, []byte(content), 0644) err = os.WriteFile(svr.cfgFile, []byte(content), 0o644)
if err != nil { if err != nil {
res.Code = 500 res.Code = 500
res.Msg = fmt.Sprintf("write content to frpc config file error: %v", err) res.Msg = fmt.Sprintf("write content to frpc config file error: %v", err)

View File

@@ -21,9 +21,13 @@ import (
"net" "net"
"runtime/debug" "runtime/debug"
"strconv" "strconv"
"sync"
"time" "time"
"github.com/fatedier/golib/control/shutdown"
"github.com/fatedier/golib/crypto"
libdial "github.com/fatedier/golib/net/dial"
fmux "github.com/hashicorp/yamux"
"github.com/fatedier/frp/client/proxy" "github.com/fatedier/frp/client/proxy"
"github.com/fatedier/frp/pkg/auth" "github.com/fatedier/frp/pkg/auth"
"github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/config"
@@ -31,10 +35,6 @@ import (
"github.com/fatedier/frp/pkg/transport" "github.com/fatedier/frp/pkg/transport"
frpNet "github.com/fatedier/frp/pkg/util/net" frpNet "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/xlog" "github.com/fatedier/frp/pkg/util/xlog"
"github.com/fatedier/golib/control/shutdown"
"github.com/fatedier/golib/crypto"
fmux "github.com/hashicorp/yamux"
) )
type Control struct { type Control struct {
@@ -78,8 +78,6 @@ type Control struct {
// The UDP port that the server is listening on // The UDP port that the server is listening on
serverUDPPort int serverUDPPort int
mu sync.RWMutex
xl *xlog.Logger xl *xlog.Logger
// service context // service context
@@ -94,8 +92,8 @@ func NewControl(ctx context.Context, runID string, conn net.Conn, session *fmux.
pxyCfgs map[string]config.ProxyConf, pxyCfgs map[string]config.ProxyConf,
visitorCfgs map[string]config.VisitorConf, visitorCfgs map[string]config.VisitorConf,
serverUDPPort int, serverUDPPort int,
authSetter auth.Setter) *Control { authSetter auth.Setter,
) *Control {
// new xlog instance // new xlog instance
ctl := &Control{ ctl := &Control{
runID: runID, runID: runID,
@@ -130,7 +128,6 @@ func (ctl *Control) Run() {
// start all visitors // start all visitors
go ctl.vm.Run() go ctl.vm.Run()
return
} }
func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) { func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) {
@@ -182,9 +179,16 @@ func (ctl *Control) HandleNewProxyResp(inMsg *msg.NewProxyResp) {
} }
func (ctl *Control) Close() error { func (ctl *Control) Close() error {
return ctl.GracefulClose(0)
}
func (ctl *Control) GracefulClose(d time.Duration) error {
ctl.pm.Close() ctl.pm.Close()
ctl.conn.Close()
ctl.vm.Close() ctl.vm.Close()
time.Sleep(d)
ctl.conn.Close()
if ctl.session != nil { if ctl.session != nil {
ctl.session.Close() ctl.session.Close()
} }
@@ -227,12 +231,38 @@ func (ctl *Control) connectServer() (conn net.Conn, err error) {
} }
} }
address := net.JoinHostPort(ctl.clientCfg.ServerAddr, strconv.Itoa(ctl.clientCfg.ServerPort)) proxyType, addr, auth, err := libdial.ParseProxyURL(ctl.clientCfg.HTTPProxy)
conn, err = frpNet.ConnectServerByProxyWithTLS(ctl.clientCfg.HTTPProxy, ctl.clientCfg.Protocol, address, tlsConfig) if err != nil {
xl.Error("fail to parse proxy url")
return nil, err
}
dialOptions := []libdial.DialOption{}
protocol := ctl.clientCfg.Protocol
if protocol == "websocket" {
protocol = "tcp"
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: frpNet.DialHookWebsocket()}))
}
if ctl.clientCfg.ConnectServerLocalIP != "" {
dialOptions = append(dialOptions, libdial.WithLocalAddr(ctl.clientCfg.ConnectServerLocalIP))
}
dialOptions = append(dialOptions,
libdial.WithProtocol(protocol),
libdial.WithTimeout(time.Duration(ctl.clientCfg.DialServerTimeout)*time.Second),
libdial.WithKeepAlive(time.Duration(ctl.clientCfg.DialServerKeepAlive)*time.Second),
libdial.WithProxy(proxyType, addr),
libdial.WithProxyAuth(auth),
libdial.WithTLSConfig(tlsConfig),
libdial.WithAfterHook(libdial.AfterHook{
Hook: frpNet.DialHookCustomTLSHeadByte(tlsConfig != nil, ctl.clientCfg.DisableCustomTLSFirstByte),
}),
)
conn, err = libdial.Dial(
net.JoinHostPort(ctl.clientCfg.ServerAddr, strconv.Itoa(ctl.clientCfg.ServerPort)),
dialOptions...,
)
if err != nil { if err != nil {
xl.Warn("start new connection to server error: %v", err) xl.Warn("start new connection to server error: %v", err)
return return nil, err
} }
} }
return return
@@ -301,16 +331,27 @@ func (ctl *Control) msgHandler() {
}() }()
defer ctl.msgHandlerShutdown.Done() defer ctl.msgHandlerShutdown.Done()
hbSend := time.NewTicker(time.Duration(ctl.clientCfg.HeartbeatInterval) * time.Second) var hbSendCh <-chan time.Time
defer hbSend.Stop() // TODO(fatedier): disable heartbeat if TCPMux is enabled.
hbCheck := time.NewTicker(time.Second) // Just keep it here to keep compatible with old version frps.
defer hbCheck.Stop() if ctl.clientCfg.HeartbeatInterval > 0 {
hbSend := time.NewTicker(time.Duration(ctl.clientCfg.HeartbeatInterval) * time.Second)
defer hbSend.Stop()
hbSendCh = hbSend.C
}
var hbCheckCh <-chan time.Time
// Check heartbeat timeout only if TCPMux is not enabled and users don't disable heartbeat feature.
if ctl.clientCfg.HeartbeatInterval > 0 && ctl.clientCfg.HeartbeatTimeout > 0 && !ctl.clientCfg.TCPMux {
hbCheck := time.NewTicker(time.Second)
defer hbCheck.Stop()
hbCheckCh = hbCheck.C
}
ctl.lastPong = time.Now() ctl.lastPong = time.Now()
for { for {
select { select {
case <-hbSend.C: case <-hbSendCh:
// send heartbeat to server // send heartbeat to server
xl.Debug("send heartbeat to server") xl.Debug("send heartbeat to server")
pingMsg := &msg.Ping{} pingMsg := &msg.Ping{}
@@ -319,7 +360,7 @@ func (ctl *Control) msgHandler() {
return return
} }
ctl.sendCh <- pingMsg ctl.sendCh <- pingMsg
case <-hbCheck.C: case <-hbCheckCh:
if time.Since(ctl.lastPong) > time.Duration(ctl.clientCfg.HeartbeatTimeout)*time.Second { if time.Since(ctl.lastPong) > time.Duration(ctl.clientCfg.HeartbeatTimeout)*time.Second {
xl.Warn("heartbeat timeout") xl.Warn("heartbeat timeout")
// let reader() stop // let reader() stop
@@ -355,24 +396,21 @@ func (ctl *Control) worker() {
go ctl.reader() go ctl.reader()
go ctl.writer() go ctl.writer()
select { <-ctl.closedCh
case <-ctl.closedCh: // close related channels and wait until other goroutines done
// close related channels and wait until other goroutines done close(ctl.readCh)
close(ctl.readCh) ctl.readerShutdown.WaitDone()
ctl.readerShutdown.WaitDone() ctl.msgHandlerShutdown.WaitDone()
ctl.msgHandlerShutdown.WaitDone()
close(ctl.sendCh) close(ctl.sendCh)
ctl.writerShutdown.WaitDone() ctl.writerShutdown.WaitDone()
ctl.pm.Close() ctl.pm.Close()
ctl.vm.Close() ctl.vm.Close()
close(ctl.closedDoneCh) close(ctl.closedDoneCh)
if ctl.session != nil { if ctl.session != nil {
ctl.session.Close() ctl.session.Close()
}
return
} }
} }

View File

@@ -6,18 +6,9 @@ import (
"github.com/fatedier/frp/pkg/msg" "github.com/fatedier/frp/pkg/msg"
) )
type Type int var ErrPayloadType = errors.New("error payload type")
const ( type Handler func(payload interface{}) error
EvStartProxy Type = iota
EvCloseProxy
)
var (
ErrPayloadType = errors.New("error payload type")
)
type Handler func(evType Type, payload interface{}) error
type StartProxyPayload struct { type StartProxyPayload struct {
NewProxyMsg *msg.NewProxy NewProxyMsg *msg.NewProxy

View File

@@ -19,7 +19,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"time" "time"
@@ -27,9 +26,7 @@ import (
"github.com/fatedier/frp/pkg/util/xlog" "github.com/fatedier/frp/pkg/util/xlog"
) )
var ( var ErrHealthCheckType = errors.New("error health check type")
ErrHealthCheckType = errors.New("error health check type")
)
type Monitor struct { type Monitor struct {
checkType string checkType string
@@ -55,8 +52,8 @@ type Monitor struct {
func NewMonitor(ctx context.Context, checkType string, func NewMonitor(ctx context.Context, checkType string,
intervalS int, timeoutS int, maxFailedTimes int, intervalS int, timeoutS int, maxFailedTimes int,
addr string, url string, addr string, url string,
statusNormalFn func(), statusFailedFn func()) *Monitor { statusNormalFn func(), statusFailedFn func(),
) *Monitor {
if intervalS <= 0 { if intervalS <= 0 {
intervalS = 10 intervalS = 10
} }
@@ -153,7 +150,7 @@ func (monitor *Monitor) doTCPCheck(ctx context.Context) error {
} }
func (monitor *Monitor) doHTTPCheck(ctx context.Context) error { func (monitor *Monitor) doHTTPCheck(ctx context.Context) error {
req, err := http.NewRequest("GET", monitor.url, nil) req, err := http.NewRequestWithContext(ctx, "GET", monitor.url, nil)
if err != nil { if err != nil {
return err return err
} }
@@ -162,7 +159,7 @@ func (monitor *Monitor) doHTTPCheck(ctx context.Context) error {
return err return err
} }
defer resp.Body.Close() defer resp.Body.Close()
io.Copy(ioutil.Discard, resp.Body) _, _ = io.Copy(io.Discard, resp.Body)
if resp.StatusCode/100 != 2 { if resp.StatusCode/100 != 2 {
return fmt.Errorf("do http health check, StatusCode is [%d] not 2xx", resp.StatusCode) return fmt.Errorf("do http health check, StatusCode is [%d] not 2xx", resp.StatusCode)

View File

@@ -17,15 +17,21 @@ package proxy
import ( import (
"bytes" "bytes"
"context" "context"
"fmt"
"io" "io"
"io/ioutil"
"net" "net"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/fatedier/golib/errors"
frpIo "github.com/fatedier/golib/io"
libdial "github.com/fatedier/golib/net/dial"
"github.com/fatedier/golib/pool"
fmux "github.com/hashicorp/yamux"
pp "github.com/pires/go-proxyproto"
"golang.org/x/time/rate"
"github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/msg" "github.com/fatedier/frp/pkg/msg"
plugin "github.com/fatedier/frp/pkg/plugin/client" plugin "github.com/fatedier/frp/pkg/plugin/client"
@@ -33,13 +39,6 @@ import (
"github.com/fatedier/frp/pkg/util/limit" "github.com/fatedier/frp/pkg/util/limit"
frpNet "github.com/fatedier/frp/pkg/util/net" frpNet "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/xlog" "github.com/fatedier/frp/pkg/util/xlog"
"github.com/fatedier/golib/errors"
frpIo "github.com/fatedier/golib/io"
"github.com/fatedier/golib/pool"
fmux "github.com/hashicorp/yamux"
pp "github.com/pires/go-proxyproto"
"golang.org/x/time/rate"
) )
// Proxy defines how to handle work connections for different proxy type. // Proxy defines how to handle work connections for different proxy type.
@@ -307,7 +306,7 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
Sid: natHoleSidMsg.Sid, Sid: natHoleSidMsg.Sid,
} }
raddr, _ := net.ResolveUDPAddr("udp", raddr, _ := net.ResolveUDPAddr("udp",
fmt.Sprintf("%s:%d", pxy.clientCfg.ServerAddr, pxy.serverUDPPort)) net.JoinHostPort(pxy.clientCfg.ServerAddr, strconv.Itoa(pxy.serverUDPPort)))
clientConn, err := net.DialUDP("udp", nil, raddr) clientConn, err := net.DialUDP("udp", nil, raddr)
if err != nil { if err != nil {
xl.Error("dial server udp addr error: %v", err) xl.Error("dial server udp addr error: %v", err)
@@ -323,7 +322,7 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
// Wait for client address at most 5 seconds. // Wait for client address at most 5 seconds.
var natHoleRespMsg msg.NatHoleResp var natHoleRespMsg msg.NatHoleResp
clientConn.SetReadDeadline(time.Now().Add(5 * time.Second)) _ = clientConn.SetReadDeadline(time.Now().Add(5 * time.Second))
buf := pool.GetBuf(1024) buf := pool.GetBuf(1024)
n, err := clientConn.Read(buf) n, err := clientConn.Read(buf)
@@ -336,8 +335,8 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
xl.Error("get natHoleRespMsg error: %v", err) xl.Error("get natHoleRespMsg error: %v", err)
return return
} }
clientConn.SetReadDeadline(time.Time{}) _ = clientConn.SetReadDeadline(time.Time{})
clientConn.Close() _ = clientConn.Close()
if natHoleRespMsg.Error != "" { if natHoleRespMsg.Error != "" {
xl.Error("natHoleRespMsg get error info: %s", natHoleRespMsg.Error) xl.Error("natHoleRespMsg get error info: %s", natHoleRespMsg.Error)
@@ -347,35 +346,34 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
xl.Trace("get natHoleRespMsg, sid [%s], client address [%s] visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr) xl.Trace("get natHoleRespMsg, sid [%s], client address [%s] visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
// Send detect message // Send detect message
array := strings.Split(natHoleRespMsg.VisitorAddr, ":") host, portStr, err := net.SplitHostPort(natHoleRespMsg.VisitorAddr)
if len(array) <= 1 { if err != nil {
xl.Error("get NatHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr) xl.Error("get NatHoleResp visitor address [%s] error: %v", natHoleRespMsg.VisitorAddr, err)
} }
laddr, _ := net.ResolveUDPAddr("udp", clientConn.LocalAddr().String()) laddr, _ := net.ResolveUDPAddr("udp", clientConn.LocalAddr().String())
/*
for i := 1000; i < 65000; i++ { port, err := strconv.ParseInt(portStr, 10, 64)
pxy.sendDetectMsg(array[0], int64(i), laddr, "a")
}
*/
port, err := strconv.ParseInt(array[1], 10, 64)
if err != nil { if err != nil {
xl.Error("get natHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr) xl.Error("get natHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
return return
} }
pxy.sendDetectMsg(array[0], int(port), laddr, []byte(natHoleRespMsg.Sid)) _ = pxy.sendDetectMsg(host, int(port), laddr, []byte(natHoleRespMsg.Sid))
xl.Trace("send all detect msg done") xl.Trace("send all detect msg done")
msg.WriteMsg(conn, &msg.NatHoleClientDetectOK{}) if err := msg.WriteMsg(conn, &msg.NatHoleClientDetectOK{}); err != nil {
xl.Error("write message error: %v", err)
return
}
// Listen for clientConn's address and wait for visitor connection // Listen for clientConn's address and wait for visitor connection
lConn, err := net.ListenUDP("udp", laddr) lConn, err := net.ListenUDP("udp", laddr)
if err != nil { if err != nil {
xl.Error("listen on visitorConn's local adress error: %v", err) xl.Error("listen on visitorConn's local address error: %v", err)
return return
} }
defer lConn.Close() defer lConn.Close()
lConn.SetReadDeadline(time.Now().Add(8 * time.Second)) _ = lConn.SetReadDeadline(time.Now().Add(8 * time.Second))
sidBuf := pool.GetBuf(1024) sidBuf := pool.GetBuf(1024)
var uAddr *net.UDPAddr var uAddr *net.UDPAddr
n, uAddr, err = lConn.ReadFromUDP(sidBuf) n, uAddr, err = lConn.ReadFromUDP(sidBuf)
@@ -383,7 +381,7 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
xl.Warn("get sid from visitor error: %v", err) xl.Warn("get sid from visitor error: %v", err)
return return
} }
lConn.SetReadDeadline(time.Time{}) _ = lConn.SetReadDeadline(time.Time{})
if string(sidBuf[:n]) != natHoleRespMsg.Sid { if string(sidBuf[:n]) != natHoleRespMsg.Sid {
xl.Warn("incorrect sid from visitor") xl.Warn("incorrect sid from visitor")
return return
@@ -391,7 +389,10 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
pool.PutBuf(sidBuf) pool.PutBuf(sidBuf)
xl.Info("nat hole connection make success, sid [%s]", natHoleRespMsg.Sid) xl.Info("nat hole connection make success, sid [%s]", natHoleRespMsg.Sid)
lConn.WriteToUDP(sidBuf[:n], uAddr) if _, err := lConn.WriteToUDP(sidBuf[:n], uAddr); err != nil {
xl.Error("write uaddr error: %v", err)
return
}
kcpConn, err := frpNet.NewKCPConnFromUDP(lConn, false, uAddr.String()) kcpConn, err := frpNet.NewKCPConnFromUDP(lConn, false, uAddr.String())
if err != nil { if err != nil {
@@ -401,7 +402,7 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
fmuxCfg := fmux.DefaultConfig() fmuxCfg := fmux.DefaultConfig()
fmuxCfg.KeepAliveInterval = 5 * time.Second fmuxCfg.KeepAliveInterval = 5 * time.Second
fmuxCfg.LogOutput = ioutil.Discard fmuxCfg.LogOutput = io.Discard
sess, err := fmux.Server(kcpConn, fmuxCfg) sess, err := fmux.Server(kcpConn, fmuxCfg)
if err != nil { if err != nil {
xl.Error("create yamux server from kcp connection error: %v", err) xl.Error("create yamux server from kcp connection error: %v", err)
@@ -419,7 +420,7 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
} }
func (pxy *XTCPProxy) sendDetectMsg(addr string, port int, laddr *net.UDPAddr, content []byte) (err error) { func (pxy *XTCPProxy) sendDetectMsg(addr string, port int, laddr *net.UDPAddr, content []byte) (err error) {
daddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", addr, port)) daddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(addr, strconv.Itoa(port)))
if err != nil { if err != nil {
return err return err
} }
@@ -429,12 +430,13 @@ func (pxy *XTCPProxy) sendDetectMsg(addr string, port int, laddr *net.UDPAddr, c
return err return err
} }
//uConn := ipv4.NewConn(tConn) // uConn := ipv4.NewConn(tConn)
//uConn.SetTTL(3) // uConn.SetTTL(3)
tConn.Write(content) if _, err := tConn.Write(content); err != nil {
tConn.Close() return err
return nil }
return tConn.Close()
} }
// UDP // UDP
@@ -452,7 +454,7 @@ type UDPProxy struct {
} }
func (pxy *UDPProxy) Run() (err error) { func (pxy *UDPProxy) Run() (err error) {
pxy.localAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pxy.cfg.LocalIP, pxy.cfg.LocalPort)) pxy.localAddr, err = net.ResolveUDPAddr("udp", net.JoinHostPort(pxy.cfg.LocalIP, strconv.Itoa(pxy.cfg.LocalPort)))
if err != nil { if err != nil {
return return
} }
@@ -544,7 +546,7 @@ func (pxy *UDPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
} }
} }
} }
heartbeatFn := func(conn net.Conn, sendCh chan msg.Message) { heartbeatFn := func(sendCh chan msg.Message) {
var errRet error var errRet error
for { for {
time.Sleep(time.Duration(30) * time.Second) time.Sleep(time.Duration(30) * time.Second)
@@ -559,7 +561,7 @@ func (pxy *UDPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
go workConnSenderFn(pxy.workConn, pxy.sendCh) go workConnSenderFn(pxy.workConn, pxy.sendCh)
go workConnReaderFn(pxy.workConn, pxy.readCh) go workConnReaderFn(pxy.workConn, pxy.readCh)
go heartbeatFn(pxy.workConn, pxy.sendCh) go heartbeatFn(pxy.sendCh)
udp.Forwarder(pxy.localAddr, pxy.readCh, pxy.sendCh, int(pxy.clientCfg.UDPPacketSize)) udp.Forwarder(pxy.localAddr, pxy.readCh, pxy.sendCh, int(pxy.clientCfg.UDPPacketSize))
} }
@@ -574,7 +576,7 @@ type SUDPProxy struct {
} }
func (pxy *SUDPProxy) Run() (err error) { func (pxy *SUDPProxy) Run() (err error) {
pxy.localAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pxy.cfg.LocalIP, pxy.cfg.LocalPort)) pxy.localAddr, err = net.ResolveUDPAddr("udp", net.JoinHostPort(pxy.cfg.LocalIP, strconv.Itoa(pxy.cfg.LocalPort)))
if err != nil { if err != nil {
return return
} }
@@ -690,7 +692,7 @@ func (pxy *SUDPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
} }
} }
heartbeatFn := func(conn net.Conn, sendCh chan msg.Message) { heartbeatFn := func(sendCh chan msg.Message) {
ticker := time.NewTicker(30 * time.Second) ticker := time.NewTicker(30 * time.Second)
defer func() { defer func() {
ticker.Stop() ticker.Stop()
@@ -716,14 +718,15 @@ func (pxy *SUDPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
go workConnSenderFn(workConn, sendCh) go workConnSenderFn(workConn, sendCh)
go workConnReaderFn(workConn, readCh) go workConnReaderFn(workConn, readCh)
go heartbeatFn(workConn, sendCh) go heartbeatFn(sendCh)
udp.Forwarder(pxy.localAddr, readCh, sendCh, int(pxy.clientCfg.UDPPacketSize)) udp.Forwarder(pxy.localAddr, readCh, sendCh, int(pxy.clientCfg.UDPPacketSize))
} }
// Common handler for tcp work connections. // Common handler for tcp work connections.
func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf, proxyPlugin plugin.Plugin, func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf, proxyPlugin plugin.Plugin,
baseInfo *config.BaseProxyConf, limiter *rate.Limiter, workConn net.Conn, encKey []byte, m *msg.StartWorkConn) { baseInfo *config.BaseProxyConf, limiter *rate.Limiter, workConn net.Conn, encKey []byte, m *msg.StartWorkConn,
) {
xl := xlog.FromContextSafe(ctx) xl := xlog.FromContextSafe(ctx)
var ( var (
remote io.ReadWriteCloser remote io.ReadWriteCloser
@@ -757,12 +760,12 @@ func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf
if m.DstAddr == "" { if m.DstAddr == "" {
m.DstAddr = "127.0.0.1" m.DstAddr = "127.0.0.1"
} }
srcAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.SrcAddr, strconv.Itoa(int(m.SrcPort))))
dstAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.DstAddr, strconv.Itoa(int(m.DstPort))))
h := &pp.Header{ h := &pp.Header{
Command: pp.PROXY, Command: pp.PROXY,
SourceAddress: net.ParseIP(m.SrcAddr), SourceAddr: srcAddr,
SourcePort: m.SrcPort, DestinationAddr: dstAddr,
DestinationAddress: net.ParseIP(m.DstAddr),
DestinationPort: m.DstPort,
} }
if strings.Contains(m.SrcAddr, ".") { if strings.Contains(m.SrcAddr, ".") {
@@ -778,7 +781,7 @@ func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf
} }
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
h.WriteTo(buf) _, _ = h.WriteTo(buf)
extraInfo = buf.Bytes() extraInfo = buf.Bytes()
} }
} }
@@ -791,7 +794,10 @@ func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf
return return
} }
localConn, err := frpNet.ConnectServer("tcp", fmt.Sprintf("%s:%d", localInfo.LocalIP, localInfo.LocalPort)) localConn, err := libdial.Dial(
net.JoinHostPort(localInfo.LocalIP, strconv.Itoa(localInfo.LocalPort)),
libdial.WithTimeout(10*time.Second),
)
if err != nil { if err != nil {
workConn.Close() workConn.Close()
xl.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIP, localInfo.LocalPort, err) xl.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIP, localInfo.LocalPort, err)
@@ -802,7 +808,11 @@ func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf
localConn.RemoteAddr().String(), workConn.LocalAddr().String(), workConn.RemoteAddr().String()) localConn.RemoteAddr().String(), workConn.LocalAddr().String(), workConn.RemoteAddr().String())
if len(extraInfo) > 0 { if len(extraInfo) > 0 {
localConn.Write(extraInfo) if _, err := localConn.Write(extraInfo); err != nil {
workConn.Close()
xl.Error("write extraInfo to local conn error: %v", err)
return
}
} }
frpIo.Join(localConn, remote) frpIo.Join(localConn, remote)

View File

@@ -6,12 +6,12 @@ import (
"net" "net"
"sync" "sync"
"github.com/fatedier/golib/errors"
"github.com/fatedier/frp/client/event" "github.com/fatedier/frp/client/event"
"github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/msg" "github.com/fatedier/frp/pkg/msg"
"github.com/fatedier/frp/pkg/util/xlog" "github.com/fatedier/frp/pkg/util/xlog"
"github.com/fatedier/golib/errors"
) )
type Manager struct { type Manager struct {
@@ -75,7 +75,7 @@ func (pm *Manager) HandleWorkConn(name string, workConn net.Conn, m *msg.StartWo
} }
} }
func (pm *Manager) HandleEvent(evType event.Type, payload interface{}) error { func (pm *Manager) HandleEvent(payload interface{}) error {
var m msg.Message var m msg.Message
switch e := payload.(type) { switch e := payload.(type) {
case *event.StartProxyPayload: case *event.StartProxyPayload:
@@ -113,10 +113,8 @@ func (pm *Manager) Reload(pxyCfgs map[string]config.ProxyConf) {
cfg, ok := pxyCfgs[name] cfg, ok := pxyCfgs[name]
if !ok { if !ok {
del = true del = true
} else { } else if !pxy.Cfg.Compare(cfg) {
if !pxy.Cfg.Compare(cfg) { del = true
del = true
}
} }
if del { if del {

View File

@@ -8,13 +8,13 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/fatedier/golib/errors"
"github.com/fatedier/frp/client/event" "github.com/fatedier/frp/client/event"
"github.com/fatedier/frp/client/health" "github.com/fatedier/frp/client/health"
"github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/msg" "github.com/fatedier/frp/pkg/msg"
"github.com/fatedier/frp/pkg/util/xlog" "github.com/fatedier/frp/pkg/util/xlog"
"github.com/fatedier/golib/errors"
) )
const ( const (
@@ -27,9 +27,9 @@ const (
) )
var ( var (
statusCheckInterval time.Duration = 3 * time.Second statusCheckInterval = 3 * time.Second
waitResponseTimeout = 20 * time.Second waitResponseTimeout = 20 * time.Second
startErrTimeout = 30 * time.Second startErrTimeout = 30 * time.Second
) )
type WorkingStatus struct { type WorkingStatus struct {
@@ -145,7 +145,7 @@ func (pw *Wrapper) Stop() {
} }
func (pw *Wrapper) close() { func (pw *Wrapper) close() {
pw.handler(event.EvCloseProxy, &event.CloseProxyPayload{ _ = pw.handler(&event.CloseProxyPayload{
CloseProxyMsg: &msg.CloseProxy{ CloseProxyMsg: &msg.CloseProxy{
ProxyName: pw.Name, ProxyName: pw.Name,
}, },
@@ -174,7 +174,7 @@ func (pw *Wrapper) checkWorker() {
var newProxyMsg msg.NewProxy var newProxyMsg msg.NewProxy
pw.Cfg.MarshalToMsg(&newProxyMsg) pw.Cfg.MarshalToMsg(&newProxyMsg)
pw.lastSendStartMsg = now pw.lastSendStartMsg = now
pw.handler(event.EvStartProxy, &event.StartProxyPayload{ _ = pw.handler(&event.StartProxyPayload{
NewProxyMsg: &newProxyMsg, NewProxyMsg: &newProxyMsg,
}) })
} }
@@ -201,7 +201,7 @@ func (pw *Wrapper) checkWorker() {
func (pw *Wrapper) statusNormalCallback() { func (pw *Wrapper) statusNormalCallback() {
xl := pw.xl xl := pw.xl
atomic.StoreUint32(&pw.health, 0) atomic.StoreUint32(&pw.health, 0)
errors.PanicToError(func() { _ = errors.PanicToError(func() {
select { select {
case pw.healthNotifyCh <- struct{}{}: case pw.healthNotifyCh <- struct{}{}:
default: default:
@@ -213,7 +213,7 @@ func (pw *Wrapper) statusNormalCallback() {
func (pw *Wrapper) statusFailedCallback() { func (pw *Wrapper) statusFailedCallback() {
xl := pw.xl xl := pw.xl
atomic.StoreUint32(&pw.health, 1) atomic.StoreUint32(&pw.health, 1)
errors.PanicToError(func() { _ = errors.PanicToError(func() {
select { select {
case pw.healthNotifyCh <- struct{}{}: case pw.healthNotifyCh <- struct{}{}:
default: default:
@@ -227,7 +227,7 @@ func (pw *Wrapper) InWorkConn(workConn net.Conn, m *msg.StartWorkConn) {
pw.mu.RLock() pw.mu.RLock()
pxy := pw.pxy pxy := pw.pxy
pw.mu.RUnlock() pw.mu.RUnlock()
if pxy != nil { if pxy != nil && pw.Phase == ProxyPhaseRunning {
xl.Debug("start a new work connection, localAddr: %s remoteAddr: %s", workConn.LocalAddr().String(), workConn.RemoteAddr().String()) xl.Debug("start a new work connection, localAddr: %s remoteAddr: %s", workConn.LocalAddr().String(), workConn.RemoteAddr().String())
go pxy.InWorkConn(workConn, m) go pxy.InWorkConn(workConn, m)
} else { } else {

View File

@@ -17,16 +17,21 @@ package client
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
"errors"
"fmt" "fmt"
"io/ioutil" "io"
"math/rand"
"net" "net"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/fatedier/golib/crypto"
libdial "github.com/fatedier/golib/net/dial"
fmux "github.com/hashicorp/yamux"
"github.com/fatedier/frp/assets" "github.com/fatedier/frp/assets"
"github.com/fatedier/frp/pkg/auth" "github.com/fatedier/frp/pkg/auth"
"github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/config"
@@ -34,12 +39,16 @@ import (
"github.com/fatedier/frp/pkg/transport" "github.com/fatedier/frp/pkg/transport"
"github.com/fatedier/frp/pkg/util/log" "github.com/fatedier/frp/pkg/util/log"
frpNet "github.com/fatedier/frp/pkg/util/net" frpNet "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/util"
"github.com/fatedier/frp/pkg/util/version" "github.com/fatedier/frp/pkg/util/version"
"github.com/fatedier/frp/pkg/util/xlog" "github.com/fatedier/frp/pkg/util/xlog"
fmux "github.com/hashicorp/yamux"
) )
func init() {
crypto.DefaultSalt = "frp"
rand.Seed(time.Now().UnixNano())
}
// Service is a client service. // Service is a client service.
type Service struct { type Service struct {
// uniq id got from frps, attach it in loginMsg // uniq id got from frps, attach it in loginMsg
@@ -72,8 +81,12 @@ type Service struct {
cancel context.CancelFunc cancel context.CancelFunc
} }
func NewService(cfg config.ClientCommonConf, pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf, cfgFile string) (svr *Service, err error) { func NewService(
cfg config.ClientCommonConf,
pxyCfgs map[string]config.ProxyConf,
visitorCfgs map[string]config.VisitorConf,
cfgFile string,
) (svr *Service, err error) {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
svr = &Service{ svr = &Service{
authSetter: auth.NewAuthSetter(cfg.ClientConfig), authSetter: auth.NewAuthSetter(cfg.ClientConfig),
@@ -97,6 +110,21 @@ func (svr *Service) GetController() *Control {
func (svr *Service) Run() error { func (svr *Service) Run() error {
xl := xlog.FromContextSafe(svr.ctx) xl := xlog.FromContextSafe(svr.ctx)
// set custom DNSServer
if svr.cfg.DNSServer != "" {
dnsAddr := svr.cfg.DNSServer
if !strings.Contains(dnsAddr, ":") {
dnsAddr += ":53"
}
// Change default dns server for frpc
net.DefaultResolver = &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
return net.Dial("udp", dnsAddr)
},
}
}
// login to frps // login to frps
for { for {
conn, session, err := svr.login() conn, session, err := svr.login()
@@ -108,7 +136,7 @@ func (svr *Service) Run() error {
if svr.cfg.LoginFailExit { if svr.cfg.LoginFailExit {
return err return err
} }
time.Sleep(10 * time.Second) util.RandomSleep(10*time.Second, 0.9, 1.1)
} else { } else {
// login success // login success
ctl := NewControl(svr.ctx, svr.runID, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort, svr.authSetter) ctl := NewControl(svr.ctx, svr.runID, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort, svr.authSetter)
@@ -124,13 +152,10 @@ func (svr *Service) Run() error {
if svr.cfg.AdminPort != 0 { if svr.cfg.AdminPort != 0 {
// Init admin server assets // Init admin server assets
err := assets.Load(svr.cfg.AssetsDir) assets.Load(svr.cfg.AssetsDir)
if err != nil {
return fmt.Errorf("Load assets error: %v", err)
}
address := net.JoinHostPort(svr.cfg.AdminAddr, strconv.Itoa(svr.cfg.AdminPort)) address := net.JoinHostPort(svr.cfg.AdminAddr, strconv.Itoa(svr.cfg.AdminPort))
err = svr.RunAdminServer(address) err := svr.RunAdminServer(address)
if err != nil { if err != nil {
log.Warn("run admin server error: %v", err) log.Warn("run admin server error: %v", err)
} }
@@ -160,8 +185,11 @@ func (svr *Service) keepControllerWorking() {
// the first three retry with no delay // the first three retry with no delay
if reconnectCounts > 3 { if reconnectCounts > 3 {
time.Sleep(reconnectDelay) util.RandomSleep(reconnectDelay, 0.9, 1.1)
xl.Info("wait %v to reconnect", reconnectDelay)
reconnectDelay *= 2 reconnectDelay *= 2
} else {
util.RandomSleep(time.Second, 0, 0.5)
} }
reconnectCounts++ reconnectCounts++
@@ -174,21 +202,19 @@ func (svr *Service) keepControllerWorking() {
} }
for { for {
if atomic.LoadUint32(&svr.exit) != 0 {
return
}
xl.Info("try to reconnect to server...") xl.Info("try to reconnect to server...")
conn, session, err := svr.login() conn, session, err := svr.login()
if err != nil { if err != nil {
xl.Warn("reconnect to server error: %v", err) xl.Warn("reconnect to server error: %v, wait %v for another retry", err, delayTime)
time.Sleep(delayTime) util.RandomSleep(delayTime, 0.9, 1.1)
opErr := &net.OpError{} delayTime *= 2
// quick retry for dial error if delayTime > maxDelayTime {
if errors.As(err, &opErr) && opErr.Op == "dial" { delayTime = maxDelayTime
delayTime = 2 * time.Second
} else {
delayTime = delayTime * 2
if delayTime > maxDelayTime {
delayTime = maxDelayTime
}
} }
continue continue
} }
@@ -231,8 +257,35 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) {
} }
} }
address := net.JoinHostPort(svr.cfg.ServerAddr, strconv.Itoa(svr.cfg.ServerPort)) proxyType, addr, auth, err := libdial.ParseProxyURL(svr.cfg.HTTPProxy)
conn, err = frpNet.ConnectServerByProxyWithTLS(svr.cfg.HTTPProxy, svr.cfg.Protocol, address, tlsConfig) if err != nil {
xl.Error("fail to parse proxy url")
return
}
dialOptions := []libdial.DialOption{}
protocol := svr.cfg.Protocol
if protocol == "websocket" {
protocol = "tcp"
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: frpNet.DialHookWebsocket()}))
}
if svr.cfg.ConnectServerLocalIP != "" {
dialOptions = append(dialOptions, libdial.WithLocalAddr(svr.cfg.ConnectServerLocalIP))
}
dialOptions = append(dialOptions,
libdial.WithProtocol(protocol),
libdial.WithTimeout(time.Duration(svr.cfg.DialServerTimeout)*time.Second),
libdial.WithKeepAlive(time.Duration(svr.cfg.DialServerKeepAlive)*time.Second),
libdial.WithProxy(proxyType, addr),
libdial.WithProxyAuth(auth),
libdial.WithTLSConfig(tlsConfig),
libdial.WithAfterHook(libdial.AfterHook{
Hook: frpNet.DialHookCustomTLSHeadByte(tlsConfig != nil, svr.cfg.DisableCustomTLSFirstByte),
}),
)
conn, err = libdial.Dial(
net.JoinHostPort(svr.cfg.ServerAddr, strconv.Itoa(svr.cfg.ServerPort)),
dialOptions...,
)
if err != nil { if err != nil {
return return
} }
@@ -248,8 +301,8 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) {
if svr.cfg.TCPMux { if svr.cfg.TCPMux {
fmuxCfg := fmux.DefaultConfig() fmuxCfg := fmux.DefaultConfig()
fmuxCfg.KeepAliveInterval = 20 * time.Second fmuxCfg.KeepAliveInterval = time.Duration(svr.cfg.TCPMuxKeepaliveInterval) * time.Second
fmuxCfg.LogOutput = ioutil.Discard fmuxCfg.LogOutput = io.Discard
session, err = fmux.Client(conn, fmuxCfg) session, err = fmux.Client(conn, fmuxCfg)
if err != nil { if err != nil {
return return
@@ -284,11 +337,11 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) {
} }
var loginRespMsg msg.LoginResp var loginRespMsg msg.LoginResp
conn.SetReadDeadline(time.Now().Add(10 * time.Second)) _ = conn.SetReadDeadline(time.Now().Add(10 * time.Second))
if err = msg.ReadMsgInto(conn, &loginRespMsg); err != nil { if err = msg.ReadMsgInto(conn, &loginRespMsg); err != nil {
return return
} }
conn.SetReadDeadline(time.Time{}) _ = conn.SetReadDeadline(time.Time{})
if loginRespMsg.Error != "" { if loginRespMsg.Error != "" {
err = fmt.Errorf("%s", loginRespMsg.Error) err = fmt.Errorf("%s", loginRespMsg.Error)
@@ -311,13 +364,28 @@ func (svr *Service) ReloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs
svr.visitorCfgs = visitorCfgs svr.visitorCfgs = visitorCfgs
svr.cfgMu.Unlock() svr.cfgMu.Unlock()
return svr.ctl.ReloadConf(pxyCfgs, visitorCfgs) svr.ctlMu.RLock()
ctl := svr.ctl
svr.ctlMu.RUnlock()
if ctl != nil {
return svr.ctl.ReloadConf(pxyCfgs, visitorCfgs)
}
return nil
} }
func (svr *Service) Close() { func (svr *Service) Close() {
svr.GracefulClose(time.Duration(0))
}
func (svr *Service) GracefulClose(d time.Duration) {
atomic.StoreUint32(&svr.exit, 1) atomic.StoreUint32(&svr.exit, 1)
svr.ctlMu.RLock()
if svr.ctl != nil { if svr.ctl != nil {
svr.ctl.Close() svr.ctl.GracefulClose(d)
} }
svr.ctlMu.RUnlock()
svr.cancel() svr.cancel()
} }

View File

@@ -19,22 +19,22 @@ import (
"context" "context"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net" "net"
"strconv"
"sync" "sync"
"time" "time"
"github.com/fatedier/golib/errors"
frpIo "github.com/fatedier/golib/io"
"github.com/fatedier/golib/pool"
fmux "github.com/hashicorp/yamux"
"github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/msg" "github.com/fatedier/frp/pkg/msg"
"github.com/fatedier/frp/pkg/proto/udp" "github.com/fatedier/frp/pkg/proto/udp"
frpNet "github.com/fatedier/frp/pkg/util/net" frpNet "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/util" "github.com/fatedier/frp/pkg/util/util"
"github.com/fatedier/frp/pkg/util/xlog" "github.com/fatedier/frp/pkg/util/xlog"
"github.com/fatedier/golib/errors"
frpIo "github.com/fatedier/golib/io"
"github.com/fatedier/golib/pool"
fmux "github.com/hashicorp/yamux"
) )
// Visitor is used for forward traffics from local port tot remote service. // Visitor is used for forward traffics from local port tot remote service.
@@ -71,9 +71,8 @@ func NewVisitor(ctx context.Context, ctl *Control, cfg config.VisitorConf) (visi
} }
type BaseVisitor struct { type BaseVisitor struct {
ctl *Control ctl *Control
l net.Listener l net.Listener
closed bool
mu sync.RWMutex mu sync.RWMutex
ctx context.Context ctx context.Context
@@ -86,7 +85,7 @@ type STCPVisitor struct {
} }
func (sv *STCPVisitor) Run() (err error) { func (sv *STCPVisitor) Run() (err error) {
sv.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort)) sv.l, err = net.Listen("tcp", net.JoinHostPort(sv.cfg.BindAddr, strconv.Itoa(sv.cfg.BindPort)))
if err != nil { if err != nil {
return return
} }
@@ -138,13 +137,13 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) {
} }
var newVisitorConnRespMsg msg.NewVisitorConnResp var newVisitorConnRespMsg msg.NewVisitorConnResp
visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second)) _ = visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg) err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg)
if err != nil { if err != nil {
xl.Warn("get newVisitorConnRespMsg error: %v", err) xl.Warn("get newVisitorConnRespMsg error: %v", err)
return return
} }
visitorConn.SetReadDeadline(time.Time{}) _ = visitorConn.SetReadDeadline(time.Time{})
if newVisitorConnRespMsg.Error != "" { if newVisitorConnRespMsg.Error != "" {
xl.Warn("start new visitor connection error: %s", newVisitorConnRespMsg.Error) xl.Warn("start new visitor connection error: %s", newVisitorConnRespMsg.Error)
@@ -175,7 +174,7 @@ type XTCPVisitor struct {
} }
func (sv *XTCPVisitor) Run() (err error) { func (sv *XTCPVisitor) Run() (err error) {
sv.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort)) sv.l, err = net.Listen("tcp", net.JoinHostPort(sv.cfg.BindAddr, strconv.Itoa(sv.cfg.BindPort)))
if err != nil { if err != nil {
return return
} }
@@ -212,7 +211,7 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
} }
raddr, err := net.ResolveUDPAddr("udp", raddr, err := net.ResolveUDPAddr("udp",
fmt.Sprintf("%s:%d", sv.ctl.clientCfg.ServerAddr, sv.ctl.serverUDPPort)) net.JoinHostPort(sv.ctl.clientCfg.ServerAddr, strconv.Itoa(sv.ctl.serverUDPPort)))
if err != nil { if err != nil {
xl.Error("resolve server UDP addr error") xl.Error("resolve server UDP addr error")
return return
@@ -239,7 +238,7 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
// Wait for client address at most 10 seconds. // Wait for client address at most 10 seconds.
var natHoleRespMsg msg.NatHoleResp var natHoleRespMsg msg.NatHoleResp
visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second)) _ = visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
buf := pool.GetBuf(1024) buf := pool.GetBuf(1024)
n, err := visitorConn.Read(buf) n, err := visitorConn.Read(buf)
if err != nil { if err != nil {
@@ -252,7 +251,7 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
xl.Warn("get natHoleRespMsg error: %v", err) xl.Warn("get natHoleRespMsg error: %v", err)
return return
} }
visitorConn.SetReadDeadline(time.Time{}) _ = visitorConn.SetReadDeadline(time.Time{})
pool.PutBuf(buf) pool.PutBuf(buf)
if natHoleRespMsg.Error != "" { if natHoleRespMsg.Error != "" {
@@ -279,17 +278,20 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
} }
defer lConn.Close() defer lConn.Close()
lConn.Write([]byte(natHoleRespMsg.Sid)) if _, err := lConn.Write([]byte(natHoleRespMsg.Sid)); err != nil {
xl.Error("write sid error: %v", err)
return
}
// read ack sid from client // read ack sid from client
sidBuf := pool.GetBuf(1024) sidBuf := pool.GetBuf(1024)
lConn.SetReadDeadline(time.Now().Add(8 * time.Second)) _ = lConn.SetReadDeadline(time.Now().Add(8 * time.Second))
n, err = lConn.Read(sidBuf) n, err = lConn.Read(sidBuf)
if err != nil { if err != nil {
xl.Warn("get sid from client error: %v", err) xl.Warn("get sid from client error: %v", err)
return return
} }
lConn.SetReadDeadline(time.Time{}) _ = lConn.SetReadDeadline(time.Time{})
if string(sidBuf[:n]) != natHoleRespMsg.Sid { if string(sidBuf[:n]) != natHoleRespMsg.Sid {
xl.Warn("incorrect sid from client") xl.Warn("incorrect sid from client")
return return
@@ -308,7 +310,7 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
fmuxCfg := fmux.DefaultConfig() fmuxCfg := fmux.DefaultConfig()
fmuxCfg.KeepAliveInterval = 5 * time.Second fmuxCfg.KeepAliveInterval = 5 * time.Second
fmuxCfg.LogOutput = ioutil.Discard fmuxCfg.LogOutput = io.Discard
sess, err := fmux.Client(remote, fmuxCfg) sess, err := fmux.Client(remote, fmuxCfg)
if err != nil { if err != nil {
xl.Error("create yamux session error: %v", err) xl.Error("create yamux session error: %v", err)
@@ -353,7 +355,7 @@ type SUDPVisitor struct {
func (sv *SUDPVisitor) Run() (err error) { func (sv *SUDPVisitor) Run() (err error) {
xl := xlog.FromContextSafe(sv.ctx) xl := xlog.FromContextSafe(sv.ctx)
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort)) addr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(sv.cfg.BindAddr, strconv.Itoa(sv.cfg.BindPort)))
if err != nil { if err != nil {
return fmt.Errorf("sudp ResolveUDPAddr error: %v", err) return fmt.Errorf("sudp ResolveUDPAddr error: %v", err)
} }
@@ -366,7 +368,7 @@ func (sv *SUDPVisitor) Run() (err error) {
sv.sendCh = make(chan *msg.UDPPacket, 1024) sv.sendCh = make(chan *msg.UDPPacket, 1024)
sv.readCh = make(chan *msg.UDPPacket, 1024) sv.readCh = make(chan *msg.UDPPacket, 1024)
xl.Info("sudp start to work") xl.Info("sudp start to work, listen on %s", addr)
go sv.dispatcher() go sv.dispatcher()
go udp.ForwardUserConn(sv.udpConn, sv.readCh, sv.sendCh, int(sv.ctl.clientCfg.UDPPacketSize)) go udp.ForwardUserConn(sv.udpConn, sv.readCh, sv.sendCh, int(sv.ctl.clientCfg.UDPPacketSize))
@@ -377,29 +379,33 @@ func (sv *SUDPVisitor) Run() (err error) {
func (sv *SUDPVisitor) dispatcher() { func (sv *SUDPVisitor) dispatcher() {
xl := xlog.FromContextSafe(sv.ctx) xl := xlog.FromContextSafe(sv.ctx)
var (
visitorConn net.Conn
err error
firstPacket *msg.UDPPacket
)
for { for {
// loop for get frpc to frps tcp conn select {
// setup worker case firstPacket = <-sv.sendCh:
// wait worker to finished if firstPacket == nil {
// retry or exit
visitorConn, err := sv.getNewVisitorConn()
if err != nil {
// check if proxy is closed
// if checkCloseCh is close, we will return, other case we will continue to reconnect
select {
case <-sv.checkCloseCh:
xl.Info("frpc sudp visitor proxy is closed") xl.Info("frpc sudp visitor proxy is closed")
return return
default:
} }
case <-sv.checkCloseCh:
xl.Info("frpc sudp visitor proxy is closed")
return
}
time.Sleep(3 * time.Second) visitorConn, err = sv.getNewVisitorConn()
if err != nil {
xl.Warn("newVisitorConn to frps error: %v, try to reconnect", err) xl.Warn("newVisitorConn to frps error: %v, try to reconnect", err)
continue continue
} }
sv.worker(visitorConn) // visitorConn always be closed when worker done.
sv.worker(visitorConn, firstPacket)
select { select {
case <-sv.checkCloseCh: case <-sv.checkCloseCh:
@@ -409,7 +415,7 @@ func (sv *SUDPVisitor) dispatcher() {
} }
} }
func (sv *SUDPVisitor) worker(workConn net.Conn) { func (sv *SUDPVisitor) worker(workConn net.Conn, firstPacket *msg.UDPPacket) {
xl := xlog.FromContextSafe(sv.ctx) xl := xlog.FromContextSafe(sv.ctx)
xl.Debug("starting sudp proxy worker") xl.Debug("starting sudp proxy worker")
@@ -432,13 +438,13 @@ func (sv *SUDPVisitor) worker(workConn net.Conn) {
) )
// frpc will send heartbeat in workConn to frpc visitor for keeping alive // frpc will send heartbeat in workConn to frpc visitor for keeping alive
conn.SetReadDeadline(time.Now().Add(60 * time.Second)) _ = conn.SetReadDeadline(time.Now().Add(60 * time.Second))
if rawMsg, errRet = msg.ReadMsg(conn); errRet != nil { if rawMsg, errRet = msg.ReadMsg(conn); errRet != nil {
xl.Warn("read from workconn for user udp conn error: %v", errRet) xl.Warn("read from workconn for user udp conn error: %v", errRet)
return return
} }
conn.SetReadDeadline(time.Time{}) _ = conn.SetReadDeadline(time.Time{})
switch m := rawMsg.(type) { switch m := rawMsg.(type) {
case *msg.Ping: case *msg.Ping:
xl.Debug("frpc visitor get ping message from frpc") xl.Debug("frpc visitor get ping message from frpc")
@@ -446,7 +452,7 @@ func (sv *SUDPVisitor) worker(workConn net.Conn) {
case *msg.UDPPacket: case *msg.UDPPacket:
if errRet := errors.PanicToError(func() { if errRet := errors.PanicToError(func() {
sv.readCh <- m sv.readCh <- m
xl.Trace("frpc visitor get udp packet from frpc") xl.Trace("frpc visitor get udp packet from workConn: %s", m.Content)
}); errRet != nil { }); errRet != nil {
xl.Info("reader goroutine for udp work connection closed") xl.Info("reader goroutine for udp work connection closed")
return return
@@ -463,6 +469,14 @@ func (sv *SUDPVisitor) worker(workConn net.Conn) {
}() }()
var errRet error var errRet error
if firstPacket != nil {
if errRet = msg.WriteMsg(conn, firstPacket); errRet != nil {
xl.Warn("sender goroutine for udp work connection closed: %v", errRet)
return
}
xl.Trace("send udp package to workConn: %s", firstPacket.Content)
}
for { for {
select { select {
case udpMsg, ok := <-sv.sendCh: case udpMsg, ok := <-sv.sendCh:
@@ -475,6 +489,7 @@ func (sv *SUDPVisitor) worker(workConn net.Conn) {
xl.Warn("sender goroutine for udp work connection closed: %v", errRet) xl.Warn("sender goroutine for udp work connection closed: %v", errRet)
return return
} }
xl.Trace("send udp package to workConn: %s", udpMsg.Content)
case <-closeCh: case <-closeCh:
return return
} }
@@ -509,12 +524,12 @@ func (sv *SUDPVisitor) getNewVisitorConn() (net.Conn, error) {
} }
var newVisitorConnRespMsg msg.NewVisitorConnResp var newVisitorConnRespMsg msg.NewVisitorConnResp
visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second)) _ = visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg) err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg)
if err != nil { if err != nil {
return nil, fmt.Errorf("frpc read newVisitorConnRespMsg error: %v", err) return nil, fmt.Errorf("frpc read newVisitorConnRespMsg error: %v", err)
} }
visitorConn.SetReadDeadline(time.Time{}) _ = visitorConn.SetReadDeadline(time.Time{})
if newVisitorConnRespMsg.Error != "" { if newVisitorConnRespMsg.Error != "" {
return nil, fmt.Errorf("start new visitor connection error: %s", newVisitorConnRespMsg.Error) return nil, fmt.Errorf("start new visitor connection error: %s", newVisitorConnRespMsg.Error)

View File

@@ -65,7 +65,7 @@ func (vm *VisitorManager) Run() {
name := cfg.GetBaseInfo().ProxyName name := cfg.GetBaseInfo().ProxyName
if _, exist := vm.visitors[name]; !exist { if _, exist := vm.visitors[name]; !exist {
xl.Info("try to start visitor [%s]", name) xl.Info("try to start visitor [%s]", name)
vm.startVisitor(cfg) _ = vm.startVisitor(cfg)
} }
} }
vm.mu.Unlock() vm.mu.Unlock()
@@ -99,10 +99,8 @@ func (vm *VisitorManager) Reload(cfgs map[string]config.VisitorConf) {
cfg, ok := cfgs[name] cfg, ok := cfgs[name]
if !ok { if !ok {
del = true del = true
} else { } else if !oldCfg.Compare(cfg) {
if !oldCfg.Compare(cfg) { del = true
del = true
}
} }
if del { if del {
@@ -123,13 +121,12 @@ func (vm *VisitorManager) Reload(cfgs map[string]config.VisitorConf) {
if _, ok := vm.cfgs[name]; !ok { if _, ok := vm.cfgs[name]; !ok {
vm.cfgs[name] = cfg vm.cfgs[name] = cfg
addNames = append(addNames, name) addNames = append(addNames, name)
vm.startVisitor(cfg) _ = vm.startVisitor(cfg)
} }
} }
if len(addNames) > 0 { if len(addNames) > 0 {
xl.Info("visitor added: %v", addNames) xl.Info("visitor added: %v", addNames)
} }
return
} }
func (vm *VisitorManager) Close() { func (vm *VisitorManager) Close() {

View File

@@ -15,18 +15,10 @@
package main package main
import ( import (
"math/rand" _ "github.com/fatedier/frp/assets/frpc"
"time"
_ "github.com/fatedier/frp/assets/frpc/statik"
"github.com/fatedier/frp/cmd/frpc/sub" "github.com/fatedier/frp/cmd/frpc/sub"
"github.com/fatedier/golib/crypto"
) )
func main() { func main() {
crypto.DefaultSalt = "frp"
rand.Seed(time.Now().UnixNano())
sub.Execute() sub.Execute()
} }

View File

@@ -19,10 +19,10 @@ import (
"os" "os"
"strings" "strings"
"github.com/spf13/cobra"
"github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/consts" "github.com/fatedier/frp/pkg/consts"
"github.com/spf13/cobra"
) )
func init() { func init() {
@@ -47,7 +47,7 @@ var httpCmd = &cobra.Command{
Use: "http", Use: "http",
Short: "Run frpc with a single http proxy", Short: "Run frpc with a single http proxy",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) clientCfg, err := parseClientCommonCfgFromCmd()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)

View File

@@ -43,7 +43,7 @@ var httpsCmd = &cobra.Command{
Use: "https", Use: "https",
Short: "Run frpc with a single https proxy", Short: "Run frpc with a single https proxy",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) clientCfg, err := parseClientCommonCfgFromCmd()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)

View File

@@ -17,14 +17,14 @@ package sub
import ( import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"os" "os"
"strings" "strings"
"github.com/fatedier/frp/pkg/config"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/fatedier/frp/pkg/config"
) )
func init() { func init() {
@@ -35,19 +35,13 @@ var reloadCmd = &cobra.Command{
Use: "reload", Use: "reload",
Short: "Hot-Reload frpc configuration", Short: "Hot-Reload frpc configuration",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
iniContent, err := config.GetRenderedConfFromFile(cfgFile) cfg, _, _, err := config.ParseClientConfig(cfgFile)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
clientCfg, err := parseClientCommonCfg(CfgFileTypeIni, iniContent) err = reload(cfg)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
err = reload(clientCfg)
if err != nil { if err != nil {
fmt.Printf("frpc reload error: %v\n", err) fmt.Printf("frpc reload error: %v\n", err)
os.Exit(1) os.Exit(1)
@@ -82,7 +76,7 @@ func reload(clientCfg config.ClientCommonConf) error {
return nil return nil
} }
body, err := ioutil.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -15,23 +15,24 @@
package sub package sub
import ( import (
"context"
"fmt" "fmt"
"io/fs"
"net" "net"
"os" "os"
"os/signal" "os/signal"
"path/filepath"
"strconv" "strconv"
"strings" "sync"
"syscall" "syscall"
"time" "time"
"github.com/spf13/cobra"
"github.com/fatedier/frp/client" "github.com/fatedier/frp/client"
"github.com/fatedier/frp/pkg/auth" "github.com/fatedier/frp/pkg/auth"
"github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/util/log" "github.com/fatedier/frp/pkg/util/log"
"github.com/fatedier/frp/pkg/util/version" "github.com/fatedier/frp/pkg/util/version"
"github.com/spf13/cobra"
) )
const ( const (
@@ -41,6 +42,7 @@ const (
var ( var (
cfgFile string cfgFile string
cfgDir string
showVersion bool showVersion bool
serverAddr string serverAddr string
@@ -72,15 +74,12 @@ var (
bindPort int bindPort int
tlsEnable bool tlsEnable bool
kcpDoneCh chan struct{}
) )
func init() { func init() {
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "./frpc.ini", "config file of frpc") rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "./frpc.ini", "config file of frpc")
rootCmd.PersistentFlags().StringVarP(&cfgDir, "config_dir", "", "", "config directory, run one frpc service for each file in config directory")
rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc") rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
kcpDoneCh = make(chan struct{})
} }
func RegisterCommonFlags(cmd *cobra.Command) { func RegisterCommonFlags(cmd *cobra.Command) {
@@ -104,6 +103,32 @@ var rootCmd = &cobra.Command{
return nil return nil
} }
// If cfgDir is not empty, run multiple frpc service for each config file in cfgDir.
// Note that it's only designed for testing. It's not guaranteed to be stable.
if cfgDir != "" {
var wg sync.WaitGroup
_ = filepath.WalkDir(cfgDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return nil
}
if d.IsDir() {
return nil
}
wg.Add(1)
time.Sleep(time.Millisecond)
go func() {
defer wg.Done()
err := runClient(path)
if err != nil {
fmt.Printf("frpc service error for config file [%s]\n", path)
}
}()
return nil
})
wg.Wait()
return nil
}
// Do not show command usage here. // Do not show command usage here.
err := runClient(cfgFile) err := runClient(cfgFile)
if err != nil { if err != nil {
@@ -120,32 +145,12 @@ func Execute() {
} }
} }
func handleSignal(svr *client.Service) { func handleSignal(svr *client.Service, doneCh chan struct{}) {
ch := make(chan os.Signal) ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
<-ch <-ch
svr.Close() svr.GracefulClose(500 * time.Millisecond)
time.Sleep(250 * time.Millisecond) close(doneCh)
close(kcpDoneCh)
}
func parseClientCommonCfg(fileType int, source []byte) (cfg config.ClientCommonConf, err error) {
if fileType == CfgFileTypeIni {
cfg, err = config.UnmarshalClientConfFromIni(source)
} else if fileType == CfgFileTypeCmd {
cfg, err = parseClientCommonCfgFromCmd()
}
if err != nil {
return
}
cfg.Complete()
err = cfg.Validate()
if err != nil {
err = fmt.Errorf("Parse config error: %v", err)
return
}
return
} }
func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) { func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
@@ -176,26 +181,19 @@ func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
cfg.Token = token cfg.Token = token
cfg.TLSEnable = tlsEnable cfg.TLSEnable = tlsEnable
cfg.Complete()
if err = cfg.Validate(); err != nil {
err = fmt.Errorf("parse config error: %v", err)
return
}
return return
} }
func runClient(cfgFilePath string) (err error) { func runClient(cfgFilePath string) error {
var content []byte cfg, pxyCfgs, visitorCfgs, err := config.ParseClientConfig(cfgFilePath)
content, err = config.GetRenderedConfFromFile(cfgFilePath)
if err != nil { if err != nil {
return err return err
} }
cfg, err := parseClientCommonCfg(CfgFileTypeIni, content)
if err != nil {
return err
}
pxyCfgs, visitorCfgs, err := config.LoadAllProxyConfsFromIni(cfg.User, content, cfg.Start)
if err != nil {
return err
}
return startService(cfg, pxyCfgs, visitorCfgs, cfgFilePath) return startService(cfg, pxyCfgs, visitorCfgs, cfgFilePath)
} }
@@ -205,22 +203,12 @@ func startService(
visitorCfgs map[string]config.VisitorConf, visitorCfgs map[string]config.VisitorConf,
cfgFile string, cfgFile string,
) (err error) { ) (err error) {
log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel, log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel,
cfg.LogMaxDays, cfg.DisableLogColor) cfg.LogMaxDays, cfg.DisableLogColor)
if cfg.DNSServer != "" { if cfgFile != "" {
s := cfg.DNSServer log.Trace("start frpc service for config file [%s]", cfgFile)
if !strings.Contains(s, ":") { defer log.Trace("frpc service for config file [%s] stopped", cfgFile)
s += ":53"
}
// Change default dns server for frpc
net.DefaultResolver = &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
return net.Dial("udp", s)
},
}
} }
svr, errRet := client.NewService(cfg, pxyCfgs, visitorCfgs, cfgFile) svr, errRet := client.NewService(cfg, pxyCfgs, visitorCfgs, cfgFile)
if errRet != nil { if errRet != nil {
@@ -228,13 +216,14 @@ func startService(
return return
} }
kcpDoneCh := make(chan struct{})
// Capture the exit signal if we use kcp. // Capture the exit signal if we use kcp.
if cfg.Protocol == "kcp" { if cfg.Protocol == "kcp" {
go handleSignal(svr) go handleSignal(svr, kcpDoneCh)
} }
err = svr.Run() err = svr.Run()
if cfg.Protocol == "kcp" { if err == nil && cfg.Protocol == "kcp" {
<-kcpDoneCh <-kcpDoneCh
} }
return return

View File

@@ -18,16 +18,16 @@ import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"os" "os"
"strings" "strings"
"github.com/fatedier/frp/client"
"github.com/fatedier/frp/pkg/config"
"github.com/rodaine/table" "github.com/rodaine/table"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/fatedier/frp/client"
"github.com/fatedier/frp/pkg/config"
) )
func init() { func init() {
@@ -38,20 +38,13 @@ var statusCmd = &cobra.Command{
Use: "status", Use: "status",
Short: "Overview of all proxies status", Short: "Overview of all proxies status",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
iniContent, err := config.GetRenderedConfFromFile(cfgFile) cfg, _, _, err := config.ParseClientConfig(cfgFile)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
clientCfg, err := parseClientCommonCfg(CfgFileTypeIni, iniContent) if err = status(cfg); err != nil {
if err != nil {
fmt.Println(err)
os.Exit(1)
}
err = status(clientCfg)
if err != nil {
fmt.Printf("frpc get status error: %v\n", err) fmt.Printf("frpc get status error: %v\n", err)
os.Exit(1) os.Exit(1)
} }
@@ -84,7 +77,7 @@ func status(clientCfg config.ClientCommonConf) error {
return fmt.Errorf("admin api status code [%d]", resp.StatusCode) return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
} }
body, err := ioutil.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return err return err
} }
@@ -96,7 +89,7 @@ func status(clientCfg config.ClientCommonConf) error {
fmt.Println("Proxy Status...") fmt.Println("Proxy Status...")
if len(res.TCP) > 0 { if len(res.TCP) > 0 {
fmt.Printf("TCP") fmt.Println("TCP")
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
for _, ps := range res.TCP { for _, ps := range res.TCP {
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
@@ -105,7 +98,7 @@ func status(clientCfg config.ClientCommonConf) error {
fmt.Println("") fmt.Println("")
} }
if len(res.UDP) > 0 { if len(res.UDP) > 0 {
fmt.Printf("UDP") fmt.Println("UDP")
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
for _, ps := range res.UDP { for _, ps := range res.UDP {
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
@@ -114,7 +107,7 @@ func status(clientCfg config.ClientCommonConf) error {
fmt.Println("") fmt.Println("")
} }
if len(res.HTTP) > 0 { if len(res.HTTP) > 0 {
fmt.Printf("HTTP") fmt.Println("HTTP")
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
for _, ps := range res.HTTP { for _, ps := range res.HTTP {
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
@@ -123,7 +116,7 @@ func status(clientCfg config.ClientCommonConf) error {
fmt.Println("") fmt.Println("")
} }
if len(res.HTTPS) > 0 { if len(res.HTTPS) > 0 {
fmt.Printf("HTTPS") fmt.Println("HTTPS")
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
for _, ps := range res.HTTPS { for _, ps := range res.HTTPS {
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
@@ -132,7 +125,7 @@ func status(clientCfg config.ClientCommonConf) error {
fmt.Println("") fmt.Println("")
} }
if len(res.STCP) > 0 { if len(res.STCP) > 0 {
fmt.Printf("STCP") fmt.Println("STCP")
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
for _, ps := range res.STCP { for _, ps := range res.STCP {
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
@@ -141,7 +134,7 @@ func status(clientCfg config.ClientCommonConf) error {
fmt.Println("") fmt.Println("")
} }
if len(res.XTCP) > 0 { if len(res.XTCP) > 0 {
fmt.Printf("XTCP") fmt.Println("XTCP")
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
for _, ps := range res.XTCP { for _, ps := range res.XTCP {
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)

View File

@@ -18,10 +18,10 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/spf13/cobra"
"github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/consts" "github.com/fatedier/frp/pkg/consts"
"github.com/spf13/cobra"
) )
func init() { func init() {
@@ -45,7 +45,7 @@ var stcpCmd = &cobra.Command{
Use: "stcp", Use: "stcp",
Short: "Run frpc with a single stcp proxy", Short: "Run frpc with a single stcp proxy",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) clientCfg, err := parseClientCommonCfgFromCmd()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)

View File

@@ -18,10 +18,10 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/spf13/cobra"
"github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/consts" "github.com/fatedier/frp/pkg/consts"
"github.com/spf13/cobra"
) )
func init() { func init() {
@@ -45,7 +45,7 @@ var sudpCmd = &cobra.Command{
Use: "sudp", Use: "sudp",
Short: "Run frpc with a single sudp proxy", Short: "Run frpc with a single sudp proxy",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) clientCfg, err := parseClientCommonCfgFromCmd()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)

View File

@@ -41,7 +41,7 @@ var tcpCmd = &cobra.Command{
Use: "tcp", Use: "tcp",
Short: "Run frpc with a single tcp proxy", Short: "Run frpc with a single tcp proxy",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) clientCfg, err := parseClientCommonCfgFromCmd()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)

View File

@@ -44,7 +44,7 @@ var tcpMuxCmd = &cobra.Command{
Use: "tcpmux", Use: "tcpmux",
Short: "Run frpc with a single tcpmux proxy", Short: "Run frpc with a single tcpmux proxy",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) clientCfg, err := parseClientCommonCfgFromCmd()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)

View File

@@ -18,10 +18,10 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/spf13/cobra"
"github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/consts" "github.com/fatedier/frp/pkg/consts"
"github.com/spf13/cobra"
) )
func init() { func init() {
@@ -41,7 +41,7 @@ var udpCmd = &cobra.Command{
Use: "udp", Use: "udp",
Short: "Run frpc with a single udp proxy", Short: "Run frpc with a single udp proxy",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) clientCfg, err := parseClientCommonCfgFromCmd()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)

43
cmd/frpc/sub/verify.go Normal file
View File

@@ -0,0 +1,43 @@
// Copyright 2021 The frp Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sub
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/fatedier/frp/pkg/config"
)
func init() {
rootCmd.AddCommand(verifyCmd)
}
var verifyCmd = &cobra.Command{
Use: "verify",
Short: "Verify that the configures is valid",
RunE: func(cmd *cobra.Command, args []string) error {
_, _, _, err := config.ParseClientConfig(cfgFile)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("frpc: the configuration file %s syntax is ok\n", cfgFile)
return nil
},
}

View File

@@ -18,10 +18,10 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/spf13/cobra"
"github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/consts" "github.com/fatedier/frp/pkg/consts"
"github.com/spf13/cobra"
) )
func init() { func init() {
@@ -45,7 +45,7 @@ var xtcpCmd = &cobra.Command{
Use: "xtcp", Use: "xtcp",
Short: "Run frpc with a single xtcp proxy", Short: "Run frpc with a single xtcp proxy",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, nil) clientCfg, err := parseClientCommonCfgFromCmd()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)

View File

@@ -20,7 +20,7 @@ import (
"github.com/fatedier/golib/crypto" "github.com/fatedier/golib/crypto"
_ "github.com/fatedier/frp/assets/frps/statik" _ "github.com/fatedier/frp/assets/frps"
_ "github.com/fatedier/frp/pkg/metrics" _ "github.com/fatedier/frp/pkg/metrics"
) )

View File

@@ -18,14 +18,14 @@ import (
"fmt" "fmt"
"os" "os"
"github.com/spf13/cobra"
"github.com/fatedier/frp/pkg/auth" "github.com/fatedier/frp/pkg/auth"
"github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/util/log" "github.com/fatedier/frp/pkg/util/log"
"github.com/fatedier/frp/pkg/util/util" "github.com/fatedier/frp/pkg/util/util"
"github.com/fatedier/frp/pkg/util/version" "github.com/fatedier/frp/pkg/util/version"
"github.com/fatedier/frp/server" "github.com/fatedier/frp/server"
"github.com/spf13/cobra"
) )
const ( const (
@@ -37,31 +37,31 @@ var (
cfgFile string cfgFile string
showVersion bool showVersion bool
bindAddr string bindAddr string
bindPort int bindPort int
bindUDPPort int bindUDPPort int
kcpBindPort int kcpBindPort int
proxyBindAddr string proxyBindAddr string
vhostHTTPPort int vhostHTTPPort int
vhostHTTPSPort int vhostHTTPSPort int
vhostHTTPTimeout int64 vhostHTTPTimeout int64
dashboardAddr string dashboardAddr string
dashboardPort int dashboardPort int
dashboardUser string dashboardUser string
dashboardPwd string dashboardPwd string
enablePrometheus bool enablePrometheus bool
assetsDir string logFile string
logFile string logLevel string
logLevel string logMaxDays int64
logMaxDays int64 disableLogColor bool
disableLogColor bool token string
token string subDomainHost string
subDomainHost string allowPorts string
tcpMux bool maxPortsPerClient int64
allowPorts string tlsOnly bool
maxPoolCount int64 dashboardTLSMode bool
maxPortsPerClient int64 dashboardTLSCertFile string
tlsOnly bool dashboardTLSKeyFile string
) )
func init() { func init() {
@@ -91,6 +91,9 @@ func init() {
rootCmd.PersistentFlags().StringVarP(&allowPorts, "allow_ports", "", "", "allow ports") rootCmd.PersistentFlags().StringVarP(&allowPorts, "allow_ports", "", "", "allow ports")
rootCmd.PersistentFlags().Int64VarP(&maxPortsPerClient, "max_ports_per_client", "", 0, "max ports per client") rootCmd.PersistentFlags().Int64VarP(&maxPortsPerClient, "max_ports_per_client", "", 0, "max ports per client")
rootCmd.PersistentFlags().BoolVarP(&tlsOnly, "tls_only", "", false, "frps tls only") rootCmd.PersistentFlags().BoolVarP(&tlsOnly, "tls_only", "", false, "frps tls only")
rootCmd.PersistentFlags().BoolVarP(&dashboardTLSMode, "dashboard_tls_mode", "", false, "dashboard tls mode")
rootCmd.PersistentFlags().StringVarP(&dashboardTLSCertFile, "dashboard_tls_cert_file", "", "", "dashboard tls cert file")
rootCmd.PersistentFlags().StringVarP(&dashboardTLSKeyFile, "dashboard_tls_key_file", "", "", "dashboard tls key file")
} }
var rootCmd = &cobra.Command{ var rootCmd = &cobra.Command{
@@ -145,7 +148,7 @@ func parseServerCommonCfg(fileType int, source []byte) (cfg config.ServerCommonC
cfg.Complete() cfg.Complete()
err = cfg.Validate() err = cfg.Validate()
if err != nil { if err != nil {
err = fmt.Errorf("Parse config error: %v", err) err = fmt.Errorf("parse config error: %v", err)
return return
} }
return return
@@ -167,6 +170,9 @@ func parseServerCommonCfgFromCmd() (cfg config.ServerCommonConf, err error) {
cfg.DashboardUser = dashboardUser cfg.DashboardUser = dashboardUser
cfg.DashboardPwd = dashboardPwd cfg.DashboardPwd = dashboardPwd
cfg.EnablePrometheus = enablePrometheus cfg.EnablePrometheus = enablePrometheus
cfg.DashboardTLSCertFile = dashboardTLSCertFile
cfg.DashboardTLSKeyFile = dashboardTLSKeyFile
cfg.DashboardTLSMode = dashboardTLSMode
cfg.LogFile = logFile cfg.LogFile = logFile
cfg.LogLevel = logLevel cfg.LogLevel = logLevel
cfg.LogMaxDays = logMaxDays cfg.LogMaxDays = logMaxDays
@@ -180,7 +186,7 @@ func parseServerCommonCfgFromCmd() (cfg config.ServerCommonConf, err error) {
// e.g. 1000-2000,2001,2002,3000-4000 // e.g. 1000-2000,2001,2002,3000-4000
ports, errRet := util.ParseRangeNumbers(allowPorts) ports, errRet := util.ParseRangeNumbers(allowPorts)
if errRet != nil { if errRet != nil {
err = fmt.Errorf("Parse conf error: allow_ports: %v", errRet) err = fmt.Errorf("parse conf error: allow_ports: %v", errRet)
return return
} }

53
cmd/frps/verify.go Normal file
View File

@@ -0,0 +1,53 @@
// Copyright 2021 The frp Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/fatedier/frp/pkg/config"
)
func init() {
rootCmd.AddCommand(verifyCmd)
}
var verifyCmd = &cobra.Command{
Use: "verify",
Short: "Verify that the configures is valid",
RunE: func(cmd *cobra.Command, args []string) error {
if cfgFile == "" {
fmt.Println("no config file is specified")
return nil
}
iniContent, err := config.GetRenderedConfFromFile(cfgFile)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
_, err = parseServerCommonCfg(CfgFileTypeIni, iniContent)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("frps: the configuration file %s syntax is ok\n", cfgFile)
return nil
},
}

View File

@@ -6,6 +6,13 @@
server_addr = 0.0.0.0 server_addr = 0.0.0.0
server_port = 7000 server_port = 7000
# The maximum amount of time a dial to server will wait for a connect to complete. Default value is 10 seconds.
# dial_server_timeout = 10
# dial_server_keepalive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
# If negative, keep-alive probes are disabled.
# dial_server_keepalive = 7200
# if you want to connect frps by http proxy or socks5 proxy or ntlm proxy, you can set http_proxy here or in global environment variables # if you want to connect frps by http proxy or socks5 proxy or ntlm proxy, you can set http_proxy here or in global environment variables
# it only works when protocol is tcp # it only works when protocol is tcp
# http_proxy = http://user:passwd@192.168.1.128:8080 # http_proxy = http://user:passwd@192.168.1.128:8080
@@ -48,6 +55,12 @@ oidc_audience =
# It will be used to get an OIDC token if AuthenticationMethod == "oidc". By default, this value is "". # It will be used to get an OIDC token if AuthenticationMethod == "oidc". By default, this value is "".
oidc_token_endpoint_url = oidc_token_endpoint_url =
# oidc_additional_xxx specifies additional parameters to be sent to the OIDC Token Endpoint.
# For example, if you want to specify the "audience" parameter, you can set as follow.
# frp will add "audience=<value>" "var1=<value>" to the additional parameters.
# oidc_additional_audience = https://dev.auth.com/api/v2/
# oidc_additional_var1 = foobar
# set admin address for control frpc's action by http api such as reload # set admin address for control frpc's action by http api such as reload
admin_addr = 127.0.0.1 admin_addr = 127.0.0.1
admin_port = 7400 admin_port = 7400
@@ -60,7 +73,11 @@ admin_pwd = admin
pool_count = 5 pool_count = 5
# if tcp stream multiplexing is used, default is true, it must be same with frps # if tcp stream multiplexing is used, default is true, it must be same with frps
tcp_mux = true # tcp_mux = true
# specify keep alive interval for tcp mux.
# only valid if tcp_mux is true.
# tcp_mux_keepalive_interval = 60
# your proxy name will be changed to {user}.{proxy} # your proxy name will be changed to {user}.{proxy}
user = your_name user = your_name
@@ -73,6 +90,10 @@ login_fail_exit = true
# now it supports tcp, kcp and websocket, default is tcp # now it supports tcp, kcp and websocket, default is tcp
protocol = tcp protocol = tcp
# set client binding ip when connect server, default is empty.
# only when protocol = tcp or websocket, the value will be used.
connect_server_local_ip = 0.0.0.0
# if tls_enable is true, frpc will connect frps by tls # if tls_enable is true, frpc will connect frps by tls
tls_enable = true tls_enable = true
@@ -84,12 +105,13 @@ tls_enable = true
# specify a dns server, so frpc will use this instead of default one # specify a dns server, so frpc will use this instead of default one
# dns_server = 8.8.8.8 # dns_server = 8.8.8.8
# proxy names you want to start seperated by ',' # proxy names you want to start separated by ','
# default is empty, means all proxies # default is empty, means all proxies
# start = ssh,dns # start = ssh,dns
# heartbeat configure, it's not recommended to modify the default value # heartbeat configure, it's not recommended to modify the default value
# the default value of heartbeat_interval is 10 and heartbeat_timeout is 90 # The default value of heartbeat_interval is 10 and heartbeat_timeout is 90. Set negative value
# to disable it.
# heartbeat_interval = 30 # heartbeat_interval = 30
# heartbeat_timeout = 90 # heartbeat_timeout = 90
@@ -102,6 +124,17 @@ meta_var2 = 234
# It affects the udp and sudp proxy. # It affects the udp and sudp proxy.
udp_packet_size = 1500 udp_packet_size = 1500
# include other config files for proxies.
# includes = ./confd/*.ini
# By default, frpc will connect frps with first custom byte if tls is enabled.
# If DisableCustomTLSFirstByte is true, frpc will not send that custom byte.
disable_custom_tls_first_byte = false
# Enable golang pprof handlers in admin listener.
# Admin port must be set first.
pprof_enable = false
# 'ssh' is the unique proxy name # 'ssh' is the unique proxy name
# if user in [common] section is not empty, it will be changed to {user}.{proxy} such as 'your_name.ssh' # if user in [common] section is not empty, it will be changed to {user}.{proxy} such as 'your_name.ssh'
[ssh] [ssh]
@@ -178,11 +211,13 @@ use_compression = true
# if not set, you can access this custom_domains without certification # if not set, you can access this custom_domains without certification
http_user = admin http_user = admin
http_pwd = admin http_pwd = admin
# if domain for frps is frps.com, then you can access [web01] proxy by URL http://test.frps.com # if domain for frps is frps.com, then you can access [web01] proxy by URL http://web01.frps.com
subdomain = web01 subdomain = web01
custom_domains = web02.yourdomain.com custom_domains = web01.yourdomain.com
# locations is only available for http type # locations is only available for http type
locations = /,/pic locations = /,/pic
# route requests to this service if http basic auto user is abc
# route_by_http_user = abc
host_header_rewrite = example.com host_header_rewrite = example.com
# params with prefix "header_" will be used to update http request headers # params with prefix "header_" will be used to update http request headers
header_X-From-Where = frp header_X-From-Where = frp
@@ -315,3 +350,4 @@ multiplexer = httpconnect
local_ip = 127.0.0.1 local_ip = 127.0.0.1
local_port = 10701 local_port = 10701
custom_domains = tunnel1 custom_domains = tunnel1
# route_by_http_user = user1

View File

@@ -30,16 +30,24 @@ vhost_https_port = 443
# HTTP CONNECT requests. By default, this value is 0. # HTTP CONNECT requests. By default, this value is 0.
# tcpmux_httpconnect_port = 1337 # tcpmux_httpconnect_port = 1337
# If tcpmux_passthrough is true, frps won't do any update on traffic.
# tcpmux_passthrough = false
# set dashboard_addr and dashboard_port to view dashboard of frps # set dashboard_addr and dashboard_port to view dashboard of frps
# dashboard_addr's default value is same with bind_addr # dashboard_addr's default value is same with bind_addr
# dashboard is available only if dashboard_port is set # dashboard is available only if dashboard_port is set
dashboard_addr = 0.0.0.0 dashboard_addr = 0.0.0.0
dashboard_port = 7500 dashboard_port = 7500
# dashboard user and passwd for basic auth protect, if not set, both default value is admin # dashboard user and passwd for basic auth protect
dashboard_user = admin dashboard_user = admin
dashboard_pwd = admin dashboard_pwd = admin
# dashboard TLS mode
dashboard_tls_mode = false
# dashboard_tls_cert_file = server.crt
# dashboard_tls_key_file = server.key
# enable_prometheus will export prometheus metrics on {dashboard_addr}:{dashboard_port} in /metrics api. # enable_prometheus will export prometheus metrics on {dashboard_addr}:{dashboard_port} in /metrics api.
enable_prometheus = true enable_prometheus = true
@@ -86,13 +94,12 @@ oidc_audience =
# By default, this value is false. # By default, this value is false.
oidc_skip_expiry_check = false oidc_skip_expiry_check = false
# oidc_skip_issuer_check specifies whether to skip checking if the OIDC token's issuer claim matches the issuer specified in OidcIssuer. # oidc_skip_issuer_check specifies whether to skip checking if the OIDC token's issuer claim matches the issuer specified in OidcIssuer.
# By default, this value is false. # By default, this value is false.
oidc_skip_issuer_check = false oidc_skip_issuer_check = false
# heartbeat configure, it's not recommended to modify the default value # heartbeat configure, it's not recommended to modify the default value
# the default value of heartbeat_timeout is 90 # the default value of heartbeat_timeout is 90. Set negative value to disable it.
# heartbeat_timeout = 90 # heartbeat_timeout = 90
# user_conn_timeout configure, it's not recommended to modify the default value # user_conn_timeout configure, it's not recommended to modify the default value
@@ -120,7 +127,15 @@ tls_only = false
subdomain_host = frps.com subdomain_host = frps.com
# if tcp stream multiplexing is used, default is true # if tcp stream multiplexing is used, default is true
tcp_mux = true # tcp_mux = true
# specify keep alive interval for tcp mux.
# only valid if tcp_mux is true.
# tcp_mux_keepalive_interval = 60
# tcp_keepalive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
# If negative, keep-alive probes are disabled.
# tcp_keepalive = 7200
# custom 404 page for HTTP requests # custom 404 page for HTTP requests
# custom_404_page = /path/to/404.html # custom_404_page = /path/to/404.html
@@ -130,6 +145,10 @@ tcp_mux = true
# It affects the udp and sudp proxy. # It affects the udp and sudp proxy.
udp_packet_size = 1500 udp_packet_size = 1500
# Enable golang pprof handlers in dashboard listener.
# Dashboard port must be set first
pprof_enable = false
[plugin.user-manager] [plugin.user-manager]
addr = 127.0.0.1:9000 addr = 127.0.0.1:9000
path = /handler path = /handler

View File

@@ -1,14 +0,0 @@
[Unit]
Description=Frp Client Service
After=network.target
[Service]
Type=simple
User=nobody
Restart=on-failure
RestartSec=5s
ExecStart=/usr/bin/frpc -c /etc/frp/frpc.ini
ExecReload=/usr/bin/frpc reload -c /etc/frp/frpc.ini
[Install]
WantedBy=multi-user.target

View File

@@ -1,14 +0,0 @@
[Unit]
Description=Frp Client Service
After=network.target
[Service]
Type=idle
User=nobody
Restart=on-failure
RestartSec=5s
ExecStart=/usr/bin/frpc -c /etc/frp/%i.ini
ExecReload=/usr/bin/frpc reload -c /etc/frp/%i.ini
[Install]
WantedBy=multi-user.target

View File

@@ -1,13 +0,0 @@
[Unit]
Description=Frp Server Service
After=network.target
[Service]
Type=simple
User=nobody
Restart=on-failure
RestartSec=5s
ExecStart=/usr/bin/frps -c /etc/frp/frps.ini
[Install]
WantedBy=multi-user.target

View File

@@ -1,13 +0,0 @@
[Unit]
Description=Frp Server Service
After=network.target
[Service]
Type=simple
User=nobody
Restart=on-failure
RestartSec=5s
ExecStart=/usr/bin/frps -c /etc/frp/%i.ini
[Install]
WantedBy=multi-user.target

BIN
doc/pic/sponsor_doppler.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
doc/pic/sponsor_workos.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -70,7 +70,7 @@ The response can look like any of the following:
### Operation ### Operation
Currently `Login`, `NewProxy`, `Ping`, `NewWorkConn` and `NewUserConn` operations are supported. Currently `Login`, `NewProxy`, `CloseProxy`, `Ping`, `NewWorkConn` and `NewUserConn` operations are supported.
#### Login #### Login
@@ -88,7 +88,8 @@ Client login operation
"privilege_key": <string>, "privilege_key": <string>,
"run_id": <string>, "run_id": <string>,
"pool_count": <int>, "pool_count": <int>,
"metas": map<string>string "metas": map<string>string,
"client_address": <string>
} }
} }
``` ```
@@ -135,6 +136,26 @@ Create new proxy
} }
``` ```
#### CloseProxy
A previously created proxy is closed.
Please note that one request will be sent for every proxy that is closed, do **NOT** use this
if you have too many proxies bound to a single client, as this may exhaust the server's resources.
```
{
"content": {
"user": {
"user": <string>,
"metas": map<string>string
"run_id": <string>
},
"proxy_name": <string>
}
}
```
#### Ping #### Ping
Heartbeat from frpc Heartbeat from frpc

View File

@@ -1,228 +0,0 @@
### 服务端管理插件
frp 管理插件的作用是在不侵入自身代码的前提下,扩展 frp 服务端的能力。
frp 管理插件会以单独进程的形式运行,并且监听在一个端口上,对外提供 RPC 接口,响应 frps 的请求。
frps 在执行某些操作前,会根据配置向管理插件发送 RPC 请求,根据管理插件的响应来执行相应的操作。
### RPC 请求
管理插件接收到操作请求后,可以给出三种回应。
* 拒绝操作,需要返回拒绝操作的原因。
* 允许操作,不需要修改操作内容。
* 允许操作,对操作请求进行修改后,返回修改后的内容。
### 接口
接口路径可以在 frps 配置中为每个插件单独配置,这里以 `/handler` 为例。
Request
```
POST /handler
{
"version": "0.1.0",
"op": "Login",
"content": {
... // 具体的操作信息
}
}
请求 Header
X-Frp-Reqid: 用于追踪请求
```
Response
非 200 的返回都认为是请求异常。
拒绝执行操作
```
{
"reject": true,
"reject_reason": "invalid user"
}
```
允许且内容不需要变动
```
{
"reject": false,
"unchange": true
}
```
允许且需要替换操作内容
```
{
"unchange": "false",
"content": {
... // 替换后的操作信息,格式必须和请求时的一致
}
}
```
### 操作类型
目前插件支持管理的操作类型有 `Login``NewProxy``Ping``NewWorkConn``NewUserConn`
#### Login
用户登录操作信息
```
{
"content": {
"version": <string>,
"hostname": <string>,
"os": <string>,
"arch": <string>,
"user": <string>,
"timestamp": <int64>,
"privilege_key": <string>,
"run_id": <string>,
"pool_count": <int>,
"metas": map<string>string
}
}
```
#### NewProxy
创建代理的相关信息
```
{
"content": {
"user": {
"user": <string>,
"metas": map<string>string
},
"proxy_name": <string>,
"proxy_type": <string>,
"use_encryption": <bool>,
"use_compression": <bool>,
"group": <string>,
"group_key": <string>,
// tcp and udp only
"remote_port": <int>,
// http and https only
"custom_domains": []<string>,
"subdomain": <string>,
"locations": <string>,
"http_user": <string>,
"http_pwd": <string>,
"host_header_rewrite": <string>,
"headers": map<string>string,
"metas": map<string>string
}
}
```
#### Ping
心跳相关信息
```
{
"content": {
"user": {
"user": <string>,
"metas": map<string>string
"run_id": <string>
},
"timestamp": <int64>,
"privilege_key": <string>
}
}
```
#### NewWorkConn
新增 `frpc` 连接相关信息
```
{
"content": {
"user": {
"user": <string>,
"metas": map<string>string
"run_id": <string>
},
"run_id": <string>
"timestamp": <int64>,
"privilege_key": <string>
}
}
```
#### NewUserConn
新增 `proxy` 连接相关信息 (支持 `tcp``stcp``https``tcpmux` 协议)。
```
{
"content": {
"user": {
"user": <string>,
"metas": map<string>string
"run_id": <string>
},
"proxy_name": <string>,
"proxy_type": <string>,
"remote_addr": <string>
}
}
```
### frps 中插件配置
```ini
[common]
bind_port = 7000
[plugin.user-manager]
addr = 127.0.0.1:9000
path = /handler
ops = Login
[plugin.port-manager]
addr = 127.0.0.1:9001
path = /handler
ops = NewProxy
```
addr: 插件监听的网络地址。
path: 插件监听的 HTTP 请求路径。
ops: 插件需要处理的操作列表,多个 op 以英文逗号分隔。
### 元数据
为了减少 frps 的代码修改,同时提高管理插件的扩展能力,在 frpc 的配置文件中引入自定义元数据的概念。元数据会在调用 RPC 请求时发送给插件。
元数据以 `meta_` 开头,可以配置多个,元数据分为两种,一种配置在 `common` 下,一种配置在各个 proxy 中。
```
# frpc.ini
[common]
server_addr = 127.0.0.1
server_port = 7000
user = fake
meta_token = fake
meta_version = 1.0.0
[ssh]
type = tcp
local_port = 22
remote_port = 6000
meta_id = 123
```

View File

@@ -1,14 +1,12 @@
FROM alpine:3.12.0 AS temp FROM golang:1.19 AS building
COPY bin/frpc /tmp COPY . /building
WORKDIR /building
RUN chmod -R 777 /tmp/frpc RUN make frpc
FROM alpine:3
FROM alpine:3.12.0 COPY --from=building /building/bin/frpc /usr/bin/frpc
WORKDIR /app
COPY --from=temp /tmp/frpc /usr/bin
ENTRYPOINT ["/usr/bin/frpc"] ENTRYPOINT ["/usr/bin/frpc"]

View File

@@ -1,14 +1,12 @@
FROM alpine:3.12.0 AS temp FROM golang:1.19 AS building
COPY bin/frps /tmp COPY . /building
WORKDIR /building
RUN chmod -R 777 /tmp/frps RUN make frps
FROM alpine:3
FROM alpine:3.12.0 COPY --from=building /building/bin/frps /usr/bin/frps
WORKDIR /app
COPY --from=temp /tmp/frps /usr/bin
ENTRYPOINT ["/usr/bin/frps"] ENTRYPOINT ["/usr/bin/frps"]

89
go.mod
View File

@@ -1,40 +1,67 @@
module github.com/fatedier/frp module github.com/fatedier/frp
go 1.16 go 1.18
require ( require (
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/coreos/go-oidc v2.2.1+incompatible github.com/coreos/go-oidc v2.2.1+incompatible
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb
github.com/fatedier/golib v0.1.1-0.20200901083111-1f870741e185 github.com/fatedier/golib v0.1.1-0.20220321042308-c306138b83ac
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible
github.com/google/uuid v1.1.1 github.com/go-playground/validator/v10 v10.11.0
github.com/gorilla/mux v1.7.3 github.com/google/uuid v1.3.0
github.com/gorilla/websocket v1.4.0 github.com/gorilla/mux v1.8.0
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d github.com/gorilla/websocket v1.5.0
github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/hashicorp/yamux v0.1.1
github.com/klauspost/cpuid v1.2.0 // indirect github.com/onsi/ginkgo v1.16.4
github.com/klauspost/reedsolomon v1.9.1 // indirect github.com/onsi/gomega v1.20.2
github.com/mattn/go-runewidth v0.0.4 // indirect github.com/pires/go-proxyproto v0.6.2
github.com/onsi/ginkgo v1.12.3 github.com/prometheus/client_golang v1.13.0
github.com/onsi/gomega v1.10.1 github.com/rodaine/table v1.0.1
github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc github.com/spf13/cobra v1.1.3
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/stretchr/testify v1.7.0
github.com/prometheus/client_golang v1.4.1 golang.org/x/net v0.0.0-20220722155237-a158d28d115b
github.com/rakyll/statik v0.1.1 golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b
github.com/rodaine/table v1.0.0 golang.org/x/time v0.0.0-20220210224613-90d013bbcef8
github.com/smartystreets/goconvey v1.6.4 // indirect gopkg.in/ini.v1 v1.67.0
github.com/spf13/cobra v0.0.3 k8s.io/apimachinery v0.25.0
github.com/stretchr/testify v1.4.0 k8s.io/client-go v0.25.0
github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047 // indirect )
github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554 // indirect
github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 // indirect require (
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae // indirect github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 github.com/beorn7/perks v1.0.1 // indirect
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d github.com/cespare/xxhash/v2 v2.1.2 // indirect
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 github.com/fsnotify/fsnotify v1.4.9 // indirect
gopkg.in/ini.v1 v1.62.0 github.com/go-playground/locales v0.14.0 // indirect
gopkg.in/square/go-jose.v2 v2.4.1 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect
k8s.io/apimachinery v0.18.3 github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
github.com/klauspost/reedsolomon v1.9.15 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/square/go-jose.v2 v2.4.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
) )

685
go.sum
View File

@@ -1,269 +1,718 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk=
github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb h1:wCrNShQidLmvVWn/0PikGmpdP0vtQmnvyRg3ZBEhczw= github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb h1:wCrNShQidLmvVWn/0PikGmpdP0vtQmnvyRg3ZBEhczw=
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk= github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk=
github.com/fatedier/golib v0.1.1-0.20200901083111-1f870741e185 h1:2p4W5xYizIYwhiGQgeHOQcRD2O84j0tjD40P6gUCRrk= github.com/fatedier/golib v0.1.1-0.20220321042308-c306138b83ac h1:td1FJwN/oz8+9GldeEm3YdBX0Husc0FSPywLesZxi4w=
github.com/fatedier/golib v0.1.1-0.20200901083111-1f870741e185/go.mod h1:MUs+IH/MGJNz5Cj2JVJBPZBKw2exON7LzO3HrJHmGiQ= github.com/fatedier/golib v0.1.1-0.20220321042308-c306138b83ac/go.mod h1:fLV0TLwHqrnB/L3jbNl67Gn6PCLggDGHniX1wLrA2Qo=
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible h1:ssXat9YXFvigNge/IkkZvFMn8yeYKFX+uI6wn2mLJ74= github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible h1:ssXat9YXFvigNge/IkkZvFMn8yeYKFX+uI6wn2mLJ74=
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s= github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/googleapis/gnostic v0.1.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/reedsolomon v1.9.1 h1:kYrT1MlR4JH6PqOpC+okdb9CDTcwEC/BqpzK4WFyXL8= github.com/klauspost/reedsolomon v1.9.15 h1:g2erWKD2M6rgnPf89fCji6jNlhMKMdXcuNHMW1SYCIo=
github.com/klauspost/reedsolomon v1.9.1/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= github.com/klauspost/reedsolomon v1.9.15/go.mod h1:eqPAcE7xar5CIzcdfwydOEdcmchAKAP/qs14y4GCBOk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.12.3 h1:+RYp9QczoWz9zfUyLP/5SLXQVhfr6gZOoKGfQqHuLZQ= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.12.3/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc h1:lNOt1SMsgHXTdpuGw+RpnJtzUcCb/oRKZP65pBy9pr8= github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY=
github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc/go.mod h1:6/gX3+E/IYGa0wMORlSMla999awQFdbaeQCHjSMKIzY= github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.1 h1:FFSuS004yOQEtDdTq+TAOLP5xUq63KqAFYyOi8zA+Y8= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rakyll/statik v0.1.1 h1:fCLHsIMajHqD5RKigbFXpvX3dN7c80Pm12+NCrI3kvg= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rakyll/statik v0.1.1/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/rodaine/table v1.0.0 h1:UaCJG5Axc/cNXVGXqnCrffm1KxP0OfYLe1HuJLf5sFY= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/rodaine/table v1.0.0/go.mod h1:YAUzwPOji0DUJNEvggdxyQcUAl4g3hDRcFlyjnnR51I= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rodaine/table v1.0.1 h1:U/VwCnUxlVYxw8+NJiLIuCxA/xa6jL38MY3FYysVWWQ=
github.com/rodaine/table v1.0.1/go.mod h1:UVEtfBsflpeEcD56nF4F5AocNFta0ZuolpSVdPtlmP4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047 h1:K+jtWCOuZgCra7eXZ/VWn2FbJmrA/D058mTXhh2rq+8= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554 h1:pexgSe+JCFuxG+uoMZLO+ce8KHtdHGhst4cs6rw3gmk= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 h1:6CNSDqI1wiE+JqyOy5Qt/yo/DoNI2/QmmOZeiCid2Nw= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI=
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44=
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y= gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y=
gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
k8s.io/apimachinery v0.18.3 h1:pOGcbVAhxADgUYnjS08EFXs9QMl8qaH5U4fr5LGUrSk= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C9bd8UpWHZ5plfAS9fzPjJuQ6JL3E= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0-20200116222232-67a7b8c61874/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sigs.k8s.io/structured-merge-diff/v3 v3.0.0/go.mod h1:PlARxl6Hbt/+BC80dRLi1qAmnMqwqDg62YvvVkZjemw= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU=
k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0=
k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E=
k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4=
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@@ -5,11 +5,16 @@ ROOT=$(unset CDPATH && cd $(dirname "${BASH_SOURCE[0]}")/.. && pwd)
which ginkgo &> /dev/null which ginkgo &> /dev/null
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "ginkgo not found, try to install..." echo "ginkgo not found, try to install..."
go install github.com/onsi/ginkgo/ginkgo go install github.com/onsi/ginkgo/ginkgo@v1.16.5
fi fi
debug=false debug=false
if [ x${DEBUG} == x"true" ]; then if [ x${DEBUG} == x"true" ]; then
debug=true debug=true
fi fi
ginkgo -nodes=4 ${ROOT}/test/e2e -- -frpc-path=${ROOT}/bin/frpc -frps-path=${ROOT}/bin/frps -log-level=debug -debug=${debug} logLevel=debug
if [ x${LOG_LEVEL} != x"" ]; then
logLevel=${LOG_LEVEL}
fi
ginkgo -nodes=8 -slowSpecThreshold=20 ${ROOT}/test/e2e -- -frpc-path=${ROOT}/bin/frpc -frps-path=${ROOT}/bin/frps -log-level=${logLevel} -debug=${debug}

View File

@@ -15,7 +15,7 @@ rm -rf ./release/packages
mkdir -p ./release/packages mkdir -p ./release/packages
os_all='linux windows darwin freebsd' os_all='linux windows darwin freebsd'
arch_all='386 amd64 arm arm64 mips64 mips64le mips mipsle' arch_all='386 amd64 arm arm64 mips64 mips64le mips mipsle riscv64'
cd ./release cd ./release

View File

@@ -18,10 +18,10 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/fatedier/frp/pkg/msg"
"github.com/coreos/go-oidc" "github.com/coreos/go-oidc"
"golang.org/x/oauth2/clientcredentials" "golang.org/x/oauth2/clientcredentials"
"github.com/fatedier/frp/pkg/msg"
) )
type OidcClientConfig struct { type OidcClientConfig struct {
@@ -34,20 +34,26 @@ type OidcClientConfig struct {
// is "". // is "".
OidcClientSecret string `ini:"oidc_client_secret" json:"oidc_client_secret"` OidcClientSecret string `ini:"oidc_client_secret" json:"oidc_client_secret"`
// OidcAudience specifies the audience of the token in OIDC authentication // OidcAudience specifies the audience of the token in OIDC authentication
//if AuthenticationMethod == "oidc". By default, this value is "". // if AuthenticationMethod == "oidc". By default, this value is "".
OidcAudience string `ini:"oidc_audience" json:"oidc_audience"` OidcAudience string `ini:"oidc_audience" json:"oidc_audience"`
// OidcTokenEndpointURL specifies the URL which implements OIDC Token Endpoint. // OidcTokenEndpointURL specifies the URL which implements OIDC Token Endpoint.
// It will be used to get an OIDC token if AuthenticationMethod == "oidc". // It will be used to get an OIDC token if AuthenticationMethod == "oidc".
// By default, this value is "". // By default, this value is "".
OidcTokenEndpointURL string `ini:"oidc_token_endpoint_url" json:"oidc_token_endpoint_url"` OidcTokenEndpointURL string `ini:"oidc_token_endpoint_url" json:"oidc_token_endpoint_url"`
// OidcAdditionalEndpointParams specifies additional parameters to be sent
// this field will be transfer to map[string][]string in OIDC token generator
// The field will be set by prefix "oidc_additional_"
OidcAdditionalEndpointParams map[string]string `ini:"-" json:"oidc_additional_endpoint_params"`
} }
func getDefaultOidcClientConf() OidcClientConfig { func getDefaultOidcClientConf() OidcClientConfig {
return OidcClientConfig{ return OidcClientConfig{
OidcClientID: "", OidcClientID: "",
OidcClientSecret: "", OidcClientSecret: "",
OidcAudience: "", OidcAudience: "",
OidcTokenEndpointURL: "", OidcTokenEndpointURL: "",
OidcAdditionalEndpointParams: make(map[string]string),
} }
} }
@@ -88,11 +94,17 @@ type OidcAuthProvider struct {
} }
func NewOidcAuthSetter(baseCfg BaseConfig, cfg OidcClientConfig) *OidcAuthProvider { func NewOidcAuthSetter(baseCfg BaseConfig, cfg OidcClientConfig) *OidcAuthProvider {
eps := make(map[string][]string)
for k, v := range cfg.OidcAdditionalEndpointParams {
eps[k] = []string{v}
}
tokenGenerator := &clientcredentials.Config{ tokenGenerator := &clientcredentials.Config{
ClientID: cfg.OidcClientID, ClientID: cfg.OidcClientID,
ClientSecret: cfg.OidcClientSecret, ClientSecret: cfg.OidcClientSecret,
Scopes: []string{cfg.OidcAudience}, Scopes: []string{cfg.OidcAudience},
TokenURL: cfg.OidcTokenEndpointURL, TokenURL: cfg.OidcTokenEndpointURL,
EndpointParams: eps,
} }
return &OidcAuthProvider{ return &OidcAuthProvider{

View File

@@ -17,12 +17,13 @@ package config
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath"
"strings" "strings"
"gopkg.in/ini.v1"
"github.com/fatedier/frp/pkg/auth" "github.com/fatedier/frp/pkg/auth"
"github.com/fatedier/frp/pkg/util/util" "github.com/fatedier/frp/pkg/util/util"
"gopkg.in/ini.v1"
) )
// ClientCommonConf contains information for a client service. It is // ClientCommonConf contains information for a client service. It is
@@ -33,10 +34,19 @@ type ClientCommonConf struct {
// ServerAddr specifies the address of the server to connect to. By // ServerAddr specifies the address of the server to connect to. By
// default, this value is "0.0.0.0". // default, this value is "0.0.0.0".
ServerAddr string `ini:"server_addr" josn:"server_addr"` ServerAddr string `ini:"server_addr" json:"server_addr"`
// ServerPort specifies the port to connect to the server on. By default, // ServerPort specifies the port to connect to the server on. By default,
// this value is 7000. // this value is 7000.
ServerPort int `ini:"server_port" json:"server_port"` ServerPort int `ini:"server_port" json:"server_port"`
// The maximum amount of time a dial to server will wait for a connect to complete.
DialServerTimeout int64 `ini:"dial_server_timeout" json:"dial_server_timeout"`
// DialServerKeepAlive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
// If negative, keep-alive probes are disabled.
DialServerKeepAlive int64 `ini:"dial_server_keepalive" json:"dial_server_keepalive"`
// ConnectServerLocalIP specifies the address of the client bind when it connect to server.
// By default, this value is empty.
// this value only use in TCP/Websocket protocol. Not support in KCP protocol.
ConnectServerLocalIP string `ini:"connect_server_local_ip" json:"connect_server_local_ip"`
// HTTPProxy specifies a proxy address to connect to the server through. If // HTTPProxy specifies a proxy address to connect to the server through. If
// this value is "", the server will be connected to directly. By default, // this value is "", the server will be connected to directly. By default,
// this value is read from the "http_proxy" environment variable. // this value is read from the "http_proxy" environment variable.
@@ -68,10 +78,10 @@ type ClientCommonConf struct {
// is 0. // is 0.
AdminPort int `ini:"admin_port" json:"admin_port"` AdminPort int `ini:"admin_port" json:"admin_port"`
// AdminUser specifies the username that the admin server will use for // AdminUser specifies the username that the admin server will use for
// login. By default, this value is "admin". // login.
AdminUser string `ini:"admin_user" json:"admin_user"` AdminUser string `ini:"admin_user" json:"admin_user"`
// AdminPwd specifies the password that the admin server will use for // AdminPwd specifies the password that the admin server will use for
// login. By default, this value is "admin". // login.
AdminPwd string `ini:"admin_pwd" json:"admin_pwd"` AdminPwd string `ini:"admin_pwd" json:"admin_pwd"`
// AssetsDir specifies the local directory that the admin server will load // AssetsDir specifies the local directory that the admin server will load
// resources from. If this value is "", assets will be loaded from the // resources from. If this value is "", assets will be loaded from the
@@ -85,6 +95,9 @@ type ClientCommonConf struct {
// the server must have TCP multiplexing enabled as well. By default, this // the server must have TCP multiplexing enabled as well. By default, this
// value is true. // value is true.
TCPMux bool `ini:"tcp_mux" json:"tcp_mux"` TCPMux bool `ini:"tcp_mux" json:"tcp_mux"`
// TCPMuxKeepaliveInterval specifies the keep alive interval for TCP stream multipler.
// If TCPMux is true, heartbeat of application layer is unnecessary because it can only rely on heartbeat in TCPMux.
TCPMuxKeepaliveInterval int64 `ini:"tcp_mux_keepalive_interval" json:"tcp_mux_keepalive_interval"`
// User specifies a prefix for proxy names to distinguish them from other // User specifies a prefix for proxy names to distinguish them from other
// clients. If this value is not "", proxy names will automatically be // clients. If this value is not "", proxy names will automatically be
// changed to "{user}.{proxy_name}". By default, this value is "". // changed to "{user}.{proxy_name}". By default, this value is "".
@@ -100,7 +113,7 @@ type ClientCommonConf struct {
// all supplied proxies are enabled. By default, this value is an empty // all supplied proxies are enabled. By default, this value is an empty
// set. // set.
Start []string `ini:"start" json:"start"` Start []string `ini:"start" json:"start"`
//Start map[string]struct{} `json:"start"` // Start map[string]struct{} `json:"start"`
// Protocol specifies the protocol to use when interacting with the server. // Protocol specifies the protocol to use when interacting with the server.
// Valid values are "tcp", "kcp" and "websocket". By default, this value // Valid values are "tcp", "kcp" and "websocket". By default, this value
// is "tcp". // is "tcp".
@@ -120,56 +133,69 @@ type ClientCommonConf struct {
// It only works when "tls_enable" is valid and tls configuration of server // It only works when "tls_enable" is valid and tls configuration of server
// has been specified. // has been specified.
TLSTrustedCaFile string `ini:"tls_trusted_ca_file" json:"tls_trusted_ca_file"` TLSTrustedCaFile string `ini:"tls_trusted_ca_file" json:"tls_trusted_ca_file"`
// TLSServerName specifices the custom server name of tls certificate. By // TLSServerName specifies the custom server name of tls certificate. By
// default, server name if same to ServerAddr. // default, server name if same to ServerAddr.
TLSServerName string `ini:"tls_server_name" json:"tls_server_name"` TLSServerName string `ini:"tls_server_name" json:"tls_server_name"`
// By default, frpc will connect frps with first custom byte if tls is enabled.
// If DisableCustomTLSFirstByte is true, frpc will not send that custom byte.
DisableCustomTLSFirstByte bool `ini:"disable_custom_tls_first_byte" json:"disable_custom_tls_first_byte"`
// HeartBeatInterval specifies at what interval heartbeats are sent to the // HeartBeatInterval specifies at what interval heartbeats are sent to the
// server, in seconds. It is not recommended to change this value. By // server, in seconds. It is not recommended to change this value. By
// default, this value is 30. // default, this value is 30. Set negative value to disable it.
HeartbeatInterval int64 `ini:"heartbeat_interval" json:"heartbeat_interval"` HeartbeatInterval int64 `ini:"heartbeat_interval" json:"heartbeat_interval"`
// HeartBeatTimeout specifies the maximum allowed heartbeat response delay // HeartBeatTimeout specifies the maximum allowed heartbeat response delay
// before the connection is terminated, in seconds. It is not recommended // before the connection is terminated, in seconds. It is not recommended
// to change this value. By default, this value is 90. // to change this value. By default, this value is 90. Set negative value to disable it.
HeartbeatTimeout int64 `ini:"heartbeat_timeout" json:"heartbeat_timeout"` HeartbeatTimeout int64 `ini:"heartbeat_timeout" json:"heartbeat_timeout"`
// Client meta info // Client meta info
Metas map[string]string `ini:"-" json:"metas"` Metas map[string]string `ini:"-" json:"metas"`
// UDPPacketSize specifies the udp packet size // UDPPacketSize specifies the udp packet size
// By default, this value is 1500 // By default, this value is 1500
UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"` UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"`
// Include other config files for proxies.
IncludeConfigFiles []string `ini:"includes" json:"includes"`
// Enable golang pprof handlers in admin listener.
// Admin port must be set first.
PprofEnable bool `ini:"pprof_enable" json:"pprof_enable"`
} }
// GetDefaultClientConf returns a client configuration with default values. // GetDefaultClientConf returns a client configuration with default values.
func GetDefaultClientConf() ClientCommonConf { func GetDefaultClientConf() ClientCommonConf {
return ClientCommonConf{ return ClientCommonConf{
ClientConfig: auth.GetDefaultClientConf(), ClientConfig: auth.GetDefaultClientConf(),
ServerAddr: "0.0.0.0", ServerAddr: "0.0.0.0",
ServerPort: 7000, ServerPort: 7000,
HTTPProxy: os.Getenv("http_proxy"), DialServerTimeout: 10,
LogFile: "console", DialServerKeepAlive: 7200,
LogWay: "console", HTTPProxy: os.Getenv("http_proxy"),
LogLevel: "info", LogFile: "console",
LogMaxDays: 3, LogWay: "console",
DisableLogColor: false, LogLevel: "info",
AdminAddr: "127.0.0.1", LogMaxDays: 3,
AdminPort: 0, DisableLogColor: false,
AdminUser: "", AdminAddr: "127.0.0.1",
AdminPwd: "", AdminPort: 0,
AssetsDir: "", AdminUser: "",
PoolCount: 1, AdminPwd: "",
TCPMux: true, AssetsDir: "",
User: "", PoolCount: 1,
DNSServer: "", TCPMux: true,
LoginFailExit: true, TCPMuxKeepaliveInterval: 60,
Start: make([]string, 0), User: "",
Protocol: "tcp", DNSServer: "",
TLSEnable: false, LoginFailExit: true,
TLSCertFile: "", Start: make([]string, 0),
TLSKeyFile: "", Protocol: "tcp",
TLSTrustedCaFile: "", TLSEnable: false,
HeartbeatInterval: 30, TLSCertFile: "",
HeartbeatTimeout: 90, TLSKeyFile: "",
Metas: make(map[string]string), TLSTrustedCaFile: "",
UDPPacketSize: 1500, HeartbeatInterval: 30,
HeartbeatTimeout: 90,
Metas: make(map[string]string),
UDPPacketSize: 1500,
IncludeConfigFiles: make([]string, 0),
PprofEnable: false,
} }
} }
@@ -182,15 +208,13 @@ func (cfg *ClientCommonConf) Complete() {
} }
func (cfg *ClientCommonConf) Validate() error { func (cfg *ClientCommonConf) Validate() error {
if cfg.HeartbeatInterval <= 0 { if cfg.HeartbeatTimeout > 0 && cfg.HeartbeatInterval > 0 {
return fmt.Errorf("invalid heartbeat_interval") if cfg.HeartbeatTimeout < cfg.HeartbeatInterval {
return fmt.Errorf("invalid heartbeat_timeout, heartbeat_timeout is less than heartbeat_interval")
}
} }
if cfg.HeartbeatTimeout < cfg.HeartbeatInterval { if !cfg.TLSEnable {
return fmt.Errorf("invalid heartbeat_timeout, heartbeat_timeout is less than heartbeat_interval")
}
if cfg.TLSEnable == false {
if cfg.TLSCertFile != "" { if cfg.TLSCertFile != "" {
fmt.Println("WARNING! tls_cert_file is invalid when tls_enable is false") fmt.Println("WARNING! tls_cert_file is invalid when tls_enable is false")
} }
@@ -208,6 +232,15 @@ func (cfg *ClientCommonConf) Validate() error {
return fmt.Errorf("invalid protocol") return fmt.Errorf("invalid protocol")
} }
for _, f := range cfg.IncludeConfigFiles {
absDir, err := filepath.Abs(filepath.Dir(f))
if err != nil {
return fmt.Errorf("include: parse directory of %s failed: %v", f, absDir)
}
if _, err := os.Stat(absDir); os.IsNotExist(err) {
return fmt.Errorf("include: directory of %s not exist", f)
}
}
return nil return nil
} }
@@ -236,6 +269,7 @@ func UnmarshalClientConfFromIni(source interface{}) (ClientCommonConf, error) {
} }
common.Metas = GetMapWithoutPrefix(s.KeysHash(), "meta_") common.Metas = GetMapWithoutPrefix(s.KeysHash(), "meta_")
common.ClientConfig.OidcAdditionalEndpointParams = GetMapWithoutPrefix(s.KeysHash(), "oidc_additional_")
return common, nil return common, nil
} }
@@ -247,7 +281,6 @@ func LoadAllProxyConfsFromIni(
source interface{}, source interface{},
start []string, start []string,
) (map[string]ProxyConf, map[string]VisitorConf, error) { ) (map[string]ProxyConf, map[string]VisitorConf, error) {
f, err := ini.LoadSources(ini.LoadOptions{ f, err := ini.LoadSources(ini.LoadOptions{
Insensitive: false, Insensitive: false,
InsensitiveSections: false, InsensitiveSections: false,
@@ -290,7 +323,7 @@ func LoadAllProxyConfsFromIni(
for _, section := range rangeSections { for _, section := range rangeSections {
err = renderRangeProxyTemplates(f, section) err = renderRangeProxyTemplates(f, section)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("fail to render range-section[%s] with error: %v", section.Name(), err) return nil, nil, fmt.Errorf("failed to render template for proxy %s: %v", section.Name(), err)
} }
} }
@@ -315,7 +348,7 @@ func LoadAllProxyConfsFromIni(
case "server": case "server":
newConf, newErr := NewProxyConfFromIni(prefix, name, section) newConf, newErr := NewProxyConfFromIni(prefix, name, section)
if newErr != nil { if newErr != nil {
return nil, nil, fmt.Errorf("fail to parse section[%s], err: %v", name, newErr) return nil, nil, fmt.Errorf("failed to parse proxy %s, err: %v", name, newErr)
} }
proxyConfs[prefix+name] = newConf proxyConfs[prefix+name] = newConf
case "visitor": case "visitor":
@@ -325,14 +358,13 @@ func LoadAllProxyConfsFromIni(
} }
visitorConfs[prefix+name] = newConf visitorConfs[prefix+name] = newConf
default: default:
return nil, nil, fmt.Errorf("section[%s] role should be 'server' or 'visitor'", name) return nil, nil, fmt.Errorf("proxy %s role should be 'server' or 'visitor'", name)
} }
} }
return proxyConfs, visitorConfs, nil return proxyConfs, visitorConfs, nil
} }
func renderRangeProxyTemplates(f *ini.File, section *ini.Section) error { func renderRangeProxyTemplates(f *ini.File, section *ini.Section) error {
// Validation // Validation
localPortStr := section.Key("local_port").String() localPortStr := section.Key("local_port").String()
remotePortStr := section.Key("remote_port").String() remotePortStr := section.Key("remote_port").String()
@@ -370,8 +402,12 @@ func renderRangeProxyTemplates(f *ini.File, section *ini.Section) error {
} }
copySection(section, tmpsection) copySection(section, tmpsection)
tmpsection.NewKey("local_port", fmt.Sprintf("%d", localPorts[i])) if _, err := tmpsection.NewKey("local_port", fmt.Sprintf("%d", localPorts[i])); err != nil {
tmpsection.NewKey("remote_port", fmt.Sprintf("%d", remotePorts[i])) return fmt.Errorf("local_port new key in section error: %v", err)
}
if _, err := tmpsection.NewKey("remote_port", fmt.Sprintf("%d", remotePorts[i])); err != nil {
return fmt.Errorf("remote_port new key in section error: %v", err)
}
} }
return nil return nil
@@ -379,6 +415,6 @@ func renderRangeProxyTemplates(f *ini.File, section *ini.Section) error {
func copySection(source, target *ini.Section) { func copySection(source, target *ini.Section) {
for key, value := range source.KeysHash() { for key, value := range source.KeysHash() {
target.NewKey(key, value) _, _ = target.NewKey(key, value)
} }
} }

View File

@@ -17,18 +17,17 @@ package config
import ( import (
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/fatedier/frp/pkg/auth" "github.com/fatedier/frp/pkg/auth"
"github.com/fatedier/frp/pkg/consts" "github.com/fatedier/frp/pkg/consts"
"github.com/stretchr/testify/assert"
) )
const ( const (
testUser = "test" testUser = "test"
) )
var ( var testClientBytesWithFull = []byte(`
testClientBytesWithFull = []byte(`
# [common] is integral section # [common] is integral section
[common] [common]
server_addr = 0.0.0.9 server_addr = 0.0.0.9
@@ -237,7 +236,6 @@ var (
use_encryption = false use_encryption = false
use_compression = false use_compression = false
`) `)
)
func Test_LoadClientCommonConf(t *testing.T) { func Test_LoadClientCommonConf(t *testing.T) {
assert := assert.New(t) assert := assert.New(t)
@@ -259,43 +257,47 @@ func Test_LoadClientCommonConf(t *testing.T) {
OidcTokenEndpointURL: "endpoint_url", OidcTokenEndpointURL: "endpoint_url",
}, },
}, },
ServerAddr: "0.0.0.9", ServerAddr: "0.0.0.9",
ServerPort: 7009, ServerPort: 7009,
HTTPProxy: "http://user:passwd@192.168.1.128:8080", DialServerTimeout: 10,
LogFile: "./frpc.log9", DialServerKeepAlive: 7200,
LogWay: "file", HTTPProxy: "http://user:passwd@192.168.1.128:8080",
LogLevel: "info9", LogFile: "./frpc.log9",
LogMaxDays: 39, LogWay: "file",
DisableLogColor: false, LogLevel: "info9",
AdminAddr: "127.0.0.9", LogMaxDays: 39,
AdminPort: 7409, DisableLogColor: false,
AdminUser: "admin9", AdminAddr: "127.0.0.9",
AdminPwd: "admin9", AdminPort: 7409,
AssetsDir: "./static9", AdminUser: "admin9",
PoolCount: 59, AdminPwd: "admin9",
TCPMux: true, AssetsDir: "./static9",
User: "your_name", PoolCount: 59,
LoginFailExit: true, TCPMux: true,
Protocol: "tcp", TCPMuxKeepaliveInterval: 60,
TLSEnable: true, User: "your_name",
TLSCertFile: "client.crt", LoginFailExit: true,
TLSKeyFile: "client.key", Protocol: "tcp",
TLSTrustedCaFile: "ca.crt", TLSEnable: true,
TLSServerName: "example.com", TLSCertFile: "client.crt",
DNSServer: "8.8.8.9", TLSKeyFile: "client.key",
Start: []string{"ssh", "dns"}, TLSTrustedCaFile: "ca.crt",
HeartbeatInterval: 39, TLSServerName: "example.com",
HeartbeatTimeout: 99, DNSServer: "8.8.8.9",
Start: []string{"ssh", "dns"},
HeartbeatInterval: 39,
HeartbeatTimeout: 99,
Metas: map[string]string{ Metas: map[string]string{
"var1": "123", "var1": "123",
"var2": "234", "var2": "234",
}, },
UDPPacketSize: 1509, UDPPacketSize: 1509,
IncludeConfigFiles: []string{},
} }
common, err := UnmarshalClientConfFromIni(testClientBytesWithFull) common, err := UnmarshalClientConfFromIni(testClientBytesWithFull)
assert.NoError(err) assert.NoError(err)
assert.Equal(expected, common) assert.EqualValues(expected, common)
} }
func Test_LoadClientBasicConf(t *testing.T) { func Test_LoadClientBasicConf(t *testing.T) {
@@ -641,5 +643,4 @@ func Test_LoadClientBasicConf(t *testing.T) {
assert.NoError(err) assert.NoError(err)
assert.Equal(proxyExpected, proxyActual) assert.Equal(proxyExpected, proxyActual)
assert.Equal(visitorExpected, visitorActual) assert.Equal(visitorExpected, visitorActual)
} }

99
pkg/config/parse.go Normal file
View File

@@ -0,0 +1,99 @@
// Copyright 2021 The frp Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package config
import (
"bytes"
"fmt"
"os"
"path/filepath"
)
func ParseClientConfig(filePath string) (
cfg ClientCommonConf,
pxyCfgs map[string]ProxyConf,
visitorCfgs map[string]VisitorConf,
err error,
) {
var content []byte
content, err = GetRenderedConfFromFile(filePath)
if err != nil {
return
}
configBuffer := bytes.NewBuffer(nil)
configBuffer.Write(content)
// Parse common section.
cfg, err = UnmarshalClientConfFromIni(content)
if err != nil {
return
}
cfg.Complete()
if err = cfg.Validate(); err != nil {
err = fmt.Errorf("parse config error: %v", err)
return
}
// Aggregate proxy configs from include files.
var buf []byte
buf, err = getIncludeContents(cfg.IncludeConfigFiles)
if err != nil {
err = fmt.Errorf("getIncludeContents error: %v", err)
return
}
configBuffer.WriteString("\n")
configBuffer.Write(buf)
// Parse all proxy and visitor configs.
pxyCfgs, visitorCfgs, err = LoadAllProxyConfsFromIni(cfg.User, configBuffer.Bytes(), cfg.Start)
if err != nil {
return
}
return
}
// getIncludeContents renders all configs from paths.
// files format can be a single file path or directory or regex path.
func getIncludeContents(paths []string) ([]byte, error) {
out := bytes.NewBuffer(nil)
for _, path := range paths {
absDir, err := filepath.Abs(filepath.Dir(path))
if err != nil {
return nil, err
}
if _, err := os.Stat(absDir); os.IsNotExist(err) {
return nil, err
}
files, err := os.ReadDir(absDir)
if err != nil {
return nil, err
}
for _, fi := range files {
if fi.IsDir() {
continue
}
absFile := filepath.Join(absDir, fi.Name())
if matched, _ := filepath.Match(filepath.Join(absDir, filepath.Base(path)), absFile); matched {
tmpContent, err := GetRenderedConfFromFile(absFile)
if err != nil {
return nil, fmt.Errorf("render extra config %s error: %v", absFile, err)
}
out.Write(tmpContent)
out.WriteString("\n")
}
}
}
return out.Bytes(), nil
}

View File

@@ -16,13 +16,15 @@ package config
import ( import (
"fmt" "fmt"
"net"
"reflect" "reflect"
"strconv"
"strings" "strings"
"gopkg.in/ini.v1"
"github.com/fatedier/frp/pkg/consts" "github.com/fatedier/frp/pkg/consts"
"github.com/fatedier/frp/pkg/msg" "github.com/fatedier/frp/pkg/msg"
"gopkg.in/ini.v1"
) )
// Proxy // Proxy
@@ -143,7 +145,6 @@ type BaseProxyConf struct {
// meta info for each proxy // meta info for each proxy
Metas map[string]string `ini:"-" json:"metas"` Metas map[string]string `ini:"-" json:"metas"`
// TODO: LocalSvrConf => LocalAppConf
LocalSvrConf `ini:",extends"` LocalSvrConf `ini:",extends"`
HealthCheckConf `ini:",extends"` HealthCheckConf `ini:",extends"`
} }
@@ -163,6 +164,7 @@ type HTTPProxyConf struct {
HTTPPwd string `ini:"http_pwd" json:"http_pwd"` HTTPPwd string `ini:"http_pwd" json:"http_pwd"`
HostHeaderRewrite string `ini:"host_header_rewrite" json:"host_header_rewrite"` HostHeaderRewrite string `ini:"host_header_rewrite" json:"host_header_rewrite"`
Headers map[string]string `ini:"-" json:"headers"` Headers map[string]string `ini:"-" json:"headers"`
RouteByHTTPUser string `ini:"route_by_http_user" json:"route_by_http_user"`
} }
// HTTPS // HTTPS
@@ -179,8 +181,9 @@ type TCPProxyConf struct {
// TCPMux // TCPMux
type TCPMuxProxyConf struct { type TCPMuxProxyConf struct {
BaseProxyConf `ini:",extends"` BaseProxyConf `ini:",extends"`
DomainConf `ini:",extends"` DomainConf `ini:",extends"`
RouteByHTTPUser string `ini:"route_by_http_user" json:"route_by_http_user"`
Multiplexer string `ini:"multiplexer"` Multiplexer string `ini:"multiplexer"`
} }
@@ -274,7 +277,7 @@ func NewProxyConfFromIni(prefix, name string, section *ini.Section) (ProxyConf,
conf := DefaultProxyConf(proxyType) conf := DefaultProxyConf(proxyType)
if conf == nil { if conf == nil {
return nil, fmt.Errorf("proxy [%s] type [%s] error", name, proxyType) return nil, fmt.Errorf("proxy %s has invalid type [%s]", name, proxyType)
} }
if err := conf.UnmarshalFromIni(prefix, name, section); err != nil { if err := conf.UnmarshalFromIni(prefix, name, section); err != nil {
@@ -371,7 +374,7 @@ func (cfg *BaseProxyConf) decorate(prefix string, name string, section *ini.Sect
} }
if cfg.HealthCheckType == "http" && cfg.Plugin == "" && cfg.HealthCheckURL != "" { if cfg.HealthCheckType == "http" && cfg.Plugin == "" && cfg.HealthCheckURL != "" {
s := fmt.Sprintf("http://%s:%d", cfg.LocalIP, cfg.LocalPort) s := "http://" + net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
if !strings.HasPrefix(cfg.HealthCheckURL, "/") { if !strings.HasPrefix(cfg.HealthCheckURL, "/") {
s += "/" s += "/"
} }
@@ -417,10 +420,6 @@ func (cfg *BaseProxyConf) checkForCli() (err error) {
return nil return nil
} }
func (cfg *BaseProxyConf) checkForSvr(conf ServerCommonConf) error {
return nil
}
// DomainConf // DomainConf
func (cfg *DomainConf) check() (err error) { func (cfg *DomainConf) check() (err error) {
if len(cfg.CustomDomains) == 0 && cfg.SubDomain == "" { if len(cfg.CustomDomains) == 0 && cfg.SubDomain == "" {
@@ -577,7 +576,7 @@ func (cfg *TCPMuxProxyConf) Compare(cmp ProxyConf) bool {
return false return false
} }
if cfg.Multiplexer != cmpConf.Multiplexer { if cfg.Multiplexer != cmpConf.Multiplexer || cfg.RouteByHTTPUser != cmpConf.RouteByHTTPUser {
return false return false
} }
@@ -602,6 +601,7 @@ func (cfg *TCPMuxProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
cfg.CustomDomains = pMsg.CustomDomains cfg.CustomDomains = pMsg.CustomDomains
cfg.SubDomain = pMsg.SubDomain cfg.SubDomain = pMsg.SubDomain
cfg.Multiplexer = pMsg.Multiplexer cfg.Multiplexer = pMsg.Multiplexer
cfg.RouteByHTTPUser = pMsg.RouteByHTTPUser
} }
func (cfg *TCPMuxProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { func (cfg *TCPMuxProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
@@ -611,6 +611,7 @@ func (cfg *TCPMuxProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
pMsg.CustomDomains = cfg.CustomDomains pMsg.CustomDomains = cfg.CustomDomains
pMsg.SubDomain = cfg.SubDomain pMsg.SubDomain = cfg.SubDomain
pMsg.Multiplexer = cfg.Multiplexer pMsg.Multiplexer = cfg.Multiplexer
pMsg.RouteByHTTPUser = cfg.RouteByHTTPUser
} }
func (cfg *TCPMuxProxyConf) CheckForCli() (err error) { func (cfg *TCPMuxProxyConf) CheckForCli() (err error) {
@@ -725,6 +726,7 @@ func (cfg *HTTPProxyConf) Compare(cmp ProxyConf) bool {
cfg.HTTPUser != cmpConf.HTTPUser || cfg.HTTPUser != cmpConf.HTTPUser ||
cfg.HTTPPwd != cmpConf.HTTPPwd || cfg.HTTPPwd != cmpConf.HTTPPwd ||
cfg.HostHeaderRewrite != cmpConf.HostHeaderRewrite || cfg.HostHeaderRewrite != cmpConf.HostHeaderRewrite ||
cfg.RouteByHTTPUser != cmpConf.RouteByHTTPUser ||
!reflect.DeepEqual(cfg.Headers, cmpConf.Headers) { !reflect.DeepEqual(cfg.Headers, cmpConf.Headers) {
return false return false
} }
@@ -755,6 +757,7 @@ func (cfg *HTTPProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
cfg.HTTPUser = pMsg.HTTPUser cfg.HTTPUser = pMsg.HTTPUser
cfg.HTTPPwd = pMsg.HTTPPwd cfg.HTTPPwd = pMsg.HTTPPwd
cfg.Headers = pMsg.Headers cfg.Headers = pMsg.Headers
cfg.RouteByHTTPUser = pMsg.RouteByHTTPUser
} }
func (cfg *HTTPProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { func (cfg *HTTPProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
@@ -768,6 +771,7 @@ func (cfg *HTTPProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
pMsg.HTTPUser = cfg.HTTPUser pMsg.HTTPUser = cfg.HTTPUser
pMsg.HTTPPwd = cfg.HTTPPwd pMsg.HTTPPwd = cfg.HTTPPwd
pMsg.Headers = cfg.Headers pMsg.Headers = cfg.Headers
pMsg.RouteByHTTPUser = cfg.RouteByHTTPUser
} }
func (cfg *HTTPProxyConf) CheckForCli() (err error) { func (cfg *HTTPProxyConf) CheckForCli() (err error) {

View File

@@ -17,10 +17,10 @@ package config
import ( import (
"testing" "testing"
"github.com/fatedier/frp/pkg/consts"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
"github.com/fatedier/frp/pkg/consts"
) )
var ( var (
@@ -49,7 +49,6 @@ func Test_Proxy_UnmarshalFromIni(t *testing.T) {
source []byte source []byte
expected ProxyConf expected ProxyConf
}{ }{
{ {
sname: "ssh", sname: "ssh",
source: []byte(` source: []byte(`
@@ -457,5 +456,4 @@ func Test_RangeProxy_UnmarshalFromIni(t *testing.T) {
assert.Equal(c.expected, actual) assert.Equal(c.expected, actual)
} }
} }

View File

@@ -18,11 +18,12 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/go-playground/validator/v10"
"gopkg.in/ini.v1"
"github.com/fatedier/frp/pkg/auth" "github.com/fatedier/frp/pkg/auth"
plugin "github.com/fatedier/frp/pkg/plugin/server" plugin "github.com/fatedier/frp/pkg/plugin/server"
"github.com/fatedier/frp/pkg/util/util" "github.com/fatedier/frp/pkg/util/util"
"gopkg.in/ini.v1"
) )
// ServerCommonConf contains information for a server service. It is // ServerCommonConf contains information for a server service. It is
@@ -36,31 +37,33 @@ type ServerCommonConf struct {
BindAddr string `ini:"bind_addr" json:"bind_addr"` BindAddr string `ini:"bind_addr" json:"bind_addr"`
// BindPort specifies the port that the server listens on. By default, this // BindPort specifies the port that the server listens on. By default, this
// value is 7000. // value is 7000.
BindPort int `ini:"bind_port" json:"bind_port"` BindPort int `ini:"bind_port" json:"bind_port" validate:"gte=0,lte=65535"`
// BindUDPPort specifies the UDP port that the server listens on. If this // BindUDPPort specifies the UDP port that the server listens on. If this
// value is 0, the server will not listen for UDP connections. By default, // value is 0, the server will not listen for UDP connections. By default,
// this value is 0 // this value is 0
BindUDPPort int `ini:"bind_udp_port" json:"bind_udp_port"` BindUDPPort int `ini:"bind_udp_port" json:"bind_udp_port" validate:"gte=0,lte=65535"`
// KCPBindPort specifies the KCP port that the server listens on. If this // KCPBindPort specifies the KCP port that the server listens on. If this
// value is 0, the server will not listen for KCP connections. By default, // value is 0, the server will not listen for KCP connections. By default,
// this value is 0. // this value is 0.
KCPBindPort int `ini:"kcp_bind_port" json:"kcp_bind_port"` KCPBindPort int `ini:"kcp_bind_port" json:"kcp_bind_port" validate:"gte=0,lte=65535"`
// ProxyBindAddr specifies the address that the proxy binds to. This value // ProxyBindAddr specifies the address that the proxy binds to. This value
// may be the same as BindAddr. // may be the same as BindAddr.
ProxyBindAddr string `ini:"proxy_bind_addr" json:"proxy_bind_addr"` ProxyBindAddr string `ini:"proxy_bind_addr" json:"proxy_bind_addr"`
// VhostHTTPPort specifies the port that the server listens for HTTP Vhost // VhostHTTPPort specifies the port that the server listens for HTTP Vhost
// requests. If this value is 0, the server will not listen for HTTP // requests. If this value is 0, the server will not listen for HTTP
// requests. By default, this value is 0. // requests. By default, this value is 0.
VhostHTTPPort int `ini:"vhost_http_port" json:"vhost_http_port"` VhostHTTPPort int `ini:"vhost_http_port" json:"vhost_http_port" validate:"gte=0,lte=65535"`
// VhostHTTPSPort specifies the port that the server listens for HTTPS // VhostHTTPSPort specifies the port that the server listens for HTTPS
// Vhost requests. If this value is 0, the server will not listen for HTTPS // Vhost requests. If this value is 0, the server will not listen for HTTPS
// requests. By default, this value is 0. // requests. By default, this value is 0.
VhostHTTPSPort int `ini:"vhost_https_port" json:"vhost_https_port"` VhostHTTPSPort int `ini:"vhost_https_port" json:"vhost_https_port" validate:"gte=0,lte=65535"`
// TCPMuxHTTPConnectPort specifies the port that the server listens for TCP // TCPMuxHTTPConnectPort specifies the port that the server listens for TCP
// HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP // HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP
// requests on one single port. If it's not - it will listen on this value for // requests on one single port. If it's not - it will listen on this value for
// HTTP CONNECT requests. By default, this value is 0. // HTTP CONNECT requests. By default, this value is 0.
TCPMuxHTTPConnectPort int `ini:"tcpmux_httpconnect_port" json:"tcpmux_httpconnect_port"` TCPMuxHTTPConnectPort int `ini:"tcpmux_httpconnect_port" json:"tcpmux_httpconnect_port" validate:"gte=0,lte=65535"`
// If TCPMuxPassthrough is true, frps won't do any update on traffic.
TCPMuxPassthrough bool `ini:"tcpmux_passthrough" json:"tcpmux_passthrough"`
// VhostHTTPTimeout specifies the response header timeout for the Vhost // VhostHTTPTimeout specifies the response header timeout for the Vhost
// HTTP server, in seconds. By default, this value is 60. // HTTP server, in seconds. By default, this value is 60.
VhostHTTPTimeout int64 `ini:"vhost_http_timeout" json:"vhost_http_timeout"` VhostHTTPTimeout int64 `ini:"vhost_http_timeout" json:"vhost_http_timeout"`
@@ -70,12 +73,23 @@ type ServerCommonConf struct {
// DashboardPort specifies the port that the dashboard listens on. If this // DashboardPort specifies the port that the dashboard listens on. If this
// value is 0, the dashboard will not be started. By default, this value is // value is 0, the dashboard will not be started. By default, this value is
// 0. // 0.
DashboardPort int `ini:"dashboard_port" json:"dashboard_port"` DashboardPort int `ini:"dashboard_port" json:"dashboard_port" validate:"gte=0,lte=65535"`
// DashboardTLSCertFile specifies the path of the cert file that the server will
// load. If "dashboard_tls_cert_file", "dashboard_tls_key_file" are valid, the server will use this
// supplied tls configuration.
DashboardTLSCertFile string `ini:"dashboard_tls_cert_file" json:"dashboard_tls_cert_file"`
// DashboardTLSKeyFile specifies the path of the secret key that the server will
// load. If "dashboard_tls_cert_file", "dashboard_tls_key_file" are valid, the server will use this
// supplied tls configuration.
DashboardTLSKeyFile string `ini:"dashboard_tls_key_file" json:"dashboard_tls_key_file"`
// DashboardTLSMode specifies the mode of the dashboard between HTTP or HTTPS modes. By
// default, this value is false, which is HTTP mode.
DashboardTLSMode bool `ini:"dashboard_tls_mode" json:"dashboard_tls_mode"`
// DashboardUser specifies the username that the dashboard will use for // DashboardUser specifies the username that the dashboard will use for
// login. By default, this value is "admin". // login.
DashboardUser string `ini:"dashboard_user" json:"dashboard_user"` DashboardUser string `ini:"dashboard_user" json:"dashboard_user"`
// DashboardUser specifies the password that the dashboard will use for // DashboardPwd specifies the password that the dashboard will use for
// login. By default, this value is "admin". // login.
DashboardPwd string `ini:"dashboard_pwd" json:"dashboard_pwd"` DashboardPwd string `ini:"dashboard_pwd" json:"dashboard_pwd"`
// EnablePrometheus will export prometheus metrics on {dashboard_addr}:{dashboard_port} // EnablePrometheus will export prometheus metrics on {dashboard_addr}:{dashboard_port}
// in /metrics api. // in /metrics api.
@@ -117,6 +131,12 @@ type ServerCommonConf struct {
// from a client to share a single TCP connection. By default, this value // from a client to share a single TCP connection. By default, this value
// is true. // is true.
TCPMux bool `ini:"tcp_mux" json:"tcp_mux"` TCPMux bool `ini:"tcp_mux" json:"tcp_mux"`
// TCPMuxKeepaliveInterval specifies the keep alive interval for TCP stream multipler.
// If TCPMux is true, heartbeat of application layer is unnecessary because it can only rely on heartbeat in TCPMux.
TCPMuxKeepaliveInterval int64 `ini:"tcp_mux_keepalive_interval" json:"tcp_mux_keepalive_interval"`
// TCPKeepAlive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
// If negative, keep-alive probes are disabled.
TCPKeepAlive int64 `ini:"tcp_keepalive" json:"tcp_keepalive"`
// Custom404Page specifies a path to a custom 404 page to display. If this // Custom404Page specifies a path to a custom 404 page to display. If this
// value is "", a default page will be displayed. By default, this value is // value is "", a default page will be displayed. By default, this value is
// "". // "".
@@ -153,7 +173,7 @@ type ServerCommonConf struct {
TLSTrustedCaFile string `ini:"tls_trusted_ca_file" json:"tls_trusted_ca_file"` TLSTrustedCaFile string `ini:"tls_trusted_ca_file" json:"tls_trusted_ca_file"`
// HeartBeatTimeout specifies the maximum time to wait for a heartbeat // HeartBeatTimeout specifies the maximum time to wait for a heartbeat
// before terminating the connection. It is not recommended to change this // before terminating the connection. It is not recommended to change this
// value. By default, this value is 90. // value. By default, this value is 90. Set negative value to disable it.
HeartbeatTimeout int64 `ini:"heartbeat_timeout" json:"heartbeat_timeout"` HeartbeatTimeout int64 `ini:"heartbeat_timeout" json:"heartbeat_timeout"`
// UserConnTimeout specifies the maximum time to wait for a work // UserConnTimeout specifies the maximum time to wait for a work
// connection. By default, this value is 10. // connection. By default, this value is 10.
@@ -163,53 +183,59 @@ type ServerCommonConf struct {
// UDPPacketSize specifies the UDP packet size // UDPPacketSize specifies the UDP packet size
// By default, this value is 1500 // By default, this value is 1500
UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"` UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"`
// Enable golang pprof handlers in dashboard listener.
// Dashboard port must be set first.
PprofEnable bool `ini:"pprof_enable" json:"pprof_enable"`
} }
// GetDefaultServerConf returns a server configuration with reasonable // GetDefaultServerConf returns a server configuration with reasonable
// defaults. // defaults.
func GetDefaultServerConf() ServerCommonConf { func GetDefaultServerConf() ServerCommonConf {
return ServerCommonConf{ return ServerCommonConf{
ServerConfig: auth.GetDefaultServerConf(), ServerConfig: auth.GetDefaultServerConf(),
BindAddr: "0.0.0.0", BindAddr: "0.0.0.0",
BindPort: 7000, BindPort: 7000,
BindUDPPort: 0, BindUDPPort: 0,
KCPBindPort: 0, KCPBindPort: 0,
ProxyBindAddr: "", ProxyBindAddr: "",
VhostHTTPPort: 0, VhostHTTPPort: 0,
VhostHTTPSPort: 0, VhostHTTPSPort: 0,
TCPMuxHTTPConnectPort: 0, TCPMuxHTTPConnectPort: 0,
VhostHTTPTimeout: 60, TCPMuxPassthrough: false,
DashboardAddr: "0.0.0.0", VhostHTTPTimeout: 60,
DashboardPort: 0, DashboardAddr: "0.0.0.0",
DashboardUser: "admin", DashboardPort: 0,
DashboardPwd: "admin", DashboardUser: "",
EnablePrometheus: false, DashboardPwd: "",
AssetsDir: "", EnablePrometheus: false,
LogFile: "console", AssetsDir: "",
LogWay: "console", LogFile: "console",
LogLevel: "info", LogWay: "console",
LogMaxDays: 3, LogLevel: "info",
DisableLogColor: false, LogMaxDays: 3,
DetailedErrorsToClient: true, DisableLogColor: false,
SubDomainHost: "", DetailedErrorsToClient: true,
TCPMux: true, SubDomainHost: "",
AllowPorts: make(map[int]struct{}), TCPMux: true,
MaxPoolCount: 5, TCPMuxKeepaliveInterval: 60,
MaxPortsPerClient: 0, TCPKeepAlive: 7200,
TLSOnly: false, AllowPorts: make(map[int]struct{}),
TLSCertFile: "", MaxPoolCount: 5,
TLSKeyFile: "", MaxPortsPerClient: 0,
TLSTrustedCaFile: "", TLSOnly: false,
HeartbeatTimeout: 90, TLSCertFile: "",
UserConnTimeout: 10, TLSKeyFile: "",
Custom404Page: "", TLSTrustedCaFile: "",
HTTPPlugins: make(map[string]plugin.HTTPPluginOptions), HeartbeatTimeout: 90,
UDPPacketSize: 1500, UserConnTimeout: 10,
Custom404Page: "",
HTTPPlugins: make(map[string]plugin.HTTPPluginOptions),
UDPPacketSize: 1500,
PprofEnable: false,
} }
} }
func UnmarshalServerConfFromIni(source interface{}) (ServerCommonConf, error) { func UnmarshalServerConfFromIni(source interface{}) (ServerCommonConf, error) {
f, err := ini.LoadSources(ini.LoadOptions{ f, err := ini.LoadSources(ini.LoadOptions{
Insensitive: false, Insensitive: false,
InsensitiveSections: false, InsensitiveSections: false,
@@ -223,7 +249,6 @@ func UnmarshalServerConfFromIni(source interface{}) (ServerCommonConf, error) {
s, err := f.GetSection("common") s, err := f.GetSection("common")
if err != nil { if err != nil {
// TODO: add error info
return ServerCommonConf{}, err return ServerCommonConf{}, err
} }
@@ -282,7 +307,24 @@ func (cfg *ServerCommonConf) Complete() {
} }
func (cfg *ServerCommonConf) Validate() error { func (cfg *ServerCommonConf) Validate() error {
return nil if !cfg.DashboardTLSMode {
if cfg.DashboardTLSCertFile != "" {
fmt.Println("WARNING! dashboard_tls_cert_file is invalid when dashboard_tls_mode is false")
}
if cfg.DashboardTLSKeyFile != "" {
fmt.Println("WARNING! dashboard_tls_key_file is invalid when dashboard_tls_mode is false")
}
} else {
if cfg.DashboardTLSCertFile == "" {
return fmt.Errorf("ERROR! dashboard_tls_cert_file must be specified when dashboard_tls_mode is true")
}
if cfg.DashboardTLSKeyFile == "" {
return fmt.Errorf("ERROR! dashboard_tls_cert_file must be specified when dashboard_tls_mode is true")
}
}
return validator.New().Struct(cfg)
} }
func loadHTTPPluginOpt(section *ini.Section) (*plugin.HTTPPluginOptions, error) { func loadHTTPPluginOpt(section *ini.Section) (*plugin.HTTPPluginOptions, error) {

View File

@@ -17,10 +17,10 @@ package config
import ( import (
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/fatedier/frp/pkg/auth" "github.com/fatedier/frp/pkg/auth"
plugin "github.com/fatedier/frp/pkg/plugin/server" plugin "github.com/fatedier/frp/pkg/plugin/server"
"github.com/stretchr/testify/assert"
) )
func Test_LoadServerCommonConf(t *testing.T) { func Test_LoadServerCommonConf(t *testing.T) {
@@ -126,20 +126,22 @@ func Test_LoadServerCommonConf(t *testing.T) {
HeartbeatTimeout: 99, HeartbeatTimeout: 99,
UserConnTimeout: 9, UserConnTimeout: 9,
AllowPorts: map[int]struct{}{ AllowPorts: map[int]struct{}{
10: struct{}{}, 10: {},
11: struct{}{}, 11: {},
12: struct{}{}, 12: {},
99: struct{}{}, 99: {},
}, },
MaxPoolCount: 59, MaxPoolCount: 59,
MaxPortsPerClient: 9, MaxPortsPerClient: 9,
TLSOnly: true, TLSOnly: true,
TLSCertFile: "server.crt", TLSCertFile: "server.crt",
TLSKeyFile: "server.key", TLSKeyFile: "server.key",
TLSTrustedCaFile: "ca.crt", TLSTrustedCaFile: "ca.crt",
SubDomainHost: "frps.com", SubDomainHost: "frps.com",
TCPMux: true, TCPMux: true,
UDPPacketSize: 1509, TCPMuxKeepaliveInterval: 60,
TCPKeepAlive: 7200,
UDPPacketSize: 1509,
HTTPPlugins: map[string]plugin.HTTPPluginOptions{ HTTPPlugins: map[string]plugin.HTTPPluginOptions{
"user-manager": { "user-manager": {
@@ -174,27 +176,29 @@ func Test_LoadServerCommonConf(t *testing.T) {
AuthenticateNewWorkConns: false, AuthenticateNewWorkConns: false,
}, },
}, },
BindAddr: "0.0.0.9", BindAddr: "0.0.0.9",
BindPort: 7009, BindPort: 7009,
BindUDPPort: 7008, BindUDPPort: 7008,
ProxyBindAddr: "0.0.0.9", ProxyBindAddr: "0.0.0.9",
VhostHTTPTimeout: 60, VhostHTTPTimeout: 60,
DashboardAddr: "0.0.0.0", DashboardAddr: "0.0.0.0",
DashboardUser: "admin", DashboardUser: "",
DashboardPwd: "admin", DashboardPwd: "",
EnablePrometheus: false, EnablePrometheus: false,
LogFile: "console", LogFile: "console",
LogWay: "console", LogWay: "console",
LogLevel: "info", LogLevel: "info",
LogMaxDays: 3, LogMaxDays: 3,
DetailedErrorsToClient: true, DetailedErrorsToClient: true,
TCPMux: true, TCPMux: true,
AllowPorts: make(map[int]struct{}), TCPMuxKeepaliveInterval: 60,
MaxPoolCount: 5, TCPKeepAlive: 7200,
HeartbeatTimeout: 90, AllowPorts: make(map[int]struct{}),
UserConnTimeout: 10, MaxPoolCount: 5,
HTTPPlugins: make(map[string]plugin.HTTPPluginOptions), HeartbeatTimeout: 90,
UDPPacketSize: 1500, UserConnTimeout: 10,
HTTPPlugins: make(map[string]plugin.HTTPPluginOptions),
UDPPacketSize: 1500,
}, },
}, },
} }

View File

@@ -75,21 +75,22 @@ func (q *BandwidthQuantity) UnmarshalString(s string) error {
f float64 f float64
err error err error
) )
if strings.HasSuffix(s, "MB") { switch {
case strings.HasSuffix(s, "MB"):
base = MB base = MB
fstr := strings.TrimSuffix(s, "MB") fstr := strings.TrimSuffix(s, "MB")
f, err = strconv.ParseFloat(fstr, 64) f, err = strconv.ParseFloat(fstr, 64)
if err != nil { if err != nil {
return err return err
} }
} else if strings.HasSuffix(s, "KB") { case strings.HasSuffix(s, "KB"):
base = KB base = KB
fstr := strings.TrimSuffix(s, "KB") fstr := strings.TrimSuffix(s, "KB")
f, err = strconv.ParseFloat(fstr, 64) f, err = strconv.ParseFloat(fstr, 64)
if err != nil { if err != nil {
return err return err
} }
} else { default:
return errors.New("unit not support") return errors.New("unit not support")
} }

View File

@@ -16,25 +16,22 @@ package config
import ( import (
"bytes" "bytes"
"io/ioutil"
"os" "os"
"strings" "strings"
"text/template" "text/template"
) )
var ( var glbEnvs map[string]string
glbEnvs map[string]string
)
func init() { func init() {
glbEnvs = make(map[string]string) glbEnvs = make(map[string]string)
envs := os.Environ() envs := os.Environ()
for _, env := range envs { for _, env := range envs {
kv := strings.Split(env, "=") pair := strings.SplitN(env, "=", 2)
if len(kv) != 2 { if len(pair) != 2 {
continue continue
} }
glbEnvs[kv[0]] = kv[1] glbEnvs[pair[0]] = pair[1]
} }
} }
@@ -67,7 +64,7 @@ func RenderContent(in []byte) (out []byte, err error) {
func GetRenderedConfFromFile(path string) (out []byte, err error) { func GetRenderedConfFromFile(path string) (out []byte, err error) {
var b []byte var b []byte
b, err = ioutil.ReadFile(path) b, err = os.ReadFile(path)
if err != nil { if err != nil {
return return
} }

View File

@@ -18,9 +18,9 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"github.com/fatedier/frp/pkg/consts"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
"github.com/fatedier/frp/pkg/consts"
) )
// Visitor // Visitor
@@ -136,6 +136,7 @@ func (cfg *BaseVisitorConf) check() (err error) {
} }
func (cfg *BaseVisitorConf) unmarshalFromIni(prefix string, name string, section *ini.Section) error { func (cfg *BaseVisitorConf) unmarshalFromIni(prefix string, name string, section *ini.Section) error {
_ = section
// Custom decoration after basic unmarshal: // Custom decoration after basic unmarshal:
// proxy name // proxy name

View File

@@ -17,10 +17,10 @@ package config
import ( import (
"testing" "testing"
"github.com/fatedier/frp/pkg/consts"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
"github.com/fatedier/frp/pkg/consts"
) )
const testVisitorPrefix = "test." const testVisitorPrefix = "test."

View File

@@ -16,26 +16,26 @@ package consts
var ( var (
// proxy status // proxy status
Idle string = "idle" Idle = "idle"
Working string = "working" Working = "working"
Closed string = "closed" Closed = "closed"
Online string = "online" Online = "online"
Offline string = "offline" Offline = "offline"
// proxy type // proxy type
TCPProxy string = "tcp" TCPProxy = "tcp"
UDPProxy string = "udp" UDPProxy = "udp"
TCPMuxProxy string = "tcpmux" TCPMuxProxy = "tcpmux"
HTTPProxy string = "http" HTTPProxy = "http"
HTTPSProxy string = "https" HTTPSProxy = "https"
STCPProxy string = "stcp" STCPProxy = "stcp"
XTCPProxy string = "xtcp" XTCPProxy = "xtcp"
SUDPProxy string = "sudp" SUDPProxy = "sudp"
// authentication method // authentication method
TokenAuthMethod string = "token" TokenAuthMethod = "token"
OidcAuthMethod string = "oidc" OidcAuthMethod = "oidc"
// TCP multiplexer // TCP multiplexer
HTTPConnectTCPMultiplexer string = "httpconnect" HTTPConnectTCPMultiplexer = "httpconnect"
) )

View File

@@ -30,7 +30,7 @@ func EnablePrometheus() {
sm.Add(prometheus.ServerMetrics) sm.Add(prometheus.ServerMetrics)
} }
var sm *serverMetrics = &serverMetrics{} var sm = &serverMetrics{}
func init() { func init() {
metrics.Register(sm) metrics.Register(sm)

View File

@@ -23,9 +23,12 @@ import (
server "github.com/fatedier/frp/server/metrics" server "github.com/fatedier/frp/server/metrics"
) )
var sm *serverMetrics = newServerMetrics() var (
var ServerMetrics server.ServerMetrics sm = newServerMetrics()
var StatsCollector Collector
ServerMetrics server.ServerMetrics
StatsCollector Collector
)
func init() { func init() {
ServerMetrics = sm ServerMetrics = sm

View File

@@ -4,5 +4,7 @@ import (
"github.com/fatedier/frp/pkg/metrics/aggregate" "github.com/fatedier/frp/pkg/metrics/aggregate"
) )
var EnableMem = aggregate.EnableMem var (
var EnablePrometheus = aggregate.EnablePrometheus EnableMem = aggregate.EnableMem
EnablePrometheus = aggregate.EnablePrometheus
)

View File

@@ -1,9 +1,9 @@
package prometheus package prometheus
import ( import (
"github.com/fatedier/frp/server/metrics"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/fatedier/frp/server/metrics"
) )
const ( const (

View File

@@ -22,9 +22,7 @@ import (
type Message = jsonMsg.Message type Message = jsonMsg.Message
var ( var msgCtl *jsonMsg.MsgCtl
msgCtl *jsonMsg.MsgCtl
)
func init() { func init() {
msgCtl = jsonMsg.NewMsgCtl() msgCtl = jsonMsg.NewMsgCtl()

View File

@@ -37,158 +37,155 @@ const (
TypeNatHoleSid = '5' TypeNatHoleSid = '5'
) )
var ( var msgTypeMap = map[byte]interface{}{
msgTypeMap = map[byte]interface{}{ TypeLogin: Login{},
TypeLogin: Login{}, TypeLoginResp: LoginResp{},
TypeLoginResp: LoginResp{}, TypeNewProxy: NewProxy{},
TypeNewProxy: NewProxy{}, TypeNewProxyResp: NewProxyResp{},
TypeNewProxyResp: NewProxyResp{}, TypeCloseProxy: CloseProxy{},
TypeCloseProxy: CloseProxy{}, TypeNewWorkConn: NewWorkConn{},
TypeNewWorkConn: NewWorkConn{}, TypeReqWorkConn: ReqWorkConn{},
TypeReqWorkConn: ReqWorkConn{}, TypeStartWorkConn: StartWorkConn{},
TypeStartWorkConn: StartWorkConn{}, TypeNewVisitorConn: NewVisitorConn{},
TypeNewVisitorConn: NewVisitorConn{}, TypeNewVisitorConnResp: NewVisitorConnResp{},
TypeNewVisitorConnResp: NewVisitorConnResp{}, TypePing: Ping{},
TypePing: Ping{}, TypePong: Pong{},
TypePong: Pong{}, TypeUDPPacket: UDPPacket{},
TypeUDPPacket: UDPPacket{}, TypeNatHoleVisitor: NatHoleVisitor{},
TypeNatHoleVisitor: NatHoleVisitor{}, TypeNatHoleClient: NatHoleClient{},
TypeNatHoleClient: NatHoleClient{}, TypeNatHoleResp: NatHoleResp{},
TypeNatHoleResp: NatHoleResp{}, TypeNatHoleClientDetectOK: NatHoleClientDetectOK{},
TypeNatHoleClientDetectOK: NatHoleClientDetectOK{}, TypeNatHoleSid: NatHoleSid{},
TypeNatHoleSid: NatHoleSid{}, }
}
)
// When frpc start, client send this message to login to server. // When frpc start, client send this message to login to server.
type Login struct { type Login struct {
Version string `json:"version"` Version string `json:"version,omitempty"`
Hostname string `json:"hostname"` Hostname string `json:"hostname,omitempty"`
Os string `json:"os"` Os string `json:"os,omitempty"`
Arch string `json:"arch"` Arch string `json:"arch,omitempty"`
User string `json:"user"` User string `json:"user,omitempty"`
PrivilegeKey string `json:"privilege_key"` PrivilegeKey string `json:"privilege_key,omitempty"`
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp,omitempty"`
RunID string `json:"run_id"` RunID string `json:"run_id,omitempty"`
Metas map[string]string `json:"metas"` Metas map[string]string `json:"metas,omitempty"`
// Some global configures. // Some global configures.
PoolCount int `json:"pool_count"` PoolCount int `json:"pool_count,omitempty"`
} }
type LoginResp struct { type LoginResp struct {
Version string `json:"version"` Version string `json:"version,omitempty"`
RunID string `json:"run_id"` RunID string `json:"run_id,omitempty"`
ServerUDPPort int `json:"server_udp_port"` ServerUDPPort int `json:"server_udp_port,omitempty"`
Error string `json:"error"` Error string `json:"error,omitempty"`
} }
// When frpc login success, send this message to frps for running a new proxy. // When frpc login success, send this message to frps for running a new proxy.
type NewProxy struct { type NewProxy struct {
ProxyName string `json:"proxy_name"` ProxyName string `json:"proxy_name,omitempty"`
ProxyType string `json:"proxy_type"` ProxyType string `json:"proxy_type,omitempty"`
UseEncryption bool `json:"use_encryption"` UseEncryption bool `json:"use_encryption,omitempty"`
UseCompression bool `json:"use_compression"` UseCompression bool `json:"use_compression,omitempty"`
Group string `json:"group"` Group string `json:"group,omitempty"`
GroupKey string `json:"group_key"` GroupKey string `json:"group_key,omitempty"`
Metas map[string]string `json:"metas"` Metas map[string]string `json:"metas,omitempty"`
// tcp and udp only // tcp and udp only
RemotePort int `json:"remote_port"` RemotePort int `json:"remote_port,omitempty"`
// http and https only // http and https only
CustomDomains []string `json:"custom_domains"` CustomDomains []string `json:"custom_domains,omitempty"`
SubDomain string `json:"subdomain"` SubDomain string `json:"subdomain,omitempty"`
Locations []string `json:"locations"` Locations []string `json:"locations,omitempty"`
HTTPUser string `json:"http_user"` HTTPUser string `json:"http_user,omitempty"`
HTTPPwd string `json:"http_pwd"` HTTPPwd string `json:"http_pwd,omitempty"`
HostHeaderRewrite string `json:"host_header_rewrite"` HostHeaderRewrite string `json:"host_header_rewrite,omitempty"`
Headers map[string]string `json:"headers"` Headers map[string]string `json:"headers,omitempty"`
RouteByHTTPUser string `json:"route_by_http_user,omitempty"`
// stcp // stcp
Sk string `json:"sk"` Sk string `json:"sk,omitempty"`
// tcpmux // tcpmux
Multiplexer string `json:"multiplexer"` Multiplexer string `json:"multiplexer,omitempty"`
} }
type NewProxyResp struct { type NewProxyResp struct {
ProxyName string `json:"proxy_name"` ProxyName string `json:"proxy_name,omitempty"`
RemoteAddr string `json:"remote_addr"` RemoteAddr string `json:"remote_addr,omitempty"`
Error string `json:"error"` Error string `json:"error,omitempty"`
} }
type CloseProxy struct { type CloseProxy struct {
ProxyName string `json:"proxy_name"` ProxyName string `json:"proxy_name,omitempty"`
} }
type NewWorkConn struct { type NewWorkConn struct {
RunID string `json:"run_id"` RunID string `json:"run_id,omitempty"`
PrivilegeKey string `json:"privilege_key"` PrivilegeKey string `json:"privilege_key,omitempty"`
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp,omitempty"`
} }
type ReqWorkConn struct { type ReqWorkConn struct{}
}
type StartWorkConn struct { type StartWorkConn struct {
ProxyName string `json:"proxy_name"` ProxyName string `json:"proxy_name,omitempty"`
SrcAddr string `json:"src_addr"` SrcAddr string `json:"src_addr,omitempty"`
DstAddr string `json:"dst_addr"` DstAddr string `json:"dst_addr,omitempty"`
SrcPort uint16 `json:"src_port"` SrcPort uint16 `json:"src_port,omitempty"`
DstPort uint16 `json:"dst_port"` DstPort uint16 `json:"dst_port,omitempty"`
Error string `json:"error"` Error string `json:"error,omitempty"`
} }
type NewVisitorConn struct { type NewVisitorConn struct {
ProxyName string `json:"proxy_name"` ProxyName string `json:"proxy_name,omitempty"`
SignKey string `json:"sign_key"` SignKey string `json:"sign_key,omitempty"`
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp,omitempty"`
UseEncryption bool `json:"use_encryption"` UseEncryption bool `json:"use_encryption,omitempty"`
UseCompression bool `json:"use_compression"` UseCompression bool `json:"use_compression,omitempty"`
} }
type NewVisitorConnResp struct { type NewVisitorConnResp struct {
ProxyName string `json:"proxy_name"` ProxyName string `json:"proxy_name,omitempty"`
Error string `json:"error"` Error string `json:"error,omitempty"`
} }
type Ping struct { type Ping struct {
PrivilegeKey string `json:"privilege_key"` PrivilegeKey string `json:"privilege_key,omitempty"`
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp,omitempty"`
} }
type Pong struct { type Pong struct {
Error string `json:"error"` Error string `json:"error,omitempty"`
} }
type UDPPacket struct { type UDPPacket struct {
Content string `json:"c"` Content string `json:"c,omitempty"`
LocalAddr *net.UDPAddr `json:"l"` LocalAddr *net.UDPAddr `json:"l,omitempty"`
RemoteAddr *net.UDPAddr `json:"r"` RemoteAddr *net.UDPAddr `json:"r,omitempty"`
} }
type NatHoleVisitor struct { type NatHoleVisitor struct {
ProxyName string `json:"proxy_name"` ProxyName string `json:"proxy_name,omitempty"`
SignKey string `json:"sign_key"` SignKey string `json:"sign_key,omitempty"`
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp,omitempty"`
} }
type NatHoleClient struct { type NatHoleClient struct {
ProxyName string `json:"proxy_name"` ProxyName string `json:"proxy_name,omitempty"`
Sid string `json:"sid"` Sid string `json:"sid,omitempty"`
} }
type NatHoleResp struct { type NatHoleResp struct {
Sid string `json:"sid"` Sid string `json:"sid,omitempty"`
VisitorAddr string `json:"visitor_addr"` VisitorAddr string `json:"visitor_addr,omitempty"`
ClientAddr string `json:"client_addr"` ClientAddr string `json:"client_addr,omitempty"`
Error string `json:"error"` Error string `json:"error,omitempty"`
} }
type NatHoleClientDetectOK struct { type NatHoleClientDetectOK struct{}
}
type NatHoleSid struct { type NatHoleSid struct {
Sid string `json:"sid"` Sid string `json:"sid,omitempty"`
} }

View File

@@ -7,15 +7,15 @@ import (
"sync" "sync"
"time" "time"
"github.com/fatedier/golib/errors"
"github.com/fatedier/golib/pool"
"github.com/fatedier/frp/pkg/msg" "github.com/fatedier/frp/pkg/msg"
"github.com/fatedier/frp/pkg/util/log" "github.com/fatedier/frp/pkg/util/log"
"github.com/fatedier/frp/pkg/util/util" "github.com/fatedier/frp/pkg/util/util"
"github.com/fatedier/golib/errors"
"github.com/fatedier/golib/pool"
) )
// Timeout seconds. // NatHoleTimeout seconds.
var NatHoleTimeout int64 = 10 var NatHoleTimeout int64 = 10
type SidRequest struct { type SidRequest struct {
@@ -107,7 +107,7 @@ func (nc *Controller) HandleVisitor(m *msg.NatHoleVisitor, raddr *net.UDPAddr) {
session := &Session{ session := &Session{
Sid: sid, Sid: sid,
VisitorAddr: raddr, VisitorAddr: raddr,
NotifyCh: make(chan struct{}, 0), NotifyCh: make(chan struct{}),
} }
nc.mu.Lock() nc.mu.Lock()
clientCfg, ok := nc.clientCfgs[m.ProxyName] clientCfg, ok := nc.clientCfgs[m.ProxyName]
@@ -115,14 +115,14 @@ func (nc *Controller) HandleVisitor(m *msg.NatHoleVisitor, raddr *net.UDPAddr) {
nc.mu.Unlock() nc.mu.Unlock()
errInfo := fmt.Sprintf("xtcp server for [%s] doesn't exist", m.ProxyName) errInfo := fmt.Sprintf("xtcp server for [%s] doesn't exist", m.ProxyName)
log.Debug(errInfo) log.Debug(errInfo)
nc.listener.WriteToUDP(nc.GenNatHoleResponse(nil, errInfo), raddr) _, _ = nc.listener.WriteToUDP(nc.GenNatHoleResponse(nil, errInfo), raddr)
return return
} }
if m.SignKey != util.GetAuthKey(clientCfg.Sk, m.Timestamp) { if m.SignKey != util.GetAuthKey(clientCfg.Sk, m.Timestamp) {
nc.mu.Unlock() nc.mu.Unlock()
errInfo := fmt.Sprintf("xtcp connection of [%s] auth failed", m.ProxyName) errInfo := fmt.Sprintf("xtcp connection of [%s] auth failed", m.ProxyName)
log.Debug(errInfo) log.Debug(errInfo)
nc.listener.WriteToUDP(nc.GenNatHoleResponse(nil, errInfo), raddr) _, _ = nc.listener.WriteToUDP(nc.GenNatHoleResponse(nil, errInfo), raddr)
return return
} }
@@ -151,7 +151,7 @@ func (nc *Controller) HandleVisitor(m *msg.NatHoleVisitor, raddr *net.UDPAddr) {
case <-session.NotifyCh: case <-session.NotifyCh:
resp := nc.GenNatHoleResponse(session, "") resp := nc.GenNatHoleResponse(session, "")
log.Trace("send nat hole response to visitor") log.Trace("send nat hole response to visitor")
nc.listener.WriteToUDP(resp, raddr) _, _ = nc.listener.WriteToUDP(resp, raddr)
case <-time.After(time.Duration(NatHoleTimeout) * time.Second): case <-time.After(time.Duration(NatHoleTimeout) * time.Second):
return return
} }
@@ -169,7 +169,7 @@ func (nc *Controller) HandleClient(m *msg.NatHoleClient, raddr *net.UDPAddr) {
resp := nc.GenNatHoleResponse(session, "") resp := nc.GenNatHoleResponse(session, "")
log.Trace("send nat hole response to client") log.Trace("send nat hole response to client")
nc.listener.WriteToUDP(resp, raddr) _, _ = nc.listener.WriteToUDP(resp, raddr)
} }
func (nc *Controller) GenNatHoleResponse(session *Session, errInfo string) []byte { func (nc *Controller) GenNatHoleResponse(session *Session, errInfo string) []byte {

View File

@@ -60,7 +60,7 @@ func NewHTTP2HTTPSPlugin(params map[string]string) (Plugin, error) {
listener := NewProxyListener() listener := NewProxyListener()
p := &HTTPS2HTTPPlugin{ p := &HTTP2HTTPSPlugin{
localAddr: localAddr, localAddr: localAddr,
hostHeaderRewrite: hostHeaderRewrite, hostHeaderRewrite: hostHeaderRewrite,
headers: headers, headers: headers,
@@ -86,17 +86,20 @@ func NewHTTP2HTTPSPlugin(params map[string]string) (Plugin, error) {
} }
p.s = &http.Server{ p.s = &http.Server{
Handler: rp, Handler: rp,
ReadHeaderTimeout: 0,
} }
go p.s.Serve(listener) go func() {
_ = p.s.Serve(listener)
}()
return p, nil return p, nil
} }
func (p *HTTP2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) { func (p *HTTP2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn) wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
p.l.PutConn(wrapConn) _ = p.l.PutConn(wrapConn)
} }
func (p *HTTP2HTTPSPlugin) Name() string { func (p *HTTP2HTTPSPlugin) Name() string {

View File

@@ -22,10 +22,10 @@ import (
"net/http" "net/http"
"strings" "strings"
frpNet "github.com/fatedier/frp/pkg/util/net"
frpIo "github.com/fatedier/golib/io" frpIo "github.com/fatedier/golib/io"
gnet "github.com/fatedier/golib/net" gnet "github.com/fatedier/golib/net"
frpNet "github.com/fatedier/frp/pkg/util/net"
) )
const PluginHTTPProxy = "http_proxy" const PluginHTTPProxy = "http_proxy"
@@ -56,7 +56,9 @@ func NewHTTPProxyPlugin(params map[string]string) (Plugin, error) {
Handler: hp, Handler: hp,
} }
go hp.s.Serve(listener) go func() {
_ = hp.s.Serve(listener)
}()
return hp, nil return hp, nil
} }
@@ -86,8 +88,7 @@ func (hp *HTTPProxy) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBuf
return return
} }
hp.l.PutConn(sc) _ = hp.l.PutConn(sc)
return
} }
func (hp *HTTPProxy) Close() error { func (hp *HTTPProxy) Close() error {
@@ -153,7 +154,7 @@ func (hp *HTTPProxy) ConnectHandler(rw http.ResponseWriter, req *http.Request) {
client.Close() client.Close()
return return
} }
client.Write([]byte("HTTP/1.1 200 OK\r\n\r\n")) _, _ = client.Write([]byte("HTTP/1.1 200 OK\r\n\r\n"))
go frpIo.Join(remote, client) go frpIo.Join(remote, client)
} }
@@ -188,7 +189,10 @@ func (hp *HTTPProxy) handleConnectReq(req *http.Request, rwc io.ReadWriteCloser)
defer rwc.Close() defer rwc.Close()
if ok := hp.Auth(req); !ok { if ok := hp.Auth(req); !ok {
res := getBadResponse() res := getBadResponse()
res.Write(rwc) _ = res.Write(rwc)
if res.Body != nil {
res.Body.Close()
}
return return
} }
@@ -200,10 +204,10 @@ func (hp *HTTPProxy) handleConnectReq(req *http.Request, rwc io.ReadWriteCloser)
ProtoMajor: 1, ProtoMajor: 1,
ProtoMinor: 1, ProtoMinor: 1,
} }
res.Write(rwc) _ = res.Write(rwc)
return return
} }
rwc.Write([]byte("HTTP/1.1 200 OK\r\n\r\n")) _, _ = rwc.Write([]byte("HTTP/1.1 200 OK\r\n\r\n"))
frpIo.Join(remote, rwc) frpIo.Join(remote, rwc)
} }

View File

@@ -23,6 +23,7 @@ import (
"net/http/httputil" "net/http/httputil"
"strings" "strings"
"github.com/fatedier/frp/pkg/transport"
frpNet "github.com/fatedier/frp/pkg/util/net" frpNet "github.com/fatedier/frp/pkg/util/net"
) )
@@ -58,12 +59,6 @@ func NewHTTPS2HTTPPlugin(params map[string]string) (Plugin, error) {
} }
} }
if crtPath == "" {
return nil, fmt.Errorf("plugin_crt_path is required")
}
if keyPath == "" {
return nil, fmt.Errorf("plugin_key_path is required")
}
if localAddr == "" { if localAddr == "" {
return nil, fmt.Errorf("plugin_local_addr is required") return nil, fmt.Errorf("plugin_local_addr is required")
} }
@@ -96,13 +91,24 @@ func NewHTTPS2HTTPPlugin(params map[string]string) (Plugin, error) {
Handler: rp, Handler: rp,
} }
tlsConfig, err := p.genTLSConfig() var (
tlsConfig *tls.Config
err error
)
if crtPath != "" || keyPath != "" {
tlsConfig, err = p.genTLSConfig()
} else {
tlsConfig, err = transport.NewServerTLSConfig("", "", "")
tlsConfig.InsecureSkipVerify = true
}
if err != nil { if err != nil {
return nil, fmt.Errorf("gen TLS config error: %v", err) return nil, fmt.Errorf("gen TLS config error: %v", err)
} }
ln := tls.NewListener(listener, tlsConfig) ln := tls.NewListener(listener, tlsConfig)
go p.s.Serve(ln) go func() {
_ = p.s.Serve(ln)
}()
return p, nil return p, nil
} }
@@ -118,7 +124,7 @@ func (p *HTTPS2HTTPPlugin) genTLSConfig() (*tls.Config, error) {
func (p *HTTPS2HTTPPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) { func (p *HTTPS2HTTPPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn) wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
p.l.PutConn(wrapConn) _ = p.l.PutConn(wrapConn)
} }
func (p *HTTPS2HTTPPlugin) Name() string { func (p *HTTPS2HTTPPlugin) Name() string {

View File

@@ -23,6 +23,7 @@ import (
"net/http/httputil" "net/http/httputil"
"strings" "strings"
"github.com/fatedier/frp/pkg/transport"
frpNet "github.com/fatedier/frp/pkg/util/net" frpNet "github.com/fatedier/frp/pkg/util/net"
) )
@@ -58,12 +59,6 @@ func NewHTTPS2HTTPSPlugin(params map[string]string) (Plugin, error) {
} }
} }
if crtPath == "" {
return nil, fmt.Errorf("plugin_crt_path is required")
}
if keyPath == "" {
return nil, fmt.Errorf("plugin_key_path is required")
}
if localAddr == "" { if localAddr == "" {
return nil, fmt.Errorf("plugin_local_addr is required") return nil, fmt.Errorf("plugin_local_addr is required")
} }
@@ -101,13 +96,24 @@ func NewHTTPS2HTTPSPlugin(params map[string]string) (Plugin, error) {
Handler: rp, Handler: rp,
} }
tlsConfig, err := p.genTLSConfig() var (
tlsConfig *tls.Config
err error
)
if crtPath != "" || keyPath != "" {
tlsConfig, err = p.genTLSConfig()
} else {
tlsConfig, err = transport.NewServerTLSConfig("", "", "")
tlsConfig.InsecureSkipVerify = true
}
if err != nil { if err != nil {
return nil, fmt.Errorf("gen TLS config error: %v", err) return nil, fmt.Errorf("gen TLS config error: %v", err)
} }
ln := tls.NewListener(listener, tlsConfig) ln := tls.NewListener(listener, tlsConfig)
go p.s.Serve(ln) go func() {
_ = p.s.Serve(ln)
}()
return p, nil return p, nil
} }
@@ -123,11 +129,11 @@ func (p *HTTPS2HTTPSPlugin) genTLSConfig() (*tls.Config, error) {
func (p *HTTPS2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) { func (p *HTTPS2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn) wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
p.l.PutConn(wrapConn) _ = p.l.PutConn(wrapConn)
} }
func (p *HTTPS2HTTPSPlugin) Name() string { func (p *HTTPS2HTTPSPlugin) Name() string {
return PluginHTTPS2HTTP return PluginHTTPS2HTTPS
} }
func (p *HTTPS2HTTPSPlugin) Close() error { func (p *HTTPS2HTTPSPlugin) Close() error {

View File

@@ -16,13 +16,12 @@ package plugin
import ( import (
"io" "io"
"io/ioutil"
"log" "log"
"net" "net"
frpNet "github.com/fatedier/frp/pkg/util/net"
gosocks5 "github.com/armon/go-socks5" gosocks5 "github.com/armon/go-socks5"
frpNet "github.com/fatedier/frp/pkg/util/net"
) )
const PluginSocks5 = "socks5" const PluginSocks5 = "socks5"
@@ -33,9 +32,6 @@ func init() {
type Socks5Plugin struct { type Socks5Plugin struct {
Server *gosocks5.Server Server *gosocks5.Server
user string
passwd string
} }
func NewSocks5Plugin(params map[string]string) (p Plugin, err error) { func NewSocks5Plugin(params map[string]string) (p Plugin, err error) {
@@ -43,7 +39,7 @@ func NewSocks5Plugin(params map[string]string) (p Plugin, err error) {
passwd := params["plugin_passwd"] passwd := params["plugin_passwd"]
cfg := &gosocks5.Config{ cfg := &gosocks5.Config{
Logger: log.New(ioutil.Discard, "", log.LstdFlags), Logger: log.New(io.Discard, "", log.LstdFlags),
} }
if user != "" || passwd != "" { if user != "" || passwd != "" {
cfg.Credentials = gosocks5.StaticCredentials(map[string]string{user: passwd}) cfg.Credentials = gosocks5.StaticCredentials(map[string]string{user: passwd})
@@ -57,7 +53,7 @@ func NewSocks5Plugin(params map[string]string) (p Plugin, err error) {
func (sp *Socks5Plugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) { func (sp *Socks5Plugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
defer conn.Close() defer conn.Close()
wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn) wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
sp.Server.ServeConn(wrapConn) _ = sp.Server.ServeConn(wrapConn)
} }
func (sp *Socks5Plugin) Name() string { func (sp *Socks5Plugin) Name() string {

View File

@@ -19,9 +19,9 @@ import (
"net" "net"
"net/http" "net/http"
frpNet "github.com/fatedier/frp/pkg/util/net"
"github.com/gorilla/mux" "github.com/gorilla/mux"
frpNet "github.com/fatedier/frp/pkg/util/net"
) )
const PluginStaticFile = "static_file" const PluginStaticFile = "static_file"
@@ -69,13 +69,15 @@ func NewStaticFilePlugin(params map[string]string) (Plugin, error) {
sp.s = &http.Server{ sp.s = &http.Server{
Handler: router, Handler: router,
} }
go sp.s.Serve(listener) go func() {
_ = sp.s.Serve(listener)
}()
return sp, nil return sp, nil
} }
func (sp *StaticFilePlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) { func (sp *StaticFilePlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn) wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
sp.l.PutConn(wrapConn) _ = sp.l.PutConn(wrapConn)
} }
func (sp *StaticFilePlugin) Name() string { func (sp *StaticFilePlugin) Name() string {

View File

@@ -57,7 +57,9 @@ func (uds *UnixDomainSocketPlugin) Handle(conn io.ReadWriteCloser, realConn net.
return return
} }
if len(extraBufToLocal) > 0 { if len(extraBufToLocal) > 0 {
localConn.Write(extraBufToLocal) if _, err := localConn.Write(extraBufToLocal); err != nil {
return
}
} }
frpIo.Join(localConn, conn) frpIo.Join(localConn, conn)

View File

@@ -20,7 +20,7 @@ import (
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io"
"net/http" "net/http"
"net/url" "net/url"
"reflect" "reflect"
@@ -43,12 +43,12 @@ type httpPlugin struct {
} }
func NewHTTPPluginOptions(options HTTPPluginOptions) Plugin { func NewHTTPPluginOptions(options HTTPPluginOptions) Plugin {
var url = fmt.Sprintf("%s%s", options.Addr, options.Path) url := fmt.Sprintf("%s%s", options.Addr, options.Path)
var client *http.Client var client *http.Client
if strings.HasPrefix(url, "https://") { if strings.HasPrefix(url, "https://") {
tr := &http.Transport{ tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: options.TLSVerify == false}, TLSClientConfig: &tls.Config{InsecureSkipVerify: !options.TLSVerify},
} }
client = &http.Client{Transport: tr} client = &http.Client{Transport: tr}
} else { } else {
@@ -116,7 +116,7 @@ func (p *httpPlugin) do(ctx context.Context, r *Request, res *Response) error {
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return fmt.Errorf("do http request error code: %d", resp.StatusCode) return fmt.Errorf("do http request error code: %d", resp.StatusCode)
} }
buf, err = ioutil.ReadAll(resp.Body) buf, err = io.ReadAll(resp.Body)
if err != nil { if err != nil {
return err return err
} }

Some files were not shown because too many files have changed in this diff Show More