mirror of
https://gitee.com/IrisVega/frp.git
synced 2024-11-01 22:31:29 +08:00
commit
143750901e
@ -2,16 +2,14 @@ version: 2
|
|||||||
jobs:
|
jobs:
|
||||||
go-version-latest:
|
go-version-latest:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.16-node
|
- image: cimg/go:1.17-node
|
||||||
working_directory: /go/src/github.com/fatedier/frp
|
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: make
|
- run: make
|
||||||
- run: make alltest
|
- run: make alltest
|
||||||
go-version-last:
|
go-version-last:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/golang:1.15-node
|
- image: cimg/go:1.16-node
|
||||||
working_directory: /go/src/github.com/fatedier/frp
|
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- run: make
|
- run: make
|
||||||
|
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: [fatedier]
|
44
.github/ISSUE_TEMPLATE/bug-report.md
vendored
44
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@ -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
77
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal 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"
|
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -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.
|
|
||||||
|
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
22
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -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. -->
|
|
36
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
36
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal 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"
|
2
.github/workflows/build-and-push-image.yml
vendored
2
.github/workflows/build-and-push-image.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
|||||||
- name: Set up Go 1.x
|
- name: Set up Go 1.x
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: 1.16
|
go-version: 1.17
|
||||||
|
|
||||||
- run: |
|
- run: |
|
||||||
# https://github.com/actions/setup-go/issues/107
|
# https://github.com/actions/setup-go/issues/107
|
||||||
|
2
.github/workflows/goreleaser.yml
vendored
2
.github/workflows/goreleaser.yml
vendored
@ -15,7 +15,7 @@ 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.17
|
||||||
|
|
||||||
- run: |
|
- run: |
|
||||||
# https://github.com/actions/setup-go/issues/107
|
# https://github.com/actions/setup-go/issues/107
|
||||||
|
8
.github/workflows/stale.yml
vendored
8
.github/workflows/stale.yml
vendored
@ -15,12 +15,12 @@ jobs:
|
|||||||
- uses: actions/stale@v3
|
- uses: actions/stale@v3
|
||||||
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: 'Issues go stale after 30d of inactivity. Stale issues 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 }}
|
||||||
|
3
Makefile
3
Makefile
@ -12,9 +12,6 @@ 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 ./...
|
||||||
|
11
Release.md
11
Release.md
@ -1,5 +1,8 @@
|
|||||||
### Fix
|
### New
|
||||||
|
|
||||||
* Plugin `https2https` not work.
|
* Add `/healthz` API.
|
||||||
* `context canceled` problem for `http_proxy` plugin when multiple requests reuse same connection.
|
* frpc support `disable_custom_tls_first_byte` .If set true, frpc will not send custom header byte.
|
||||||
* In some cases, frps can't get server name for `https` proxy.
|
|
||||||
|
### Improve
|
||||||
|
|
||||||
|
* Use go standard embed package instead of statik.
|
||||||
|
@ -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
14
assets/frpc/embed.go
Normal 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)
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
14
assets/frps/embed.go
Normal file
14
assets/frps/embed.go
Normal 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)
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
@ -37,7 +37,8 @@ func (svr *Service) RunAdminServer(address string) (err error) {
|
|||||||
user, passwd := svr.cfg.AdminUser, svr.cfg.AdminPwd
|
user, passwd := svr.cfg.AdminUser, svr.cfg.AdminPwd
|
||||||
router.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware)
|
router.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware)
|
||||||
|
|
||||||
// api, see dashboard_api.go
|
// api, see admin_api.go
|
||||||
|
router.HandleFunc("/healthz", svr.healthz)
|
||||||
router.HandleFunc("/api/reload", svr.apiReload).Methods("GET")
|
router.HandleFunc("/api/reload", svr.apiReload).Methods("GET")
|
||||||
router.HandleFunc("/api/status", svr.apiStatus).Methods("GET")
|
router.HandleFunc("/api/status", svr.apiStatus).Methods("GET")
|
||||||
router.HandleFunc("/api/config", svr.apiGetConfig).Methods("GET")
|
router.HandleFunc("/api/config", svr.apiGetConfig).Methods("GET")
|
||||||
|
@ -17,8 +17,9 @@ package client
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -32,6 +33,11 @@ type GeneralResponse struct {
|
|||||||
Msg string
|
Msg string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// /healthz
|
||||||
|
func (svr *Service) healthz(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(200)
|
||||||
|
}
|
||||||
|
|
||||||
// GET api/reload
|
// 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}
|
||||||
@ -251,7 +257,7 @@ func (svr *Service) apiPutConfig(w http.ResponseWriter, r *http.Request) {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
// 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)
|
||||||
@ -268,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()
|
||||||
@ -307,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), 0644)
|
||||||
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)
|
||||||
|
@ -182,9 +182,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()
|
||||||
}
|
}
|
||||||
@ -228,7 +235,7 @@ func (ctl *Control) connectServer() (conn net.Conn, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
address := net.JoinHostPort(ctl.clientCfg.ServerAddr, strconv.Itoa(ctl.clientCfg.ServerPort))
|
address := net.JoinHostPort(ctl.clientCfg.ServerAddr, strconv.Itoa(ctl.clientCfg.ServerPort))
|
||||||
conn, err = frpNet.ConnectServerByProxyWithTLS(ctl.clientCfg.HTTPProxy, ctl.clientCfg.Protocol, address, tlsConfig)
|
conn, err = frpNet.ConnectServerByProxyWithTLS(ctl.clientCfg.HTTPProxy, ctl.clientCfg.Protocol, address, tlsConfig, ctl.clientCfg.DisableCustomTLSFirstByte)
|
||||||
|
|
||||||
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)
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
@ -162,7 +161,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)
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -401,7 +400,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)
|
||||||
|
@ -19,7 +19,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -124,13 +124,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)
|
||||||
}
|
}
|
||||||
@ -232,7 +229,7 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
address := net.JoinHostPort(svr.cfg.ServerAddr, strconv.Itoa(svr.cfg.ServerPort))
|
address := net.JoinHostPort(svr.cfg.ServerAddr, strconv.Itoa(svr.cfg.ServerPort))
|
||||||
conn, err = frpNet.ConnectServerByProxyWithTLS(svr.cfg.HTTPProxy, svr.cfg.Protocol, address, tlsConfig)
|
conn, err = frpNet.ConnectServerByProxyWithTLS(svr.cfg.HTTPProxy, svr.cfg.Protocol, address, tlsConfig, svr.cfg.DisableCustomTLSFirstByte)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -249,7 +246,7 @@ 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 = 20 * 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
|
||||||
@ -315,9 +312,13 @@ func (svr *Service) ReloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
if svr.ctl != nil {
|
if svr.ctl != nil {
|
||||||
svr.ctl.Close()
|
svr.ctl.GracefulClose(d)
|
||||||
}
|
}
|
||||||
svr.cancel()
|
svr.cancel()
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -308,7 +307,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)
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
_ "github.com/fatedier/frp/assets/frpc/statik"
|
_ "github.com/fatedier/frp/assets/frpc"
|
||||||
"github.com/fatedier/frp/cmd/frpc/sub"
|
"github.com/fatedier/frp/cmd/frpc/sub"
|
||||||
|
|
||||||
"github.com/fatedier/golib/crypto"
|
"github.com/fatedier/golib/crypto"
|
||||||
|
@ -17,7 +17,7 @@ package sub
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -76,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
|
||||||
}
|
}
|
||||||
|
@ -124,8 +124,7 @@ func handleSignal(svr *client.Service) {
|
|||||||
ch := make(chan os.Signal)
|
ch := make(chan os.Signal)
|
||||||
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(kcpDoneCh)
|
close(kcpDoneCh)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -77,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
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -105,6 +105,10 @@ udp_packet_size = 1500
|
|||||||
# include other config files for proxies.
|
# include other config files for proxies.
|
||||||
# includes = ./confd/*.ini
|
# 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
|
||||||
|
|
||||||
# '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]
|
||||||
@ -181,9 +185,9 @@ 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
|
||||||
host_header_rewrite = example.com
|
host_header_rewrite = example.com
|
||||||
|
1
go.mod
1
go.mod
@ -21,7 +21,6 @@ require (
|
|||||||
github.com/pires/go-proxyproto v0.5.0
|
github.com/pires/go-proxyproto v0.5.0
|
||||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||||
github.com/prometheus/client_golang v1.11.0
|
github.com/prometheus/client_golang v1.11.0
|
||||||
github.com/rakyll/statik v0.1.1
|
|
||||||
github.com/rodaine/table v1.0.1
|
github.com/rodaine/table v1.0.1
|
||||||
github.com/spf13/cobra v1.1.3
|
github.com/spf13/cobra v1.1.3
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
|
2
go.sum
2
go.sum
@ -332,8 +332,6 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
|||||||
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
|
||||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
github.com/rakyll/statik v0.1.1 h1:fCLHsIMajHqD5RKigbFXpvX3dN7c80Pm12+NCrI3kvg=
|
|
||||||
github.com/rakyll/statik v0.1.1/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
|
|
||||||
github.com/rodaine/table v1.0.1 h1:U/VwCnUxlVYxw8+NJiLIuCxA/xa6jL38MY3FYysVWWQ=
|
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/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/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
|
@ -5,7 +5,7 @@ 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 get -u github.com/onsi/ginkgo/ginkgo
|
go install github.com/onsi/ginkgo/ginkgo@latest
|
||||||
fi
|
fi
|
||||||
|
|
||||||
debug=false
|
debug=false
|
||||||
|
@ -124,6 +124,9 @@ type ClientCommonConf struct {
|
|||||||
// TLSServerName specifices the custom server name of tls certificate. By
|
// TLSServerName specifices 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.
|
||||||
|
@ -17,7 +17,6 @@ package config
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
@ -77,7 +76,7 @@ func getIncludeContents(paths []string) ([]byte, error) {
|
|||||||
if _, err := os.Stat(absDir); os.IsNotExist(err) {
|
if _, err := os.Stat(absDir); os.IsNotExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
files, err := ioutil.ReadDir(absDir)
|
files, err := os.ReadDir(absDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
@ -67,7 +66,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
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ package plugin
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
@ -43,7 +42,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})
|
||||||
|
@ -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"
|
||||||
@ -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
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,8 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"io/ioutil"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newCustomTLSKeyPair(certfile, keyfile string) (*tls.Certificate, error) {
|
func newCustomTLSKeyPair(certfile, keyfile string) (*tls.Certificate, error) {
|
||||||
@ -47,7 +47,7 @@ func newRandomTLSKeyPair() *tls.Certificate {
|
|||||||
func newCertPool(caPath string) (*x509.CertPool, error) {
|
func newCertPool(caPath string) (*x509.CertPool, error) {
|
||||||
pool := x509.NewCertPool()
|
pool := x509.NewCertPool()
|
||||||
|
|
||||||
caCrt, err := ioutil.ReadFile(caPath)
|
caCrt, err := os.ReadFile(caPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -228,7 +228,7 @@ func ConnectServerByProxy(proxyURL string, protocol string, addr string) (c net.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConnectServerByProxyWithTLS(proxyURL string, protocol string, addr string, tlsConfig *tls.Config) (c net.Conn, err error) {
|
func ConnectServerByProxyWithTLS(proxyURL string, protocol string, addr string, tlsConfig *tls.Config, disableCustomTLSHeadByte bool) (c net.Conn, err error) {
|
||||||
c, err = ConnectServerByProxy(proxyURL, protocol, addr)
|
c, err = ConnectServerByProxy(proxyURL, protocol, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -238,6 +238,6 @@ func ConnectServerByProxyWithTLS(proxyURL string, protocol string, addr string,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c = WrapTLSClientConn(c, tlsConfig)
|
c = WrapTLSClientConn(c, tlsConfig, disableCustomTLSHeadByte)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -27,13 +27,18 @@ var (
|
|||||||
FRPTLSHeadByte = 0x17
|
FRPTLSHeadByte = 0x17
|
||||||
)
|
)
|
||||||
|
|
||||||
func WrapTLSClientConn(c net.Conn, tlsConfig *tls.Config) (out net.Conn) {
|
func WrapTLSClientConn(c net.Conn, tlsConfig *tls.Config, disableCustomTLSHeadByte bool) (out net.Conn) {
|
||||||
|
if !disableCustomTLSHeadByte {
|
||||||
c.Write([]byte{byte(FRPTLSHeadByte)})
|
c.Write([]byte{byte(FRPTLSHeadByte)})
|
||||||
|
}
|
||||||
out = tls.Client(c, tlsConfig)
|
out = tls.Client(c, tlsConfig)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckAndEnableTLSServerConnWithTimeout(c net.Conn, tlsConfig *tls.Config, tlsOnly bool, timeout time.Duration) (out net.Conn, err error) {
|
func CheckAndEnableTLSServerConnWithTimeout(
|
||||||
|
c net.Conn, tlsConfig *tls.Config, tlsOnly bool, timeout time.Duration,
|
||||||
|
) (out net.Conn, isTLS bool, custom bool, err error) {
|
||||||
|
|
||||||
sc, r := gnet.NewSharedConnSize(c, 2)
|
sc, r := gnet.NewSharedConnSize(c, 2)
|
||||||
buf := make([]byte, 1)
|
buf := make([]byte, 1)
|
||||||
var n int
|
var n int
|
||||||
@ -46,6 +51,11 @@ func CheckAndEnableTLSServerConnWithTimeout(c net.Conn, tlsConfig *tls.Config, t
|
|||||||
|
|
||||||
if n == 1 && int(buf[0]) == FRPTLSHeadByte {
|
if n == 1 && int(buf[0]) == FRPTLSHeadByte {
|
||||||
out = tls.Server(c, tlsConfig)
|
out = tls.Server(c, tlsConfig)
|
||||||
|
isTLS = true
|
||||||
|
custom = true
|
||||||
|
} else if n == 1 && int(buf[0]) == 0x16 {
|
||||||
|
out = tls.Server(sc, tlsConfig)
|
||||||
|
isTLS = true
|
||||||
} else {
|
} else {
|
||||||
if tlsOnly {
|
if tlsOnly {
|
||||||
err = fmt.Errorf("non-TLS connection received on a TlsOnly server")
|
err = fmt.Errorf("non-TLS connection received on a TlsOnly server")
|
||||||
|
@ -19,7 +19,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version string = "0.37.1"
|
var version string = "0.38.0"
|
||||||
|
|
||||||
func Full() string {
|
func Full() string {
|
||||||
return version
|
return version
|
||||||
|
@ -16,8 +16,9 @@ package vhost
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
frpLog "github.com/fatedier/frp/pkg/util/log"
|
frpLog "github.com/fatedier/frp/pkg/util/log"
|
||||||
"github.com/fatedier/frp/pkg/util/version"
|
"github.com/fatedier/frp/pkg/util/version"
|
||||||
@ -57,7 +58,7 @@ func getNotFoundPageContent() []byte {
|
|||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if NotFoundPagePath != "" {
|
if NotFoundPagePath != "" {
|
||||||
buf, err = ioutil.ReadFile(NotFoundPagePath)
|
buf, err = os.ReadFile(NotFoundPagePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
frpLog.Warn("read custom 404 page error: %v", err)
|
frpLog.Warn("read custom 404 page error: %v", err)
|
||||||
buf = []byte(NotFound)
|
buf = []byte(NotFound)
|
||||||
@ -80,7 +81,7 @@ func notFoundResponse() *http.Response {
|
|||||||
ProtoMajor: 1,
|
ProtoMajor: 1,
|
||||||
ProtoMinor: 0,
|
ProtoMinor: 0,
|
||||||
Header: header,
|
Header: header,
|
||||||
Body: ioutil.NopCloser(bytes.NewReader(getNotFoundPageContent())),
|
Body: io.NopCloser(bytes.NewReader(getNotFoundPageContent())),
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ func (svr *Service) RunDashboardServer(address string) (err error) {
|
|||||||
router.HandleFunc("/api/proxy/{type}", svr.APIProxyByType).Methods("GET")
|
router.HandleFunc("/api/proxy/{type}", svr.APIProxyByType).Methods("GET")
|
||||||
router.HandleFunc("/api/proxy/{type}/{name}", svr.APIProxyByTypeAndName).Methods("GET")
|
router.HandleFunc("/api/proxy/{type}/{name}", svr.APIProxyByTypeAndName).Methods("GET")
|
||||||
router.HandleFunc("/api/traffic/{name}", svr.APIProxyTraffic).Methods("GET")
|
router.HandleFunc("/api/traffic/{name}", svr.APIProxyTraffic).Methods("GET")
|
||||||
|
router.HandleFunc("/healthz", svr.Healthz)
|
||||||
|
|
||||||
// view
|
// view
|
||||||
router.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET")
|
router.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET")
|
||||||
|
@ -51,6 +51,11 @@ type serverInfoResp struct {
|
|||||||
ProxyTypeCounts map[string]int64 `json:"proxy_type_count"`
|
ProxyTypeCounts map[string]int64 `json:"proxy_type_count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// /healthz
|
||||||
|
func (svr *Service) Healthz(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(200)
|
||||||
|
}
|
||||||
|
|
||||||
// api/serverinfo
|
// api/serverinfo
|
||||||
func (svr *Service) APIServerInfo(w http.ResponseWriter, r *http.Request) {
|
func (svr *Service) APIServerInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
res := GeneralResponse{Code: 200}
|
res := GeneralResponse{Code: 200}
|
||||||
|
@ -19,7 +19,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
@ -258,8 +258,9 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// frp tls listener
|
// frp tls listener
|
||||||
svr.tlsListener = svr.muxer.Listen(1, 1, func(data []byte) bool {
|
svr.tlsListener = svr.muxer.Listen(2, 1, func(data []byte) bool {
|
||||||
return int(data[0]) == frpNet.FRPTLSHeadByte
|
// tls first byte can be 0x16 only when vhost https port is not same with bind port
|
||||||
|
return int(data[0]) == frpNet.FRPTLSHeadByte || int(data[0]) == 0x16
|
||||||
})
|
})
|
||||||
|
|
||||||
// Create nat hole controller.
|
// Create nat hole controller.
|
||||||
@ -279,11 +280,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
|
|||||||
// Create dashboard web server.
|
// Create dashboard web server.
|
||||||
if cfg.DashboardPort > 0 {
|
if cfg.DashboardPort > 0 {
|
||||||
// Init dashboard assets
|
// Init dashboard assets
|
||||||
err = assets.Load(cfg.AssetsDir)
|
assets.Load(cfg.AssetsDir)
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("Load assets error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
address := net.JoinHostPort(cfg.DashboardAddr, strconv.Itoa(cfg.DashboardPort))
|
address := net.JoinHostPort(cfg.DashboardAddr, strconv.Itoa(cfg.DashboardPort))
|
||||||
err = svr.RunDashboardServer(address)
|
err = svr.RunDashboardServer(address)
|
||||||
@ -395,20 +392,21 @@ func (svr *Service) HandleListener(l net.Listener) {
|
|||||||
|
|
||||||
log.Trace("start check TLS connection...")
|
log.Trace("start check TLS connection...")
|
||||||
originConn := c
|
originConn := c
|
||||||
c, err = frpNet.CheckAndEnableTLSServerConnWithTimeout(c, svr.tlsConfig, svr.cfg.TLSOnly, connReadTimeout)
|
var isTLS, custom bool
|
||||||
|
c, isTLS, custom, err = frpNet.CheckAndEnableTLSServerConnWithTimeout(c, svr.tlsConfig, svr.cfg.TLSOnly, connReadTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("CheckAndEnableTLSServerConnWithTimeout error: %v", err)
|
log.Warn("CheckAndEnableTLSServerConnWithTimeout error: %v", err)
|
||||||
originConn.Close()
|
originConn.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Trace("success check TLS connection")
|
log.Trace("check TLS connection success, isTLS: %v custom: %v", isTLS, custom)
|
||||||
|
|
||||||
// Start a new goroutine for dealing connections.
|
// Start a new goroutine to handle connection.
|
||||||
go func(ctx context.Context, frpConn net.Conn) {
|
go func(ctx context.Context, frpConn net.Conn) {
|
||||||
if svr.cfg.TCPMux {
|
if svr.cfg.TCPMux {
|
||||||
fmuxCfg := fmux.DefaultConfig()
|
fmuxCfg := fmux.DefaultConfig()
|
||||||
fmuxCfg.KeepAliveInterval = 20 * time.Second
|
fmuxCfg.KeepAliveInterval = 20 * time.Second
|
||||||
fmuxCfg.LogOutput = ioutil.Discard
|
fmuxCfg.LogOutput = io.Discard
|
||||||
session, err := fmux.Server(frpConn, fmuxCfg)
|
session, err := fmux.Server(frpConn, fmuxCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("Failed to create mux connection: %v", err)
|
log.Warn("Failed to create mux connection: %v", err)
|
||||||
|
@ -231,4 +231,22 @@ var _ = Describe("[Feature: Client-Server]", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Describe("TLS with disable_custom_tls_first_byte", func() {
|
||||||
|
supportProtocols := []string{"tcp", "kcp", "websocket"}
|
||||||
|
for _, protocol := range supportProtocols {
|
||||||
|
tmp := protocol
|
||||||
|
defineClientServerTest("TLS over "+strings.ToUpper(tmp), f, &generalTestConfigures{
|
||||||
|
server: fmt.Sprintf(`
|
||||||
|
kcp_bind_port = {{ .%s }}
|
||||||
|
protocol = %s
|
||||||
|
`, consts.PortServerName, protocol),
|
||||||
|
client: fmt.Sprintf(`
|
||||||
|
tls_enable = true
|
||||||
|
protocol = %s
|
||||||
|
disable_custom_tls_first_byte = true
|
||||||
|
`, protocol),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -3,6 +3,7 @@ package features
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/util/log"
|
"github.com/fatedier/frp/pkg/util/log"
|
||||||
"github.com/fatedier/frp/test/e2e/framework"
|
"github.com/fatedier/frp/test/e2e/framework"
|
||||||
@ -35,6 +36,7 @@ var _ = Describe("[Feature: Monitor]", func() {
|
|||||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||||
|
|
||||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
|
||||||
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
|
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
|
||||||
r.HTTP().Port(dashboardPort).HTTPPath("/metrics")
|
r.HTTP().Port(dashboardPort).HTTPPath("/metrics")
|
||||||
|
@ -3,7 +3,6 @@ package framework
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
@ -90,7 +89,7 @@ func (f *Framework) BeforeEach() {
|
|||||||
|
|
||||||
f.cleanupHandle = AddCleanupAction(f.AfterEach)
|
f.cleanupHandle = AddCleanupAction(f.AfterEach)
|
||||||
|
|
||||||
dir, err := ioutil.TempDir(os.TempDir(), "frp-e2e-test-*")
|
dir, err := os.MkdirTemp(os.TempDir(), "frp-e2e-test-*")
|
||||||
ExpectNoError(err)
|
ExpectNoError(err)
|
||||||
f.TempDirectory = dir
|
f.TempDirectory = dir
|
||||||
|
|
||||||
@ -260,7 +259,7 @@ func (f *Framework) SetEnvs(envs []string) {
|
|||||||
|
|
||||||
func (f *Framework) WriteTempFile(name string, content string) string {
|
func (f *Framework) WriteTempFile(name string, content string) string {
|
||||||
filePath := filepath.Join(f.TempDirectory, name)
|
filePath := filepath.Join(f.TempDirectory, name)
|
||||||
err := ioutil.WriteFile(filePath, []byte(content), 0766)
|
err := os.WriteFile(filePath, []byte(content), 0766)
|
||||||
ExpectNoError(err)
|
ExpectNoError(err)
|
||||||
return filePath
|
return filePath
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package framework
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
|
|||||||
|
|
||||||
for i := range serverTemplates {
|
for i := range serverTemplates {
|
||||||
path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-server-%d", i))
|
path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-server-%d", i))
|
||||||
err = ioutil.WriteFile(path, []byte(outs[i]), 0666)
|
err = os.WriteFile(path, []byte(outs[i]), 0666)
|
||||||
ExpectNoError(err)
|
ExpectNoError(err)
|
||||||
flog.Trace("[%s] %s", path, outs[i])
|
flog.Trace("[%s] %s", path, outs[i])
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
|
|||||||
for i := range clientTemplates {
|
for i := range clientTemplates {
|
||||||
index := i + len(serverTemplates)
|
index := i + len(serverTemplates)
|
||||||
path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-client-%d", i))
|
path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-client-%d", i))
|
||||||
err = ioutil.WriteFile(path, []byte(outs[index]), 0666)
|
err = os.WriteFile(path, []byte(outs[index]), 0666)
|
||||||
ExpectNoError(err)
|
ExpectNoError(err)
|
||||||
flog.Trace("[%s] %s", path, outs[index])
|
flog.Trace("[%s] %s", path, outs[index])
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ func (f *Framework) RunFrpc(args ...string) (*process.Process, string, error) {
|
|||||||
func (f *Framework) GenerateConfigFile(content string) string {
|
func (f *Framework) GenerateConfigFile(content string) string {
|
||||||
f.configFileIndex++
|
f.configFileIndex++
|
||||||
path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-config-%d", f.configFileIndex))
|
path := filepath.Join(f.TempDirectory, fmt.Sprintf("frp-e2e-config-%d", f.configFileIndex))
|
||||||
err := ioutil.WriteFile(path, []byte(content), 0666)
|
err := os.WriteFile(path, []byte(content), 0666)
|
||||||
ExpectNoError(err)
|
ExpectNoError(err)
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -219,7 +218,7 @@ func (r *Request) sendHTTPRequest(method, urlstr string, host string, headers ma
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret := &Response{Code: resp.StatusCode, Header: resp.Header}
|
ret := &Response{Code: resp.StatusCode, Header: resp.Header}
|
||||||
buf, err := ioutil.ReadAll(resp.Body)
|
buf, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package client
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -125,7 +125,7 @@ func (c *Client) do(req *http.Request) (string, error) {
|
|||||||
if resp.StatusCode != 200 {
|
if resp.StatusCode != 200 {
|
||||||
return "", fmt.Errorf("api status code [%d]", resp.StatusCode)
|
return "", fmt.Errorf("api status 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
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package plugin
|
|||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
plugin "github.com/fatedier/frp/pkg/plugin/server"
|
plugin "github.com/fatedier/frp/pkg/plugin/server"
|
||||||
@ -21,7 +21,7 @@ func NewHTTPPluginServer(port int, newFunc NewPluginRequest, handler PluginHandl
|
|||||||
httpserver.WithTlsConfig(tlsConfig),
|
httpserver.WithTlsConfig(tlsConfig),
|
||||||
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
httpserver.WithHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
r := newFunc()
|
r := newFunc()
|
||||||
buf, err := ioutil.ReadAll(req.Body)
|
buf, err := io.ReadAll(req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
return
|
return
|
||||||
|
Loading…
Reference in New Issue
Block a user