mirror of
https://gitee.com/IrisVega/frp.git
synced 2024-11-01 22:31:29 +08:00
commit
5a6d9f60c2
2
.github/workflows/golangci-lint.yml
vendored
2
.github/workflows/golangci-lint.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
|||||||
uses: golangci/golangci-lint-action@v4
|
uses: golangci/golangci-lint-action@v4
|
||||||
with:
|
with:
|
||||||
# Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version
|
# 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.56
|
version: v1.57
|
||||||
|
|
||||||
# Optional: golangci-lint command line arguments.
|
# Optional: golangci-lint command line arguments.
|
||||||
# args: --issues-exit-code=0
|
# args: --issues-exit-code=0
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
service:
|
service:
|
||||||
golangci-lint-version: 1.56.x # use the fixed version to not introduce new linters unexpectedly
|
golangci-lint-version: 1.57.x # use the fixed version to not introduce new linters unexpectedly
|
||||||
|
|
||||||
run:
|
run:
|
||||||
concurrency: 4
|
concurrency: 4
|
||||||
@ -8,23 +8,6 @@ run:
|
|||||||
build-tags:
|
build-tags:
|
||||||
- integ
|
- integ
|
||||||
- integfuzz
|
- 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:
|
linters:
|
||||||
disable-all: true
|
disable-all: true
|
||||||
@ -136,6 +119,14 @@ issues:
|
|||||||
- unparam
|
- unparam
|
||||||
text: "is always false"
|
text: "is always false"
|
||||||
|
|
||||||
|
exclude-dirs:
|
||||||
|
- genfiles$
|
||||||
|
- vendor$
|
||||||
|
- bin$
|
||||||
|
exclude-files:
|
||||||
|
- ".*\\.pb\\.go"
|
||||||
|
- ".*\\.gen\\.go"
|
||||||
|
|
||||||
# Independently from option `exclude` we use default exclude patterns,
|
# Independently from option `exclude` we use default exclude patterns,
|
||||||
# it can be disabled by this option. To list all
|
# it can be disabled by this option. To list all
|
||||||
# excluded by default patterns execute `golangci-lint run --help`.
|
# excluded by default patterns execute `golangci-lint run --help`.
|
||||||
|
@ -2,7 +2,7 @@ export PATH := $(PATH):`go env GOPATH`/bin
|
|||||||
export GO111MODULE=on
|
export GO111MODULE=on
|
||||||
LDFLAGS := -s -w
|
LDFLAGS := -s -w
|
||||||
|
|
||||||
os-archs=darwin:amd64 darwin:arm64 freebsd:amd64 linux:amd64 linux:arm linux:arm64 windows:amd64 windows:arm64 linux:mips64 linux:mips64le linux:mips:softfloat linux:mipsle:softfloat linux:riscv64
|
os-archs=darwin:amd64 darwin:arm64 freebsd:amd64 linux:amd64 linux:arm linux:arm64 windows:amd64 windows:arm64 linux:mips64 linux:mips64le linux:mips:softfloat linux:mipsle:softfloat linux:riscv64 android:arm64
|
||||||
|
|
||||||
all: build
|
all: build
|
||||||
|
|
||||||
|
19
README.md
19
README.md
@ -78,6 +78,7 @@ frp also offers a P2P connect mode.
|
|||||||
* [URL Routing](#url-routing)
|
* [URL Routing](#url-routing)
|
||||||
* [TCP Port Multiplexing](#tcp-port-multiplexing)
|
* [TCP Port Multiplexing](#tcp-port-multiplexing)
|
||||||
* [Connecting to frps via PROXY](#connecting-to-frps-via-proxy)
|
* [Connecting to frps via PROXY](#connecting-to-frps-via-proxy)
|
||||||
|
* [Port range mapping](#port-range-mapping)
|
||||||
* [Client Plugins](#client-plugins)
|
* [Client Plugins](#client-plugins)
|
||||||
* [Server Manage Plugins](#server-manage-plugins)
|
* [Server Manage Plugins](#server-manage-plugins)
|
||||||
* [SSH Tunnel Gateway](#ssh-tunnel-gateway)
|
* [SSH Tunnel Gateway](#ssh-tunnel-gateway)
|
||||||
@ -1158,6 +1159,24 @@ serverPort = 7000
|
|||||||
transport.proxyURL = "http://user:pwd@192.168.1.128:8080"
|
transport.proxyURL = "http://user:pwd@192.168.1.128:8080"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Port range mapping
|
||||||
|
|
||||||
|
*Added in v0.56.0*
|
||||||
|
|
||||||
|
We can use the range syntax of Go template combined with the built-in `parseNumberRangePair` function to achieve port range mapping.
|
||||||
|
|
||||||
|
The following example, when run, will create 8 proxies named `test-6000, test-6001 ... test-6007`, each mapping the remote port to the local port.
|
||||||
|
|
||||||
|
```
|
||||||
|
{{- range $_, $v := parseNumberRangePair "6000-6006,6007" "6000-6006,6007" }}
|
||||||
|
[[proxies]]
|
||||||
|
name = "tcp-{{ $v.First }}"
|
||||||
|
type = "tcp"
|
||||||
|
localPort = {{ $v.First }}
|
||||||
|
remotePort = {{ $v.Second }}
|
||||||
|
{{- end }}
|
||||||
|
```
|
||||||
|
|
||||||
### Client Plugins
|
### Client Plugins
|
||||||
|
|
||||||
frpc only forwards requests to local TCP or UDP ports by default.
|
frpc only forwards requests to local TCP or UDP ports by default.
|
||||||
|
26
Release.md
26
Release.md
@ -1 +1,25 @@
|
|||||||
No feature changes, just a fix for the issue of no released assets in version 0.55.0.
|
### Features
|
||||||
|
|
||||||
|
* Support range ports mapping in TOML/YAML/JSON configuration file by using go template syntax.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
{{- range $_, $v := parseNumberRangePair "6000-6006,6007" "6000-6006,6007" }}
|
||||||
|
[[proxies]]
|
||||||
|
name = "tcp-{{ $v.First }}"
|
||||||
|
type = "tcp"
|
||||||
|
localPort = {{ $v.First }}
|
||||||
|
remotePort = {{ $v.Second }}
|
||||||
|
{{- end }}
|
||||||
|
```
|
||||||
|
|
||||||
|
This will create 8 proxies such as `tcp-6000, tcp-6001, ... tcp-6007`.
|
||||||
|
|
||||||
|
* Health check supports custom request headers.
|
||||||
|
* Enable compatibility mode for the Android system to solve the issues of incorrect log time caused by time zone problems and default DNS resolution failures.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Fix the issue of incorrect interval time for rotating the log by day.
|
||||||
|
* Disable quic-go's ECN support by default. It may cause issues on certain operating systems.
|
||||||
|
@ -40,8 +40,8 @@ type Monitor struct {
|
|||||||
addr string
|
addr string
|
||||||
|
|
||||||
// For http
|
// For http
|
||||||
url string
|
url string
|
||||||
|
header http.Header
|
||||||
failedTimes uint64
|
failedTimes uint64
|
||||||
statusOK bool
|
statusOK bool
|
||||||
statusNormalFn func()
|
statusNormalFn func()
|
||||||
@ -73,6 +73,11 @@ func NewMonitor(ctx context.Context, cfg v1.HealthCheckConfig, addr string,
|
|||||||
}
|
}
|
||||||
url = s + cfg.Path
|
url = s + cfg.Path
|
||||||
}
|
}
|
||||||
|
header := make(http.Header)
|
||||||
|
for _, h := range cfg.HTTPHeaders {
|
||||||
|
header.Set(h.Name, h.Value)
|
||||||
|
}
|
||||||
|
|
||||||
return &Monitor{
|
return &Monitor{
|
||||||
checkType: cfg.Type,
|
checkType: cfg.Type,
|
||||||
interval: time.Duration(cfg.IntervalSeconds) * time.Second,
|
interval: time.Duration(cfg.IntervalSeconds) * time.Second,
|
||||||
@ -80,6 +85,7 @@ func NewMonitor(ctx context.Context, cfg v1.HealthCheckConfig, addr string,
|
|||||||
maxFailedTimes: cfg.MaxFailed,
|
maxFailedTimes: cfg.MaxFailed,
|
||||||
addr: addr,
|
addr: addr,
|
||||||
url: url,
|
url: url,
|
||||||
|
header: header,
|
||||||
statusOK: false,
|
statusOK: false,
|
||||||
statusNormalFn: statusNormalFn,
|
statusNormalFn: statusNormalFn,
|
||||||
statusFailedFn: statusFailedFn,
|
statusFailedFn: statusFailedFn,
|
||||||
@ -163,6 +169,8 @@ func (monitor *Monitor) doHTTPCheck(ctx context.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
req.Header = monitor.header
|
||||||
|
req.Host = monitor.header.Get("Host")
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := http.DefaultClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -40,6 +41,12 @@ import (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
crypto.DefaultSalt = "frp"
|
crypto.DefaultSalt = "frp"
|
||||||
|
// Disable quic-go's receive buffer warning.
|
||||||
|
os.Setenv("QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING", "true")
|
||||||
|
// Disable quic-go's ECN support by default. It may cause issues on certain operating systems.
|
||||||
|
if os.Getenv("QUIC_GO_DISABLE_ECN") == "" {
|
||||||
|
os.Setenv("QUIC_GO_DISABLE_ECN", "true")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type cancelErr struct {
|
type cancelErr struct {
|
||||||
|
@ -17,8 +17,10 @@ package main
|
|||||||
import (
|
import (
|
||||||
_ "github.com/fatedier/frp/assets/frpc"
|
_ "github.com/fatedier/frp/assets/frpc"
|
||||||
"github.com/fatedier/frp/cmd/frpc/sub"
|
"github.com/fatedier/frp/cmd/frpc/sub"
|
||||||
|
"github.com/fatedier/frp/pkg/util/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
system.EnableCompatibilityMode()
|
||||||
sub.Execute()
|
sub.Execute()
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fatedier/golib/crypto"
|
|
||||||
|
|
||||||
_ "github.com/fatedier/frp/assets/frps"
|
_ "github.com/fatedier/frp/assets/frps"
|
||||||
_ "github.com/fatedier/frp/pkg/metrics"
|
_ "github.com/fatedier/frp/pkg/metrics"
|
||||||
|
"github.com/fatedier/frp/pkg/util/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
crypto.DefaultSalt = "frp"
|
system.EnableCompatibilityMode()
|
||||||
Execute()
|
Execute()
|
||||||
}
|
}
|
||||||
|
@ -216,6 +216,10 @@ healthCheck.path = "/status"
|
|||||||
healthCheck.intervalSeconds = 10
|
healthCheck.intervalSeconds = 10
|
||||||
healthCheck.maxFailed = 3
|
healthCheck.maxFailed = 3
|
||||||
healthCheck.timeoutSeconds = 3
|
healthCheck.timeoutSeconds = 3
|
||||||
|
# set health check headers
|
||||||
|
healthCheck.httpHeaders=[
|
||||||
|
{ name = "x-from-where", value = "frp" }
|
||||||
|
]
|
||||||
|
|
||||||
[[proxies]]
|
[[proxies]]
|
||||||
name = "web02"
|
name = "web02"
|
||||||
|
2
go.mod
2
go.mod
@ -5,7 +5,7 @@ go 1.22
|
|||||||
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/v3 v3.6.0
|
github.com/coreos/go-oidc/v3 v3.6.0
|
||||||
github.com/fatedier/golib v0.4.0
|
github.com/fatedier/golib v0.4.2
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
|
4
go.sum
4
go.sum
@ -24,8 +24,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
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/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fatedier/golib v0.4.0 h1:lafvYRMhFmqrfIUChKy/f5AXqs1eDSk+GAUtLexN5bU=
|
github.com/fatedier/golib v0.4.2 h1:k+ZBdUFTTipnP1RHfEhGbzyShRdz/rZtFGnjpXG9D9c=
|
||||||
github.com/fatedier/golib v0.4.0/go.mod h1:gpu+1vXxtJ072NYaNsn/YWgojDL8Ap2kFZQtbzT2qkg=
|
github.com/fatedier/golib v0.4.2/go.mod h1:gpu+1vXxtJ072NYaNsn/YWgojDL8Ap2kFZQtbzT2qkg=
|
||||||
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d h1:ynk1ra0RUqDWQfvFi5KtMiSobkVQ3cNc0ODb8CfIETo=
|
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d h1:ynk1ra0RUqDWQfvFi5KtMiSobkVQ3cNc0ODb8CfIETo=
|
||||||
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||||
github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA=
|
github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA=
|
||||||
|
@ -80,7 +80,10 @@ func DetectLegacyINIFormatFromFile(path string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func RenderWithTemplate(in []byte, values *Values) ([]byte, error) {
|
func RenderWithTemplate(in []byte, values *Values) ([]byte, error) {
|
||||||
tmpl, err := template.New("frp").Parse(string(in))
|
tmpl, err := template.New("frp").Funcs(template.FuncMap{
|
||||||
|
"parseNumberRange": parseNumberRange,
|
||||||
|
"parseNumberRangePair": parseNumberRangePair,
|
||||||
|
}).Parse(string(in))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
52
pkg/config/template.go
Normal file
52
pkg/config/template.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2024 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 (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NumberPair struct {
|
||||||
|
First int64
|
||||||
|
Second int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseNumberRangePair(firstRangeStr, secondRangeStr string) ([]NumberPair, error) {
|
||||||
|
firstRangeNumbers, err := util.ParseRangeNumbers(firstRangeStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
secondRangeNumbers, err := util.ParseRangeNumbers(secondRangeStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(firstRangeNumbers) != len(secondRangeNumbers) {
|
||||||
|
return nil, fmt.Errorf("first and second range numbers are not in pairs")
|
||||||
|
}
|
||||||
|
pairs := make([]NumberPair, 0, len(firstRangeNumbers))
|
||||||
|
for i := 0; i < len(firstRangeNumbers); i++ {
|
||||||
|
pairs = append(pairs, NumberPair{
|
||||||
|
First: firstRangeNumbers[i],
|
||||||
|
Second: secondRangeNumbers[i],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return pairs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseNumberRange(firstRangeStr string) ([]int64, error) {
|
||||||
|
return util.ParseRangeNumbers(firstRangeStr)
|
||||||
|
}
|
@ -129,3 +129,8 @@ type HTTPPluginOptions struct {
|
|||||||
type HeaderOperations struct {
|
type HeaderOperations struct {
|
||||||
Set map[string]string `json:"set,omitempty"`
|
Set map[string]string `json:"set,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HTTPHeader struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
@ -97,6 +97,9 @@ type HealthCheckConfig struct {
|
|||||||
// Path specifies the path to send health checks to if the
|
// Path specifies the path to send health checks to if the
|
||||||
// health check type is "http".
|
// health check type is "http".
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
|
// HTTPHeaders specifies the headers to send with the health request, if
|
||||||
|
// the health check type is "http".
|
||||||
|
HTTPHeaders []HTTPHeader `json:"httpHeaders,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DomainConfig struct {
|
type DomainConfig struct {
|
||||||
|
@ -17,7 +17,7 @@ package nathole
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand/v2"
|
||||||
"net"
|
"net"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -341,7 +341,7 @@ func sendSidMessage(
|
|||||||
TransactionID: transactionID,
|
TransactionID: transactionID,
|
||||||
Sid: sid,
|
Sid: sid,
|
||||||
Response: false,
|
Response: false,
|
||||||
Nonce: strings.Repeat("0", rand.Intn(20)),
|
Nonce: strings.Repeat("0", rand.IntN(20)),
|
||||||
}
|
}
|
||||||
buf, err := EncodeMessage(m, key)
|
buf, err := EncodeMessage(m, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -398,7 +398,7 @@ func sendSidMessageToRandomPorts(
|
|||||||
used := sets.New[int]()
|
used := sets.New[int]()
|
||||||
getUnusedPort := func() int {
|
getUnusedPort := func() int {
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
port := rand.Intn(65535-1024) + 1024
|
port := rand.IntN(65535-1024) + 1024
|
||||||
if !used.Has(port) {
|
if !used.Has(port) {
|
||||||
used.Insert(port)
|
used.Insert(port)
|
||||||
return port
|
return port
|
||||||
|
@ -26,8 +26,8 @@ func SetDefaultDNSAddress(dnsAddress string) {
|
|||||||
// Change default dns server
|
// Change default dns server
|
||||||
net.DefaultResolver = &net.Resolver{
|
net.DefaultResolver = &net.Resolver{
|
||||||
PreferGo: true,
|
PreferGo: true,
|
||||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
|
||||||
return net.Dial("udp", dnsAddress)
|
return net.Dial(network, dnsAddress)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
pkg/util/system/system.go
Normal file
22
pkg/util/system/system.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2024 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.
|
||||||
|
|
||||||
|
//go:build !android
|
||||||
|
|
||||||
|
package system
|
||||||
|
|
||||||
|
// EnableCompatibilityMode enables compatibility mode for different system.
|
||||||
|
// For example, on Android, the inability to obtain the correct time zone will result in incorrect log time output.
|
||||||
|
func EnableCompatibilityMode() {
|
||||||
|
}
|
66
pkg/util/system/system_android.go
Normal file
66
pkg/util/system/system_android.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2024 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 system
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func EnableCompatibilityMode() {
|
||||||
|
fixTimezone()
|
||||||
|
fixDNSResolver()
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixTimezone is used to try our best to fix timezone issue on some Android devices.
|
||||||
|
func fixTimezone() {
|
||||||
|
out, err := exec.Command("/system/bin/getprop", "persist.sys.timezone").Output()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loc, err := time.LoadLocation(strings.TrimSpace(string(out)))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Local = loc
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixDNSResolver will first attempt to resolve google.com to check if the current DNS is available.
|
||||||
|
// If it is not available, it will default to using 8.8.8.8 as the DNS server.
|
||||||
|
// This is a workaround for the issue that golang can't get the default DNS servers on Android.
|
||||||
|
func fixDNSResolver() {
|
||||||
|
// First, we attempt to resolve a domain. If resolution is successful, no modifications are necessary.
|
||||||
|
// In real-world scenarios, users may have already configured /etc/resolv.conf, or compiled directly
|
||||||
|
// in the Android environment instead of using cross-platform compilation, so this issue does not arise.
|
||||||
|
if net.DefaultResolver != nil {
|
||||||
|
timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||||
|
defer cancel()
|
||||||
|
_, err := net.DefaultResolver.LookupHost(timeoutCtx, "google.com")
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the resolution fails, use 8.8.8.8 as the DNS server.
|
||||||
|
// Note: If there are other methods to obtain the default DNS servers, the default DNS servers should be used preferentially.
|
||||||
|
net.DefaultResolver = &net.Resolver{
|
||||||
|
PreferGo: true,
|
||||||
|
Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
|
||||||
|
return net.Dial(network, "8.8.8.8:53")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,7 @@ import (
|
|||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
mathrand "math/rand"
|
mathrand "math/rand/v2"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -124,7 +124,7 @@ func RandomSleep(duration time.Duration, minRatio, maxRatio float64) time.Durati
|
|||||||
if max <= min {
|
if max <= min {
|
||||||
n = min
|
n = min
|
||||||
} else {
|
} else {
|
||||||
n = mathrand.Int63n(max-min) + min
|
n = mathrand.Int64N(max-min) + min
|
||||||
}
|
}
|
||||||
d := duration * time.Duration(n) / time.Duration(1000)
|
d := duration * time.Duration(n) / time.Duration(1000)
|
||||||
time.Sleep(d)
|
time.Sleep(d)
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
package version
|
package version
|
||||||
|
|
||||||
var version = "0.55.1"
|
var version = "0.56.0"
|
||||||
|
|
||||||
func Full() string {
|
func Full() string {
|
||||||
return version
|
return version
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
package wait
|
package wait
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand/v2"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/util/util"
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
|
@ -22,9 +22,11 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/golib/crypto"
|
||||||
"github.com/fatedier/golib/net/mux"
|
"github.com/fatedier/golib/net/mux"
|
||||||
fmux "github.com/hashicorp/yamux"
|
fmux "github.com/hashicorp/yamux"
|
||||||
quic "github.com/quic-go/quic-go"
|
quic "github.com/quic-go/quic-go"
|
||||||
@ -59,6 +61,16 @@ const (
|
|||||||
vhostReadWriteTimeout time.Duration = 30 * time.Second
|
vhostReadWriteTimeout time.Duration = 30 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
crypto.DefaultSalt = "frp"
|
||||||
|
// Disable quic-go's receive buffer warning.
|
||||||
|
os.Setenv("QUIC_GO_DISABLE_RECEIVE_BUFFER_WARNING", "true")
|
||||||
|
// Disable quic-go's ECN support by default. It may cause issues on certain operating systems.
|
||||||
|
if os.Getenv("QUIC_GO_DISABLE_ECN") == "" {
|
||||||
|
os.Setenv("QUIC_GO_DISABLE_ECN", "true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Server service
|
// Server service
|
||||||
type Service struct {
|
type Service struct {
|
||||||
// Dispatch connections to different handlers listen on same port
|
// Dispatch connections to different handlers listen on same port
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
package framework
|
package framework
|
||||||
|
|
||||||
type FRPClient struct {
|
import (
|
||||||
port int
|
clientsdk "github.com/fatedier/frp/pkg/sdk/client"
|
||||||
}
|
)
|
||||||
|
|
||||||
func (f *Framework) FRPClient(port int) *FRPClient {
|
func (f *Framework) APIClientForFrpc(port int) *clientsdk.Client {
|
||||||
return &FRPClient{
|
return clientsdk.New("127.0.0.1", port)
|
||||||
port: port,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,10 @@ func ExpectNoErrorWithOffset(offset int, err error, explain ...interface{}) {
|
|||||||
gomega.ExpectWithOffset(1+offset, err).NotTo(gomega.HaveOccurred(), explain...)
|
gomega.ExpectWithOffset(1+offset, err).NotTo(gomega.HaveOccurred(), explain...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExpectContainSubstring(actual, substr string, explain ...interface{}) {
|
||||||
|
gomega.ExpectWithOffset(1, actual).To(gomega.ContainSubstring(substr), explain...)
|
||||||
|
}
|
||||||
|
|
||||||
// ExpectConsistOf expects actual contains precisely the extra elements. The ordering of the elements does not matter.
|
// ExpectConsistOf expects actual contains precisely the extra elements. The ordering of the elements does not matter.
|
||||||
func ExpectConsistOf(actual interface{}, extra interface{}, explain ...interface{}) {
|
func ExpectConsistOf(actual interface{}, extra interface{}, explain ...interface{}) {
|
||||||
gomega.ExpectWithOffset(1, actual).To(gomega.ConsistOf(extra), explain...)
|
gomega.ExpectWithOffset(1, actual).To(gomega.ConsistOf(extra), explain...)
|
||||||
|
@ -217,7 +217,7 @@ func (f *Framework) RenderTemplates(templates []string) (outs []string, ports ma
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range templates {
|
for _, t := range templates {
|
||||||
tmpl, err := template.New("").Parse(t)
|
tmpl, err := template.New("frp-e2e").Parse(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/onsi/ginkgo/v2"
|
"github.com/onsi/ginkgo/v2"
|
||||||
|
|
||||||
clientsdk "github.com/fatedier/frp/pkg/sdk/client"
|
|
||||||
"github.com/fatedier/frp/test/e2e/framework"
|
"github.com/fatedier/frp/test/e2e/framework"
|
||||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||||
"github.com/fatedier/frp/test/e2e/pkg/request"
|
"github.com/fatedier/frp/test/e2e/pkg/request"
|
||||||
@ -54,7 +53,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
|
|||||||
framework.NewRequestExpect(f).Port(p2Port).Ensure()
|
framework.NewRequestExpect(f).Port(p2Port).Ensure()
|
||||||
framework.NewRequestExpect(f).Port(p3Port).Ensure()
|
framework.NewRequestExpect(f).Port(p3Port).Ensure()
|
||||||
|
|
||||||
client := clientsdk.New("127.0.0.1", adminPort)
|
client := f.APIClientForFrpc(adminPort)
|
||||||
conf, err := client.GetConfig()
|
conf, err := client.GetConfig()
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
@ -120,7 +119,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
|
|||||||
|
|
||||||
framework.NewRequestExpect(f).Port(testPort).Ensure()
|
framework.NewRequestExpect(f).Port(testPort).Ensure()
|
||||||
|
|
||||||
client := clientsdk.New("127.0.0.1", adminPort)
|
client := f.APIClientForFrpc(adminPort)
|
||||||
err := client.Stop()
|
err := client.Stop()
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/onsi/ginkgo/v2"
|
"github.com/onsi/ginkgo/v2"
|
||||||
|
|
||||||
clientsdk "github.com/fatedier/frp/pkg/sdk/client"
|
|
||||||
"github.com/fatedier/frp/test/e2e/framework"
|
"github.com/fatedier/frp/test/e2e/framework"
|
||||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||||
"github.com/fatedier/frp/test/e2e/pkg/port"
|
"github.com/fatedier/frp/test/e2e/pkg/port"
|
||||||
@ -99,7 +98,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
|
|||||||
|
|
||||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||||
|
|
||||||
client := clientsdk.New("127.0.0.1", adminPort)
|
client := f.APIClientForFrpc(adminPort)
|
||||||
|
|
||||||
// tcp random port
|
// tcp random port
|
||||||
status, err := client.GetProxyStatus("tcp")
|
status, err := client.GetProxyStatus("tcp")
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/onsi/ginkgo/v2"
|
"github.com/onsi/ginkgo/v2"
|
||||||
|
|
||||||
clientsdk "github.com/fatedier/frp/pkg/sdk/client"
|
|
||||||
"github.com/fatedier/frp/test/e2e/framework"
|
"github.com/fatedier/frp/test/e2e/framework"
|
||||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||||
"github.com/fatedier/frp/test/e2e/pkg/request"
|
"github.com/fatedier/frp/test/e2e/pkg/request"
|
||||||
@ -57,7 +56,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
|
|||||||
framework.NewRequestExpect(f).Port(p2Port).Ensure()
|
framework.NewRequestExpect(f).Port(p2Port).Ensure()
|
||||||
framework.NewRequestExpect(f).Port(p3Port).Ensure()
|
framework.NewRequestExpect(f).Port(p3Port).Ensure()
|
||||||
|
|
||||||
client := clientsdk.New("127.0.0.1", adminPort)
|
client := f.APIClientForFrpc(adminPort)
|
||||||
conf, err := client.GetConfig()
|
conf, err := client.GetConfig()
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
@ -124,7 +123,7 @@ var _ = ginkgo.Describe("[Feature: ClientManage]", func() {
|
|||||||
|
|
||||||
framework.NewRequestExpect(f).Port(testPort).Ensure()
|
framework.NewRequestExpect(f).Port(testPort).Ensure()
|
||||||
|
|
||||||
client := clientsdk.New("127.0.0.1", adminPort)
|
client := f.APIClientForFrpc(adminPort)
|
||||||
err := client.Stop()
|
err := client.Stop()
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
|
@ -38,6 +38,51 @@ var _ = ginkgo.Describe("[Feature: Config]", func() {
|
|||||||
|
|
||||||
framework.NewRequestExpect(f).PortName(portName).Ensure()
|
framework.NewRequestExpect(f).PortName(portName).Ensure()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ginkgo.It("Range ports mapping", func() {
|
||||||
|
serverConf := consts.DefaultServerConfig
|
||||||
|
clientConf := consts.DefaultClientConfig
|
||||||
|
|
||||||
|
adminPort := f.AllocPort()
|
||||||
|
|
||||||
|
localPortsRange := "13010-13012,13014"
|
||||||
|
remotePortsRange := "23010-23012,23014"
|
||||||
|
escapeTemplate := func(s string) string {
|
||||||
|
return "{{ `" + s + "` }}"
|
||||||
|
}
|
||||||
|
clientConf += fmt.Sprintf(`
|
||||||
|
webServer.port = %d
|
||||||
|
|
||||||
|
%s
|
||||||
|
[[proxies]]
|
||||||
|
name = "tcp-%s"
|
||||||
|
type = "tcp"
|
||||||
|
localPort = %s
|
||||||
|
remotePort = %s
|
||||||
|
%s
|
||||||
|
`, adminPort,
|
||||||
|
escapeTemplate(fmt.Sprintf(`{{- range $_, $v := parseNumberRangePair "%s" "%s" }}`, localPortsRange, remotePortsRange)),
|
||||||
|
escapeTemplate("{{ $v.First }}"),
|
||||||
|
escapeTemplate("{{ $v.First }}"),
|
||||||
|
escapeTemplate("{{ $v.Second }}"),
|
||||||
|
escapeTemplate("{{- end }}"),
|
||||||
|
)
|
||||||
|
|
||||||
|
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||||
|
|
||||||
|
client := f.APIClientForFrpc(adminPort)
|
||||||
|
checkProxyFn := func(name string, localPort, remotePort int) {
|
||||||
|
status, err := client.GetProxyStatus(name)
|
||||||
|
framework.ExpectNoError(err)
|
||||||
|
|
||||||
|
framework.ExpectContainSubstring(status.LocalAddr, fmt.Sprintf(":%d", localPort))
|
||||||
|
framework.ExpectContainSubstring(status.RemoteAddr, fmt.Sprintf(":%d", remotePort))
|
||||||
|
}
|
||||||
|
checkProxyFn("tcp-13010", 13010, 23010)
|
||||||
|
checkProxyFn("tcp-13011", 13011, 23011)
|
||||||
|
checkProxyFn("tcp-13012", 13012, 23012)
|
||||||
|
checkProxyFn("tcp-13014", 13014, 23014)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
ginkgo.Describe("Includes", func() {
|
ginkgo.Describe("Includes", func() {
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/onsi/ginkgo/v2"
|
"github.com/onsi/ginkgo/v2"
|
||||||
|
|
||||||
clientsdk "github.com/fatedier/frp/pkg/sdk/client"
|
|
||||||
"github.com/fatedier/frp/test/e2e/framework"
|
"github.com/fatedier/frp/test/e2e/framework"
|
||||||
"github.com/fatedier/frp/test/e2e/framework/consts"
|
"github.com/fatedier/frp/test/e2e/framework/consts"
|
||||||
"github.com/fatedier/frp/test/e2e/pkg/port"
|
"github.com/fatedier/frp/test/e2e/pkg/port"
|
||||||
@ -110,7 +109,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
|
|||||||
|
|
||||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||||
|
|
||||||
client := clientsdk.New("127.0.0.1", adminPort)
|
client := f.APIClientForFrpc(adminPort)
|
||||||
|
|
||||||
// tcp random port
|
// tcp random port
|
||||||
status, err := client.GetProxyStatus("tcp")
|
status, err := client.GetProxyStatus("tcp")
|
||||||
|
Loading…
Reference in New Issue
Block a user