mirror of
https://github.com/fatedier/frp.git
synced 2025-07-29 09:18:11 +00:00
Compare commits
91 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
da78e3f52e | ||
|
e1918f6396 | ||
|
ad1e32fd2d | ||
|
3726f99b04 | ||
|
c8a7405992 | ||
|
040d198e36 | ||
|
1a6cbbb2d2 | ||
|
ea79e03bd0 | ||
|
3e349455a0 | ||
|
c7a457a045 | ||
|
0b0d5c982e | ||
|
c4f873c07a | ||
|
01b1df2b91 | ||
|
f1bea49314 | ||
|
2ffae3489b | ||
|
96b94d9164 | ||
|
76b04f52d1 | ||
|
97db0d187a | ||
|
d9aadab4cb | ||
|
a0fe2fc2c2 | ||
|
1464836f05 | ||
|
b2a2037032 | ||
|
071cbf4b15 | ||
|
20fcb58437 | ||
|
a27e3dda88 | ||
|
1dd7317c06 | ||
|
58a54bd0fb | ||
|
caec4982cc | ||
|
dd8f788ca4 | ||
|
8a6d6c534a | ||
|
39089cf262 | ||
|
55800dc29f | ||
|
04560c1896 | ||
|
178efd67f1 | ||
|
6ef5fb6391 | ||
|
1ae43e4d41 | ||
|
5db605ca02 | ||
|
e087301425 | ||
|
f45283dbdb | ||
|
b0959b3caa | ||
|
c5c89a2519 | ||
|
bebd1db22a | ||
|
cd37d22f3b | ||
|
30af32728a | ||
|
60ecd1d58c | ||
|
a60be8f562 | ||
|
c008b14d0f | ||
|
853892f3cd | ||
|
e43f9f5850 | ||
|
d5f30ccd6b | ||
|
b87df569e7 | ||
|
976cf3e9f8 | ||
|
371c401f5b | ||
|
69919e8ef9 | ||
|
9abbe33790 | ||
|
4a5c00286e | ||
|
dfb892c8f6 | ||
|
461c4c18fd | ||
|
00b9ba95ae | ||
|
c47aad348d | ||
|
4cb4da3afc | ||
|
c1f57da00d | ||
|
fe187eb8ec | ||
|
0f6f674a64 | ||
|
814afbe1f6 | ||
|
3fde9176c9 | ||
|
af7cca1a93 | ||
|
7dd28a14aa | ||
|
1325c59a4c | ||
|
82dc1e924f | ||
|
3166bdf3f0 | ||
|
8af70c8822 | ||
|
87763e8251 | ||
|
e9241aeb94 | ||
|
2eaf134042 | ||
|
1739e012d6 | ||
|
9e8980429f | ||
|
1d0865ca49 | ||
|
5c9909aeef | ||
|
456ce09061 | ||
|
ffc13b704a | ||
|
5d239127bb | ||
|
9b990adf96 | ||
|
44e8108910 | ||
|
1c35e9a0c6 | ||
|
8e719ff0ff | ||
|
637ddbce1f | ||
|
ce8fde793c | ||
|
eede31c064 | ||
|
41c41789b6 | ||
|
68dfc89bce |
@@ -2,8 +2,7 @@ sudo: false
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
|
||||
install:
|
||||
- make
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.8
|
||||
FROM golang:1.10
|
||||
|
||||
COPY . /go/src/github.com/fatedier/frp
|
||||
|
||||
|
173
Gopkg.lock
generated
Normal file
173
Gopkg.lock
generated
Normal file
@@ -0,0 +1,173 @@
|
||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/armon/go-socks5"
|
||||
packages = ["."]
|
||||
revision = "e75332964ef517daa070d7c38a9466a0d687e0a5"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/fatedier/beego"
|
||||
packages = ["logs"]
|
||||
revision = "6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/fatedier/golib"
|
||||
packages = [
|
||||
"control/shutdown",
|
||||
"crypto",
|
||||
"errors",
|
||||
"io",
|
||||
"msg/json",
|
||||
"net",
|
||||
"net/mux",
|
||||
"pool"
|
||||
]
|
||||
revision = "354693cdd7fd9fa4f207c2f91ec2534615d3e5e5"
|
||||
|
||||
[[projects]]
|
||||
branch = "frp"
|
||||
name = "github.com/fatedier/kcp-go"
|
||||
packages = ["."]
|
||||
revision = "cd167d2f15f451b0f33780ce862fca97adc0331e"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/golang/snappy"
|
||||
packages = ["."]
|
||||
revision = "553a641470496b2327abcac10b36396bd98e45c9"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gorilla/websocket"
|
||||
packages = ["."]
|
||||
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/hashicorp/yamux"
|
||||
packages = ["."]
|
||||
revision = "2658be15c5f05e76244154714161f17e3e77de2e"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/inconshreveable/mousetrap"
|
||||
packages = ["."]
|
||||
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||
version = "v1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/julienschmidt/httprouter"
|
||||
packages = ["."]
|
||||
revision = "8c199fb6259ffc1af525cc3ad52ee60ba8359669"
|
||||
version = "v1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/rakyll/statik"
|
||||
packages = ["fs"]
|
||||
revision = "fd36b3595eb2ec8da4b8153b107f7ea08504899d"
|
||||
version = "v0.1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/rodaine/table"
|
||||
packages = ["."]
|
||||
revision = "212a2ad1c462ed4d5b5511ea2b480a573281dbbd"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/cobra"
|
||||
packages = ["."]
|
||||
revision = "a1f051bc3eba734da4772d60e2d677f47cf93ef4"
|
||||
version = "v0.0.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = ["assert"]
|
||||
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
|
||||
version = "v1.2.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/templexxx/cpufeat"
|
||||
packages = ["."]
|
||||
revision = "3794dfbfb04749f896b521032f69383f24c3687e"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/templexxx/reedsolomon"
|
||||
packages = ["."]
|
||||
revision = "5e06b81a1c7628d9c8d4fb7c3c4e401e37db39b4"
|
||||
version = "0.1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/templexxx/xor"
|
||||
packages = ["."]
|
||||
revision = "0af8e873c554da75f37f2049cdffda804533d44c"
|
||||
version = "0.1.2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/tjfoc/gmsm"
|
||||
packages = ["sm4"]
|
||||
revision = "98aa888b79d8de04afe0fccf45ed10594efc858b"
|
||||
version = "v1.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/vaughan0/go-ini"
|
||||
packages = ["."]
|
||||
revision = "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1"
|
||||
|
||||
[[projects]]
|
||||
name = "golang.org/x/crypto"
|
||||
packages = [
|
||||
"blowfish",
|
||||
"cast5",
|
||||
"pbkdf2",
|
||||
"salsa20",
|
||||
"salsa20/salsa",
|
||||
"tea",
|
||||
"twofish",
|
||||
"xtea"
|
||||
]
|
||||
revision = "4ec37c66abab2c7e02ae775328b2ff001c3f025a"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"bpf",
|
||||
"context",
|
||||
"internal/iana",
|
||||
"internal/socket",
|
||||
"internal/socks",
|
||||
"ipv4",
|
||||
"proxy"
|
||||
]
|
||||
revision = "d11bb6cd8e3c4e60239c9cb20ef68586d74500d0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "d934d16928772cfb22c55a39964c7ca364d02fe1ab680a90cdb5c3c8be256273"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
70
Gopkg.toml
Normal file
70
Gopkg.toml
Normal file
@@ -0,0 +1,70 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/armon/go-socks5"
|
||||
revision = "e75332964ef517daa070d7c38a9466a0d687e0a5"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/fatedier/beego"
|
||||
revision = "6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/fatedier/golib"
|
||||
branch = "master"
|
||||
|
||||
[[constraint]]
|
||||
branch = "frp"
|
||||
name = "github.com/fatedier/kcp-go"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gorilla/websocket"
|
||||
version = "1.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/hashicorp/yamux"
|
||||
revision = "2658be15c5f05e76244154714161f17e3e77de2e"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/rakyll/statik"
|
||||
version = "0.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/rodaine/table"
|
||||
version = "1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/cobra"
|
||||
version = "0.0.2"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/vaughan0/go-ini"
|
||||
revision = "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
13
Makefile
13
Makefile
@@ -1,5 +1,4 @@
|
||||
export PATH := $(GOPATH)/bin:$(PATH)
|
||||
export GO15VENDOREXPERIMENT := 1
|
||||
|
||||
all: fmt build
|
||||
|
||||
@@ -15,12 +14,7 @@ file:
|
||||
go generate ./assets/...
|
||||
|
||||
fmt:
|
||||
go fmt ./assets/...
|
||||
go fmt ./client/...
|
||||
go fmt ./cmd/...
|
||||
go fmt ./models/...
|
||||
go fmt ./server/...
|
||||
go fmt ./utils/...
|
||||
go fmt ./...
|
||||
|
||||
frps:
|
||||
go build -o bin/frps ./cmd/frps
|
||||
@@ -44,7 +38,7 @@ ci:
|
||||
go test -v ./tests/...
|
||||
cd ./tests && ./clean_test.sh && cd -
|
||||
|
||||
ciclean:
|
||||
cic:
|
||||
cd ./tests && ./clean_test.sh && cd -
|
||||
|
||||
alltest: gotest ci
|
||||
@@ -53,6 +47,3 @@ clean:
|
||||
rm -f ./bin/frpc
|
||||
rm -f ./bin/frps
|
||||
cd ./tests && ./clean_test.sh && cd -
|
||||
|
||||
save:
|
||||
godep save ./...
|
||||
|
@@ -1,5 +1,4 @@
|
||||
export PATH := $(GOPATH)/bin:$(PATH)
|
||||
export GO15VENDOREXPERIMENT := 1
|
||||
LDFLAGS := -s -w
|
||||
|
||||
all: build
|
||||
@@ -9,6 +8,10 @@ build: app
|
||||
app:
|
||||
env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frpc_darwin_amd64 ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_darwin_amd64 ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frpc_freebsd_386 ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frps_freebsd_386 ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frpc_freebsd_amd64 ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_freebsd_amd64 ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_386 ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_386 ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_amd64 ./cmd/frpc
|
||||
@@ -23,10 +26,10 @@ app:
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips64 ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mips64le ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips64le ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mips ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mipsle ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mipsle ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mips ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips ./cmd/frps
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mipsle ./cmd/frpc
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mipsle ./cmd/frps
|
||||
|
||||
temp:
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_amd64 ./cmd/frps
|
||||
|
94
README.md
94
README.md
@@ -20,9 +20,9 @@ frp is a fast reverse proxy to help you expose a local server behind a NAT or fi
|
||||
* [Visit your web service in LAN by custom domains](#visit-your-web-service-in-lan-by-custom-domains)
|
||||
* [Forward DNS query request](#forward-dns-query-request)
|
||||
* [Forward unix domain socket](#forward-unix-domain-socket)
|
||||
* [Expose a simple http file server](#expose-a-simple-http-file-server)
|
||||
* [Expose your service in security](#expose-your-service-in-security)
|
||||
* [P2P Mode](#p2p-mode)
|
||||
* [Connect website through frpc's network](#connect-website-through-frpcs-network)
|
||||
* [Features](#features)
|
||||
* [Configuration File](#configuration-file)
|
||||
* [Dashboard](#dashboard)
|
||||
@@ -30,8 +30,8 @@ frp is a fast reverse proxy to help you expose a local server behind a NAT or fi
|
||||
* [Encryption and Compression](#encryption-and-compression)
|
||||
* [Hot-Reload frpc configuration](#hot-reload-frpc-configuration)
|
||||
* [Get proxy status from client](#get-proxy-status-from-client)
|
||||
* [Privilege Mode](#privilege-mode)
|
||||
* [Port White List](#port-white-list)
|
||||
* [Port Reuse](#port-reuse)
|
||||
* [TCP Stream Multiplexing](#tcp-stream-multiplexing)
|
||||
* [Support KCP Protocol](#support-kcp-protocol)
|
||||
* [Connection Pool](#connection-pool)
|
||||
@@ -41,6 +41,7 @@ frp is a fast reverse proxy to help you expose a local server behind a NAT or fi
|
||||
* [Custom subdomain names](#custom-subdomain-names)
|
||||
* [URL routing](#url-routing)
|
||||
* [Connect frps by HTTP PROXY](#connect-frps-by-http-proxy)
|
||||
* [Range ports mapping](#range-ports-mapping)
|
||||
* [Plugin](#plugin)
|
||||
* [Development Plan](#development-plan)
|
||||
* [Contributing](#contributing)
|
||||
@@ -214,6 +215,32 @@ Configure frps same as above.
|
||||
|
||||
`curl http://x.x.x.x:6000/version`
|
||||
|
||||
### Expose a simple http file server
|
||||
|
||||
A simple way to visit files in the LAN.
|
||||
|
||||
Configure frps same as above.
|
||||
|
||||
1. Start frpc with configurations:
|
||||
|
||||
```ini
|
||||
# frpc.ini
|
||||
[common]
|
||||
server_addr = x.x.x.x
|
||||
server_port = 7000
|
||||
|
||||
[test_static_file]
|
||||
type = tcp
|
||||
remote_port = 6000
|
||||
plugin = static_file
|
||||
plugin_local_path = /tmp/file
|
||||
plugin_strip_prefix = static
|
||||
plugin_http_user = abc
|
||||
plugin_http_passwd = abc
|
||||
```
|
||||
|
||||
2. Visit `http://x.x.x.x:6000/static/` by your browser, set correct user and password, so you can see files in `/tmp/file`.
|
||||
|
||||
### Expose your service in security
|
||||
|
||||
For some services, if expose them to the public network directly will be a security risk.
|
||||
@@ -306,26 +333,6 @@ Now it can't penetrate all types of NAT devices. You can try **stcp** if **xtcp*
|
||||
|
||||
`ssh -oPort=6000 test@127.0.0.1`
|
||||
|
||||
### Connect website through frpc's network
|
||||
|
||||
Configure frps same as above.
|
||||
|
||||
1. Start frpc with configurations:
|
||||
|
||||
```ini
|
||||
# frpc.ini
|
||||
[common]
|
||||
server_addr = x.x.x.x
|
||||
server_port = 7000
|
||||
|
||||
[http_proxy]
|
||||
type = tcp
|
||||
remote_port = 6000
|
||||
plugin = http_proxy # or socks5
|
||||
```
|
||||
|
||||
2. Set http proxy or socks5 proxy `x.x.x.x:6000` in your browser and visit website through frpc's network.
|
||||
|
||||
## Features
|
||||
|
||||
### Configuration File
|
||||
@@ -356,7 +363,7 @@ Then visit `http://[server_addr]:7500` to see dashboard, default username and pa
|
||||
|
||||
### Authentication
|
||||
|
||||
Since v0.10.0, you only need to set `privilege_token` in frps.ini and frpc.ini.
|
||||
Since v0.10.0, you only need to set `token` in frps.ini and frpc.ini.
|
||||
|
||||
Note that time duration between server of frpc and frps mustn't exceed 15 minutes because timestamp is used for authentication.
|
||||
|
||||
@@ -395,21 +402,23 @@ Then run command `frpc reload -c ./frpc.ini` and wait for about 10 seconds to le
|
||||
|
||||
Use `frpc status -c ./frpc.ini` to get status of all proxies. You need to set admin port in frpc's configure file.
|
||||
|
||||
### Privilege Mode
|
||||
### Port White List
|
||||
|
||||
Privilege mode is the default and only mode support in frp since v0.10.0. All proxy configurations are set in client.
|
||||
|
||||
#### Port White List
|
||||
|
||||
`privilege_allow_ports` in frps.ini is used for preventing abuse of ports:
|
||||
`allow_ports` in frps.ini is used for preventing abuse of ports:
|
||||
|
||||
```ini
|
||||
# frps.ini
|
||||
[common]
|
||||
privilege_allow_ports = 2000-3000,3001,3003,4000-50000
|
||||
allow_ports = 2000-3000,3001,3003,4000-50000
|
||||
```
|
||||
|
||||
`privilege_allow_ports` consists of a specific port or a range of ports divided by `,`.
|
||||
`allow_ports` consists of a specific port or a range of ports divided by `,`.
|
||||
|
||||
### Port Reuse
|
||||
|
||||
Now `vhost_http_port` and `vhost_https_port` in frps can use same port with `bind_port`. frps will detect connection's protocol and handle it correspondingly.
|
||||
|
||||
We would like to try to allow multiple proxies bind a same remote port with different protocols in the future.
|
||||
|
||||
### TCP Stream Multiplexing
|
||||
|
||||
@@ -512,7 +521,7 @@ type = http
|
||||
local_port = 80
|
||||
custom_domains = test.yourdomain.com
|
||||
http_user = abc
|
||||
http_pwd = abc
|
||||
http_passwd = abc
|
||||
```
|
||||
|
||||
Visit `http://test.yourdomain.com` and now you need to input username and password.
|
||||
@@ -576,11 +585,26 @@ server_port = 7000
|
||||
http_proxy = http://user:pwd@192.168.1.128:8080
|
||||
```
|
||||
|
||||
### Range ports mapping
|
||||
|
||||
Proxy name has prefix `range:` will support mapping range ports.
|
||||
|
||||
```ini
|
||||
# frpc.ini
|
||||
[range:test_tcp]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 6000-6006,6007
|
||||
remote_port = 6000-6006,6007
|
||||
```
|
||||
|
||||
frpc will generate 6 proxies like `test_tcp_0, test_tcp_1 ... test_tcp_5`.
|
||||
|
||||
### Plugin
|
||||
|
||||
frpc only forward request to local tcp or udp port by default.
|
||||
|
||||
Plugin is used for providing rich features. There are built-in plugins such as **unix_domain_socket**, **http_proxy**, **socks5** and you can see [example usage](#example-usage).
|
||||
Plugin is used for providing rich features. There are built-in plugins such as `unix_domain_socket`, `http_proxy`, `socks5`, `static_file` and you can see [example usage](#example-usage).
|
||||
|
||||
Specify which plugin to use by `plugin` parameter. Configuration parameters of plugin should be started with `plugin_`. `local_ip` and `local_port` is useless for plugin.
|
||||
|
||||
@@ -598,17 +622,13 @@ plugin_http_passwd = abc
|
||||
|
||||
`plugin_http_user` and `plugin_http_passwd` are configuration parameters used in `http_proxy` plugin.
|
||||
|
||||
|
||||
## Development Plan
|
||||
|
||||
* Log http request information in frps.
|
||||
* Direct reverse proxy, like haproxy.
|
||||
* Load balance to different service in frpc.
|
||||
* Frpc can directly be a webserver for static files.
|
||||
* P2p communicate by making udp hole to penetrate NAT.
|
||||
* kubernetes ingress support.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Interested in getting involved? We would like to help you!
|
||||
|
104
README_zh.md
104
README_zh.md
@@ -18,9 +18,9 @@ frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp
|
||||
* [通过自定义域名访问部署于内网的 web 服务](#通过自定义域名访问部署于内网的-web-服务)
|
||||
* [转发 DNS 查询请求](#转发-dns-查询请求)
|
||||
* [转发 Unix域套接字](#转发-unix域套接字)
|
||||
* [对外提供简单的文件访问服务](#对外提供简单的文件访问服务)
|
||||
* [安全地暴露内网服务](#安全地暴露内网服务)
|
||||
* [点对点内网穿透](#点对点内网穿透)
|
||||
* [通过 frpc 所在机器访问外网](#通过-frpc-所在机器访问外网)
|
||||
* [功能说明](#功能说明)
|
||||
* [配置文件](#配置文件)
|
||||
* [Dashboard](#dashboard)
|
||||
@@ -28,8 +28,8 @@ frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp
|
||||
* [加密与压缩](#加密与压缩)
|
||||
* [客户端热加载配置文件](#客户端热加载配置文件)
|
||||
* [客户端查看代理状态](#客户端查看代理状态)
|
||||
* [特权模式](#特权模式)
|
||||
* [端口白名单](#端口白名单)
|
||||
* [端口复用](#端口复用)
|
||||
* [TCP 多路复用](#tcp-多路复用)
|
||||
* [底层通信可选 kcp 协议](#底层通信可选-kcp-协议)
|
||||
* [连接池](#连接池)
|
||||
@@ -39,6 +39,7 @@ frp 是一个可用于内网穿透的高性能的反向代理应用,支持 tcp
|
||||
* [自定义二级域名](#自定义二级域名)
|
||||
* [URL 路由](#url-路由)
|
||||
* [通过代理连接 frps](#通过代理连接-frps)
|
||||
* [范围端口映射](#范围端口映射)
|
||||
* [插件](#插件)
|
||||
* [开发计划](#开发计划)
|
||||
* [为 frp 做贡献](#为-frp-做贡献)
|
||||
@@ -192,11 +193,11 @@ DNS 查询请求通常使用 UDP 协议,frp 支持对内网 UDP 服务的穿
|
||||
|
||||
### 转发 Unix域套接字
|
||||
|
||||
通过 tcp 端口访问内网的 unix域套接字(和 docker daemon 通信)。
|
||||
通过 tcp 端口访问内网的 unix域套接字(例如和 docker daemon 通信)。
|
||||
|
||||
frps 的部署步骤同上。
|
||||
|
||||
1. 启动 frpc,启用 unix_domain_socket 插件,配置如下:
|
||||
1. 启动 frpc,启用 `unix_domain_socket` 插件,配置如下:
|
||||
|
||||
```ini
|
||||
# frpc.ini
|
||||
@@ -215,6 +216,34 @@ frps 的部署步骤同上。
|
||||
|
||||
`curl http://x.x.x.x:6000/version`
|
||||
|
||||
### 对外提供简单的文件访问服务
|
||||
|
||||
通过 `static_file` 插件可以对外提供一个简单的基于 HTTP 的文件访问服务。
|
||||
|
||||
frps 的部署步骤同上。
|
||||
|
||||
1. 启动 frpc,启用 `static_file` 插件,配置如下:
|
||||
|
||||
```ini
|
||||
# frpc.ini
|
||||
[common]
|
||||
server_addr = x.x.x.x
|
||||
server_port = 7000
|
||||
|
||||
[test_static_file]
|
||||
type = tcp
|
||||
remote_port = 6000
|
||||
plugin = static_file
|
||||
# 要对外暴露的文件目录
|
||||
plugin_local_path = /tmp/file
|
||||
# 访问 url 中会被去除的前缀,保留的内容即为要访问的文件路径
|
||||
plugin_strip_prefix = static
|
||||
plugin_http_user = abc
|
||||
plugin_http_passwd = abc
|
||||
```
|
||||
|
||||
2. 通过浏览器访问 `http://x.x.x.x:6000/static/` 来查看位于 `/tmp/file` 目录下的文件,会要求输入已设置好的用户名和密码。
|
||||
|
||||
### 安全地暴露内网服务
|
||||
|
||||
对于某些服务来说如果直接暴露于公网上将会存在安全隐患。
|
||||
@@ -319,28 +348,6 @@ frp 提供了一种新的代理类型 **xtcp** 用于应对在希望传输大量
|
||||
|
||||
`ssh -oPort=6000 test@127.0.0.1`
|
||||
|
||||
### 通过 frpc 所在机器访问外网
|
||||
|
||||
frpc 内置了 http proxy 和 socks5 插件,可以使其他机器通过 frpc 的网络访问互联网。
|
||||
|
||||
frps 的部署步骤同上。
|
||||
|
||||
1. 启动 frpc,启用 http_proxy 或 socks5 插件(plugin 换为 socks5 即可), 配置如下:
|
||||
|
||||
```ini
|
||||
# frpc.ini
|
||||
[common]
|
||||
server_addr = x.x.x.x
|
||||
server_port = 7000
|
||||
|
||||
[http_proxy]
|
||||
type = tcp
|
||||
remote_port = 6000
|
||||
plugin = http_proxy
|
||||
```
|
||||
|
||||
2. 浏览器设置 http 或 socks5 代理地址为 `x.x.x.x:6000`,通过 frpc 机器的网络访问互联网。
|
||||
|
||||
## 功能说明
|
||||
|
||||
### 配置文件
|
||||
@@ -371,7 +378,7 @@ dashboard_pwd = admin
|
||||
|
||||
### 身份验证
|
||||
|
||||
从 v0.10.0 版本开始,所有 proxy 配置全部放在客户端(也就是之前版本的特权模式),服务端和客户端的 common 配置中的 `privilege_token` 参数一致则身份验证通过。
|
||||
从 v0.10.0 版本开始,所有 proxy 配置全部放在客户端(也就是之前版本的特权模式),服务端和客户端的 common 配置中的 `token` 参数一致则身份验证通过。
|
||||
|
||||
需要注意的是 frpc 所在机器和 frps 所在机器的时间相差不能超过 15 分钟,因为时间戳会被用于加密验证中,防止报文被劫持后被其他人利用。
|
||||
|
||||
@@ -420,21 +427,25 @@ admin_port = 7400
|
||||
|
||||
frpc 支持通过 `frpc status -c ./frpc.ini` 命令查看代理的状态信息,此功能需要在 frpc 中配置 admin 端口。
|
||||
|
||||
### 特权模式
|
||||
### 端口白名单
|
||||
|
||||
由于从 v0.10.0 版本开始,所有 proxy 都在客户端配置,原先的特权模式是目前唯一支持的模式。
|
||||
|
||||
#### 端口白名单
|
||||
|
||||
为了防止端口被滥用,可以手动指定允许哪些端口被使用,在 frps.ini 中通过 privilege_allow_ports 来指定:
|
||||
为了防止端口被滥用,可以手动指定允许哪些端口被使用,在 frps.ini 中通过 `allow_ports` 来指定:
|
||||
|
||||
```ini
|
||||
# frps.ini
|
||||
[common]
|
||||
privilege_allow_ports = 2000-3000,3001,3003,4000-50000
|
||||
allow_ports = 2000-3000,3001,3003,4000-50000
|
||||
```
|
||||
|
||||
privilege_allow_ports 可以配置允许使用的某个指定端口或者是一个范围内的所有端口,以 `,` 分隔,指定的范围以 `-` 分隔。
|
||||
`allow_ports` 可以配置允许使用的某个指定端口或者是一个范围内的所有端口,以 `,` 分隔,指定的范围以 `-` 分隔。
|
||||
|
||||
### 端口复用
|
||||
|
||||
目前 frps 中的 `vhost_http_port` 和 `vhost_https_port` 支持配置成和 `bind_port` 为同一个端口,frps 会对连接的协议进行分析,之后进行不同的处理。
|
||||
|
||||
例如在某些限制较严格的网络环境中,可以将 `bind_port` 和 `vhost_https_port` 都设置为 443。
|
||||
|
||||
后续会尝试允许多个 proxy 绑定同一个远端端口的不同协议。
|
||||
|
||||
### TCP 多路复用
|
||||
|
||||
@@ -609,11 +620,30 @@ server_port = 7000
|
||||
http_proxy = http://user:pwd@192.168.1.128:8080
|
||||
```
|
||||
|
||||
### 范围端口映射
|
||||
|
||||
在 frpc 的配置文件中可以指定映射多个端口,目前只支持 tcp 和 udp 的类型。
|
||||
|
||||
这一功能通过 `range:` 段落标记来实现,客户端会解析这个标记中的配置,将其拆分成多个 proxy,每一个 proxy 以数字为后缀命名。
|
||||
|
||||
例如要映射本地 6000-6005, 6007 这6个端口,主要配置如下:
|
||||
|
||||
```ini
|
||||
# frpc.ini
|
||||
[range:test_tcp]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 6000-6006,6007
|
||||
remote_port = 6000-6006,6007
|
||||
```
|
||||
|
||||
实际连接成功后会创建 6 个 proxy,命名为 `test_tcp_0, test_tcp_1 ... test_tcp_5`。
|
||||
|
||||
### 插件
|
||||
|
||||
默认情况下,frpc 只会转发请求到本地 tcp 或 udp 端口。
|
||||
|
||||
插件模式是为了在客户端提供更加丰富的功能,目前内置的插件有 **unix_domain_socket**、**http_proxy**、**socks5**。具体使用方式请查看[使用示例](#使用示例)。
|
||||
插件模式是为了在客户端提供更加丰富的功能,目前内置的插件有 `unix_domain_socket`、`http_proxy`、`socks5`、`static_file`。具体使用方式请查看[使用示例](#使用示例)。
|
||||
|
||||
通过 `plugin` 指定需要使用的插件,插件的配置参数都以 `plugin_` 开头。使用插件后 `local_ip` 和 `local_port` 不再需要配置。
|
||||
|
||||
@@ -638,8 +668,6 @@ plugin_http_passwd = abc
|
||||
* frps 记录 http 请求日志。
|
||||
* frps 支持直接反向代理,类似 haproxy。
|
||||
* frpc 支持负载均衡到后端不同服务。
|
||||
* frpc 支持直接作为 webserver 访问指定静态页面。
|
||||
* 支持 udp 打洞的方式,提供两边内网机器直接通信,流量不经过服务器转发。
|
||||
* 集成对 k8s 等平台的支持。
|
||||
|
||||
## 为 frp 做贡献
|
||||
|
BIN
assets/static/6f0a76321d30f3c8120915e57f7bd77e.ttf
Normal file
BIN
assets/static/6f0a76321d30f3c8120915e57f7bd77e.ttf
Normal file
Binary file not shown.
Binary file not shown.
@@ -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?facf06d98c7e1aea259d"></script><script type="text/javascript" src="vendor.js?a05a344be2b42183469b"></script><script type="text/javascript" src="index.js?a914c2dc7a5bb16ad443"></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?b90a32ccfb87def61aaa"></script><script type="text/javascript" src="vendor.js?f04985ef00f520142368"></script></body> </html>
|
File diff suppressed because one or more lines are too long
@@ -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,a,c){for(var u,i,f,l=0,s=[];l<t.length;l++)i=t[l],o[i]&&s.push(o[i][0]),o[i]=0;for(u in a)Object.prototype.hasOwnProperty.call(a,u)&&(e[u]=a[u]);for(r&&r(t,a,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={2:0};n.e=function(e){function r(){u.onerror=u.onload=null,clearTimeout(i);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 a=new Promise(function(n,r){t=o[e]=[n,r]});t[2]=a;var c=document.getElementsByTagName("head")[0],u=document.createElement("script");u.type="text/javascript",u.charset="utf-8",u.async=!0,u.timeout=12e4,n.nc&&u.setAttribute("nonce",n.nc),u.src=n.p+""+e+".js?"+{0:"a914c2dc7a5bb16ad443",1:"a05a344be2b42183469b"}[e];var i=setTimeout(r,12e4);return u.onerror=u.onload=r,c.appendChild(u),a},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:"f04985ef00f520142368"}[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
@@ -20,7 +20,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/g"
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
@@ -35,7 +35,7 @@ func (svr *Service) RunAdminServer(addr string, port int) (err error) {
|
||||
// url router
|
||||
router := httprouter.New()
|
||||
|
||||
user, passwd := config.ClientCommonCfg.AdminUser, config.ClientCommonCfg.AdminPwd
|
||||
user, passwd := g.GlbClientCfg.AdminUser, g.GlbClientCfg.AdminPwd
|
||||
|
||||
// api, see dashboard_api.go
|
||||
router.GET("/api/reload", frpNet.HttprouterBasicAuth(svr.apiReload, user, passwd))
|
||||
|
@@ -17,6 +17,7 @@ package client
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -24,6 +25,7 @@ import (
|
||||
"github.com/julienschmidt/httprouter"
|
||||
ini "github.com/vaughan0/go-ini"
|
||||
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
)
|
||||
@@ -51,15 +53,16 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request, _ httprout
|
||||
|
||||
log.Info("Http request: [/api/reload]")
|
||||
|
||||
conf, err := ini.LoadFile(config.ClientCommonCfg.ConfigFile)
|
||||
b, err := ioutil.ReadFile(g.GlbClientCfg.CfgFile)
|
||||
if err != nil {
|
||||
res.Code = 1
|
||||
res.Msg = err.Error()
|
||||
log.Error("reload frpc config file error: %v", err)
|
||||
return
|
||||
}
|
||||
content := string(b)
|
||||
|
||||
newCommonCfg, err := config.LoadClientCommonConf(conf)
|
||||
newCommonCfg, err := config.UnmarshalClientConfFromIni(nil, content)
|
||||
if err != nil {
|
||||
res.Code = 2
|
||||
res.Msg = err.Error()
|
||||
@@ -67,7 +70,15 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request, _ httprout
|
||||
return
|
||||
}
|
||||
|
||||
pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromFile(config.ClientCommonCfg.User, conf, newCommonCfg.Start)
|
||||
conf, err := ini.LoadFile(g.GlbClientCfg.CfgFile)
|
||||
if err != nil {
|
||||
res.Code = 1
|
||||
res.Msg = err.Error()
|
||||
log.Error("reload frpc config file error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromIni(g.GlbClientCfg.User, conf, newCommonCfg.Start)
|
||||
if err != nil {
|
||||
res.Code = 3
|
||||
res.Msg = err.Error()
|
||||
@@ -125,18 +136,18 @@ func NewProxyStatusResp(status *ProxyStatus) ProxyStatusResp {
|
||||
}
|
||||
psr.Plugin = cfg.Plugin
|
||||
if status.Err != "" {
|
||||
psr.RemoteAddr = fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, cfg.RemotePort)
|
||||
psr.RemoteAddr = fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, cfg.RemotePort)
|
||||
} else {
|
||||
psr.RemoteAddr = config.ClientCommonCfg.ServerAddr + status.RemoteAddr
|
||||
psr.RemoteAddr = g.GlbClientCfg.ServerAddr + status.RemoteAddr
|
||||
}
|
||||
case *config.UdpProxyConf:
|
||||
if cfg.LocalPort != 0 {
|
||||
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort)
|
||||
}
|
||||
if status.Err != "" {
|
||||
psr.RemoteAddr = fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, cfg.RemotePort)
|
||||
psr.RemoteAddr = fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, cfg.RemotePort)
|
||||
} else {
|
||||
psr.RemoteAddr = config.ClientCommonCfg.ServerAddr + status.RemoteAddr
|
||||
psr.RemoteAddr = g.GlbClientCfg.ServerAddr + status.RemoteAddr
|
||||
}
|
||||
case *config.HttpProxyConf:
|
||||
if cfg.LocalPort != 0 {
|
||||
|
@@ -17,19 +17,23 @@ package client
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
"github.com/fatedier/frp/utils/crypto"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
"github.com/fatedier/frp/utils/shutdown"
|
||||
"github.com/fatedier/frp/utils/util"
|
||||
"github.com/fatedier/frp/utils/version"
|
||||
"github.com/xtaci/smux"
|
||||
|
||||
"github.com/fatedier/golib/control/shutdown"
|
||||
"github.com/fatedier/golib/crypto"
|
||||
fmux "github.com/hashicorp/yamux"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -49,7 +53,7 @@ type Control struct {
|
||||
conn frpNet.Conn
|
||||
|
||||
// tcp stream multiplexing, if enabled
|
||||
session *smux.Session
|
||||
session *fmux.Session
|
||||
|
||||
// put a message in this channel to send it over control connection to server
|
||||
sendCh chan (msg.Message)
|
||||
@@ -82,15 +86,15 @@ func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf, visitorCfgs m
|
||||
loginMsg := &msg.Login{
|
||||
Arch: runtime.GOARCH,
|
||||
Os: runtime.GOOS,
|
||||
PoolCount: config.ClientCommonCfg.PoolCount,
|
||||
User: config.ClientCommonCfg.User,
|
||||
PoolCount: g.GlbClientCfg.PoolCount,
|
||||
User: g.GlbClientCfg.User,
|
||||
Version: version.Full(),
|
||||
}
|
||||
ctl := &Control{
|
||||
svr: svr,
|
||||
loginMsg: loginMsg,
|
||||
sendCh: make(chan msg.Message, 10),
|
||||
readCh: make(chan msg.Message, 10),
|
||||
sendCh: make(chan msg.Message, 100),
|
||||
readCh: make(chan msg.Message, 100),
|
||||
closedCh: make(chan int),
|
||||
readerShutdown: shutdown.New(),
|
||||
writerShutdown: shutdown.New(),
|
||||
@@ -98,7 +102,7 @@ func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf, visitorCfgs m
|
||||
Logger: log.NewPrefixLogger(""),
|
||||
}
|
||||
ctl.pm = NewProxyManager(ctl, ctl.sendCh, "")
|
||||
ctl.pm.Reload(pxyCfgs, visitorCfgs)
|
||||
ctl.pm.Reload(pxyCfgs, visitorCfgs, false)
|
||||
return ctl
|
||||
}
|
||||
|
||||
@@ -110,7 +114,7 @@ func (ctl *Control) Run() (err error) {
|
||||
|
||||
// if login_fail_exit is true, just exit this program
|
||||
// otherwise sleep a while and continues relogin to server
|
||||
if config.ClientCommonCfg.LoginFailExit {
|
||||
if g.GlbClientCfg.LoginFailExit {
|
||||
return
|
||||
} else {
|
||||
time.Sleep(10 * time.Second)
|
||||
@@ -124,7 +128,7 @@ func (ctl *Control) Run() (err error) {
|
||||
|
||||
// start all local visitors and send NewProxy message for all configured proxies
|
||||
ctl.pm.Reset(ctl.sendCh, ctl.runId)
|
||||
ctl.pm.CheckAndStartProxy()
|
||||
ctl.pm.CheckAndStartProxy([]string{ProxyStatusNew})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -183,8 +187,8 @@ func (ctl *Control) login() (err error) {
|
||||
ctl.session.Close()
|
||||
}
|
||||
|
||||
conn, err := frpNet.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
|
||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
|
||||
conn, err := frpNet.ConnectServerByProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol,
|
||||
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -195,8 +199,10 @@ func (ctl *Control) login() (err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
if config.ClientCommonCfg.TcpMux {
|
||||
session, errRet := smux.Client(conn, nil)
|
||||
if g.GlbClientCfg.TcpMux {
|
||||
fmuxCfg := fmux.DefaultConfig()
|
||||
fmuxCfg.LogOutput = ioutil.Discard
|
||||
session, errRet := fmux.Client(conn, fmuxCfg)
|
||||
if errRet != nil {
|
||||
return errRet
|
||||
}
|
||||
@@ -210,7 +216,7 @@ func (ctl *Control) login() (err error) {
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
ctl.loginMsg.PrivilegeKey = util.GetAuthKey(config.ClientCommonCfg.PrivilegeToken, now)
|
||||
ctl.loginMsg.PrivilegeKey = util.GetAuthKey(g.GlbClientCfg.Token, now)
|
||||
ctl.loginMsg.Timestamp = now
|
||||
ctl.loginMsg.RunId = ctl.runId
|
||||
|
||||
@@ -234,7 +240,7 @@ func (ctl *Control) login() (err error) {
|
||||
ctl.conn = conn
|
||||
// update runId got from server
|
||||
ctl.runId = loginRespMsg.RunId
|
||||
config.ClientCommonCfg.ServerUdpPort = loginRespMsg.ServerUdpPort
|
||||
g.GlbClientCfg.ServerUdpPort = loginRespMsg.ServerUdpPort
|
||||
ctl.ClearLogPrefix()
|
||||
ctl.AddLogPrefix(loginRespMsg.RunId)
|
||||
ctl.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunId, loginRespMsg.ServerUdpPort)
|
||||
@@ -242,7 +248,7 @@ func (ctl *Control) login() (err error) {
|
||||
}
|
||||
|
||||
func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
|
||||
if config.ClientCommonCfg.TcpMux {
|
||||
if g.GlbClientCfg.TcpMux {
|
||||
stream, errRet := ctl.session.OpenStream()
|
||||
if errRet != nil {
|
||||
err = errRet
|
||||
@@ -251,8 +257,8 @@ func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
|
||||
}
|
||||
conn = frpNet.WrapConn(stream)
|
||||
} else {
|
||||
conn, err = frpNet.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
|
||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
|
||||
conn, err = frpNet.ConnectServerByProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol,
|
||||
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort))
|
||||
if err != nil {
|
||||
ctl.Warn("start new connection to server error: %v", err)
|
||||
return
|
||||
@@ -266,12 +272,13 @@ func (ctl *Control) reader() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ctl.Error("panic error: %v", err)
|
||||
ctl.Error(string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
defer ctl.readerShutdown.Done()
|
||||
defer close(ctl.closedCh)
|
||||
|
||||
encReader := crypto.NewReader(ctl.conn, []byte(config.ClientCommonCfg.PrivilegeToken))
|
||||
encReader := crypto.NewReader(ctl.conn, []byte(g.GlbClientCfg.Token))
|
||||
for {
|
||||
if m, err := msg.ReadMsg(encReader); err != nil {
|
||||
if err == io.EOF {
|
||||
@@ -290,7 +297,7 @@ func (ctl *Control) reader() {
|
||||
// writer writes messages got from sendCh to frps
|
||||
func (ctl *Control) writer() {
|
||||
defer ctl.writerShutdown.Done()
|
||||
encWriter, err := crypto.NewWriter(ctl.conn, []byte(config.ClientCommonCfg.PrivilegeToken))
|
||||
encWriter, err := crypto.NewWriter(ctl.conn, []byte(g.GlbClientCfg.Token))
|
||||
if err != nil {
|
||||
ctl.conn.Error("crypto new writer error: %v", err)
|
||||
ctl.conn.Close()
|
||||
@@ -314,11 +321,12 @@ func (ctl *Control) msgHandler() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ctl.Error("panic error: %v", err)
|
||||
ctl.Error(string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
defer ctl.msgHandlerShutdown.Done()
|
||||
|
||||
hbSend := time.NewTicker(time.Duration(config.ClientCommonCfg.HeartBeatInterval) * time.Second)
|
||||
hbSend := time.NewTicker(time.Duration(g.GlbClientCfg.HeartBeatInterval) * time.Second)
|
||||
defer hbSend.Stop()
|
||||
hbCheck := time.NewTicker(time.Second)
|
||||
defer hbCheck.Stop()
|
||||
@@ -332,7 +340,7 @@ func (ctl *Control) msgHandler() {
|
||||
ctl.Debug("send heartbeat to server")
|
||||
ctl.sendCh <- &msg.Ping{}
|
||||
case <-hbCheck.C:
|
||||
if time.Since(ctl.lastPong) > time.Duration(config.ClientCommonCfg.HeartBeatTimeout)*time.Second {
|
||||
if time.Since(ctl.lastPong) > time.Duration(g.GlbClientCfg.HeartBeatTimeout)*time.Second {
|
||||
ctl.Warn("heartbeat timeout")
|
||||
// let reader() stop
|
||||
ctl.conn.Close()
|
||||
@@ -360,20 +368,20 @@ func (ctl *Control) msgHandler() {
|
||||
// If controler is notified by closedCh, reader and writer and handler will exit, then recall these functions.
|
||||
func (ctl *Control) worker() {
|
||||
go ctl.msgHandler()
|
||||
go ctl.writer()
|
||||
go ctl.reader()
|
||||
go ctl.writer()
|
||||
|
||||
var err error
|
||||
maxDelayTime := 20 * time.Second
|
||||
delayTime := time.Second
|
||||
|
||||
checkInterval := 10 * time.Second
|
||||
checkInterval := 60 * time.Second
|
||||
checkProxyTicker := time.NewTicker(checkInterval)
|
||||
for {
|
||||
select {
|
||||
case <-checkProxyTicker.C:
|
||||
// every 10 seconds, check which proxy registered failed and reregister it to server
|
||||
ctl.pm.CheckAndStartProxy()
|
||||
// check which proxy registered failed and reregister it to server
|
||||
ctl.pm.CheckAndStartProxy([]string{ProxyStatusStartErr, ProxyStatusClosed})
|
||||
case _, ok := <-ctl.closedCh:
|
||||
// we won't get any variable from this channel
|
||||
if !ok {
|
||||
@@ -413,8 +421,8 @@ func (ctl *Control) worker() {
|
||||
}
|
||||
|
||||
// init related channels and variables
|
||||
ctl.sendCh = make(chan msg.Message, 10)
|
||||
ctl.readCh = make(chan msg.Message, 10)
|
||||
ctl.sendCh = make(chan msg.Message, 100)
|
||||
ctl.readCh = make(chan msg.Message, 100)
|
||||
ctl.closedCh = make(chan int)
|
||||
ctl.readerShutdown = shutdown.New()
|
||||
ctl.writerShutdown = shutdown.New()
|
||||
@@ -427,7 +435,7 @@ func (ctl *Control) worker() {
|
||||
go ctl.reader()
|
||||
|
||||
// start all configured proxies
|
||||
ctl.pm.CheckAndStartProxy()
|
||||
ctl.pm.CheckAndStartProxy([]string{ProxyStatusNew, ProxyStatusClosed})
|
||||
|
||||
checkProxyTicker.Stop()
|
||||
checkProxyTicker = time.NewTicker(checkInterval)
|
||||
@@ -437,6 +445,6 @@ func (ctl *Control) worker() {
|
||||
}
|
||||
|
||||
func (ctl *Control) reloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf) error {
|
||||
err := ctl.pm.Reload(pxyCfgs, visitorCfgs)
|
||||
err := ctl.pm.Reload(pxyCfgs, visitorCfgs, true)
|
||||
return err
|
||||
}
|
||||
|
@@ -22,15 +22,17 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
"github.com/fatedier/frp/models/plugin"
|
||||
"github.com/fatedier/frp/models/proto/udp"
|
||||
"github.com/fatedier/frp/utils/errors"
|
||||
frpIo "github.com/fatedier/frp/utils/io"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
"github.com/fatedier/frp/utils/pool"
|
||||
|
||||
"github.com/fatedier/golib/errors"
|
||||
frpIo "github.com/fatedier/golib/io"
|
||||
"github.com/fatedier/golib/pool"
|
||||
)
|
||||
|
||||
// Proxy defines how to deal with work connections for different proxy type.
|
||||
@@ -46,7 +48,7 @@ type Proxy interface {
|
||||
|
||||
func NewProxy(pxyConf config.ProxyConf) (pxy Proxy) {
|
||||
baseProxy := BaseProxy{
|
||||
Logger: log.NewPrefixLogger(pxyConf.GetName()),
|
||||
Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName),
|
||||
}
|
||||
switch cfg := pxyConf.(type) {
|
||||
case *config.TcpProxyConf:
|
||||
@@ -115,7 +117,7 @@ func (pxy *TcpProxy) Close() {
|
||||
|
||||
func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
||||
[]byte(g.GlbClientCfg.Token))
|
||||
}
|
||||
|
||||
// HTTP
|
||||
@@ -144,7 +146,7 @@ func (pxy *HttpProxy) Close() {
|
||||
|
||||
func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn) {
|
||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
||||
[]byte(g.GlbClientCfg.Token))
|
||||
}
|
||||
|
||||
// HTTPS
|
||||
@@ -173,7 +175,7 @@ func (pxy *HttpsProxy) Close() {
|
||||
|
||||
func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn) {
|
||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
||||
[]byte(g.GlbClientCfg.Token))
|
||||
}
|
||||
|
||||
// STCP
|
||||
@@ -202,7 +204,7 @@ func (pxy *StcpProxy) Close() {
|
||||
|
||||
func (pxy *StcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
|
||||
[]byte(config.ClientCommonCfg.PrivilegeToken))
|
||||
[]byte(g.GlbClientCfg.Token))
|
||||
}
|
||||
|
||||
// XTCP
|
||||
@@ -243,7 +245,7 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||
Sid: natHoleSidMsg.Sid,
|
||||
}
|
||||
raddr, _ := net.ResolveUDPAddr("udp",
|
||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerUdpPort))
|
||||
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerUdpPort))
|
||||
clientConn, err := net.DialUDP("udp", nil, raddr)
|
||||
defer clientConn.Close()
|
||||
|
||||
@@ -270,6 +272,12 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn) {
|
||||
}
|
||||
clientConn.SetReadDeadline(time.Time{})
|
||||
clientConn.Close()
|
||||
|
||||
if natHoleRespMsg.Error != "" {
|
||||
pxy.Error("natHoleRespMsg get error info: %s", natHoleRespMsg.Error)
|
||||
return
|
||||
}
|
||||
|
||||
pxy.Trace("get natHoleRespMsg, sid [%s], client address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr)
|
||||
|
||||
// Send sid to visitor udp address.
|
||||
|
@@ -6,14 +6,16 @@ import (
|
||||
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
"github.com/fatedier/frp/utils/errors"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
|
||||
"github.com/fatedier/golib/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
ProxyStatusNew = "new"
|
||||
ProxyStatusStartErr = "start error"
|
||||
ProxyStatusWaitStart = "wait start"
|
||||
ProxyStatusRunning = "running"
|
||||
ProxyStatusClosed = "closed"
|
||||
)
|
||||
@@ -61,22 +63,18 @@ type ProxyStatus struct {
|
||||
|
||||
func NewProxyWrapper(cfg config.ProxyConf) *ProxyWrapper {
|
||||
return &ProxyWrapper{
|
||||
Name: cfg.GetName(),
|
||||
Type: cfg.GetType(),
|
||||
Name: cfg.GetBaseInfo().ProxyName,
|
||||
Type: cfg.GetBaseInfo().ProxyType,
|
||||
Status: ProxyStatusNew,
|
||||
Cfg: cfg,
|
||||
pxy: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (pw *ProxyWrapper) IsRunning() bool {
|
||||
func (pw *ProxyWrapper) GetStatusStr() string {
|
||||
pw.mu.RLock()
|
||||
defer pw.mu.RUnlock()
|
||||
if pw.Status == ProxyStatusRunning {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return pw.Status
|
||||
}
|
||||
|
||||
func (pw *ProxyWrapper) GetStatus() *ProxyStatus {
|
||||
@@ -93,6 +91,12 @@ func (pw *ProxyWrapper) GetStatus() *ProxyStatus {
|
||||
return ps
|
||||
}
|
||||
|
||||
func (pw *ProxyWrapper) WaitStart() {
|
||||
pw.mu.Lock()
|
||||
defer pw.mu.Unlock()
|
||||
pw.Status = ProxyStatusWaitStart
|
||||
}
|
||||
|
||||
func (pw *ProxyWrapper) Start(remoteAddr string, serverRespErr string) error {
|
||||
if pw.pxy != nil {
|
||||
pw.pxy.Close()
|
||||
@@ -210,7 +214,8 @@ func (pm *ProxyManager) CloseProxies() {
|
||||
}
|
||||
}
|
||||
|
||||
func (pm *ProxyManager) CheckAndStartProxy() {
|
||||
// pxyStatus: check and start proxies in which status
|
||||
func (pm *ProxyManager) CheckAndStartProxy(pxyStatus []string) {
|
||||
pm.mu.RLock()
|
||||
defer pm.mu.RUnlock()
|
||||
if pm.closed {
|
||||
@@ -219,35 +224,46 @@ func (pm *ProxyManager) CheckAndStartProxy() {
|
||||
}
|
||||
|
||||
for _, pxy := range pm.proxies {
|
||||
if !pxy.IsRunning() {
|
||||
status := pxy.GetStatusStr()
|
||||
for _, s := range pxyStatus {
|
||||
if status == s {
|
||||
var newProxyMsg msg.NewProxy
|
||||
pxy.Cfg.UnMarshalToMsg(&newProxyMsg)
|
||||
pxy.Cfg.MarshalToMsg(&newProxyMsg)
|
||||
err := pm.sendMsg(&newProxyMsg)
|
||||
if err != nil {
|
||||
pm.Warn("[%s] proxy send NewProxy message error")
|
||||
return
|
||||
}
|
||||
pxy.WaitStart()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, cfg := range pm.visitorCfgs {
|
||||
if _, exist := pm.visitors[cfg.GetName()]; !exist {
|
||||
pm.Info("try to start visitor [%s]", cfg.GetName())
|
||||
name := cfg.GetBaseInfo().ProxyName
|
||||
if _, exist := pm.visitors[name]; !exist {
|
||||
pm.Info("try to start visitor [%s]", name)
|
||||
visitor := NewVisitor(pm.ctl, cfg)
|
||||
err := visitor.Run()
|
||||
if err != nil {
|
||||
visitor.Warn("start error: %v", err)
|
||||
continue
|
||||
}
|
||||
pm.visitors[cfg.GetName()] = visitor
|
||||
pm.visitors[name] = visitor
|
||||
visitor.Info("start visitor success")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf) error {
|
||||
func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf, startNow bool) error {
|
||||
pm.mu.Lock()
|
||||
defer pm.mu.Unlock()
|
||||
defer func() {
|
||||
pm.mu.Unlock()
|
||||
if startNow {
|
||||
go pm.CheckAndStartProxy([]string{ProxyStatusNew})
|
||||
}
|
||||
}()
|
||||
if pm.closed {
|
||||
err := fmt.Errorf("Reload error: ProxyManager is closed now")
|
||||
pm.Warn(err.Error())
|
||||
|
@@ -15,6 +15,7 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
)
|
||||
@@ -41,12 +42,12 @@ func (svr *Service) Run() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.ClientCommonCfg.AdminPort != 0 {
|
||||
err = svr.RunAdminServer(config.ClientCommonCfg.AdminAddr, config.ClientCommonCfg.AdminPort)
|
||||
if g.GlbClientCfg.AdminPort != 0 {
|
||||
err = svr.RunAdminServer(g.GlbClientCfg.AdminAddr, g.GlbClientCfg.AdminPort)
|
||||
if err != nil {
|
||||
log.Warn("run admin server error: %v", err)
|
||||
}
|
||||
log.Info("admin server listen on %s:%d", config.ClientCommonCfg.AdminAddr, config.ClientCommonCfg.AdminPort)
|
||||
log.Info("admin server listen on %s:%d", g.GlbClientCfg.AdminAddr, g.GlbClientCfg.AdminPort)
|
||||
}
|
||||
|
||||
<-svr.closedCh
|
||||
|
@@ -26,13 +26,15 @@ import (
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
frpIo "github.com/fatedier/frp/utils/io"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
"github.com/fatedier/frp/utils/pool"
|
||||
"github.com/fatedier/frp/utils/util"
|
||||
|
||||
frpIo "github.com/fatedier/golib/io"
|
||||
"github.com/fatedier/golib/pool"
|
||||
)
|
||||
|
||||
// Visitor is used for forward traffics from local port tot remote service.
|
||||
@@ -45,7 +47,7 @@ type Visitor interface {
|
||||
func NewVisitor(ctl *Control, pxyConf config.ProxyConf) (visitor Visitor) {
|
||||
baseVisitor := BaseVisitor{
|
||||
ctl: ctl,
|
||||
Logger: log.NewPrefixLogger(pxyConf.GetName()),
|
||||
Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName),
|
||||
}
|
||||
switch cfg := pxyConf.(type) {
|
||||
case *config.StcpProxyConf:
|
||||
@@ -193,13 +195,13 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
|
||||
defer userConn.Close()
|
||||
|
||||
sv.Debug("get a new xtcp user connection")
|
||||
if config.ClientCommonCfg.ServerUdpPort == 0 {
|
||||
if g.GlbClientCfg.ServerUdpPort == 0 {
|
||||
sv.Error("xtcp is not supported by server")
|
||||
return
|
||||
}
|
||||
|
||||
raddr, err := net.ResolveUDPAddr("udp",
|
||||
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerUdpPort))
|
||||
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerUdpPort))
|
||||
visitorConn, err := net.DialUDP("udp", nil, raddr)
|
||||
defer visitorConn.Close()
|
||||
|
||||
@@ -233,6 +235,11 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
|
||||
visitorConn.SetReadDeadline(time.Time{})
|
||||
pool.PutBuf(buf)
|
||||
|
||||
if natHoleRespMsg.Error != "" {
|
||||
sv.Error("natHoleRespMsg get error info: %s", natHoleRespMsg.Error)
|
||||
return
|
||||
}
|
||||
|
||||
sv.Trace("get natHoleRespMsg, sid [%s], client address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr)
|
||||
|
||||
// Close visitorConn, so we can use it's local address.
|
||||
|
286
cmd/frpc/main.go
286
cmd/frpc/main.go
@@ -15,291 +15,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
"github.com/fatedier/frp/cmd/frpc/sub"
|
||||
|
||||
docopt "github.com/docopt/docopt-go"
|
||||
"github.com/rodaine/table"
|
||||
ini "github.com/vaughan0/go-ini"
|
||||
|
||||
"github.com/fatedier/frp/client"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
"github.com/fatedier/frp/utils/version"
|
||||
"github.com/fatedier/golib/crypto"
|
||||
)
|
||||
|
||||
var (
|
||||
configFile string = "./frpc.ini"
|
||||
)
|
||||
|
||||
var usage string = `frpc is the client of frp
|
||||
|
||||
Usage:
|
||||
frpc [-c config_file] [-L log_file] [--log-level=<log_level>] [--server-addr=<server_addr>]
|
||||
frpc reload [-c config_file]
|
||||
frpc status [-c config_file]
|
||||
frpc -h | --help
|
||||
frpc -v | --version
|
||||
|
||||
Options:
|
||||
-c config_file set config file
|
||||
-L log_file set output log file, including console
|
||||
--log-level=<log_level> set log level: debug, info, warn, error
|
||||
--server-addr=<server_addr> addr which frps is listening for, example: 0.0.0.0:7000
|
||||
-h --help show this screen
|
||||
-v --version show version
|
||||
`
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
confFile := "./frpc.ini"
|
||||
// the configures parsed from file will be replaced by those from command line if exist
|
||||
args, err := docopt.Parse(usage, nil, true, version.Full(), false)
|
||||
crypto.DefaultSalt = "frp"
|
||||
|
||||
if args["-c"] != nil {
|
||||
confFile = args["-c"].(string)
|
||||
}
|
||||
|
||||
conf, err := ini.LoadFile(confFile)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
config.ClientCommonCfg, err = config.LoadClientCommonConf(conf)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
config.ClientCommonCfg.ConfigFile = confFile
|
||||
|
||||
// check if reload command
|
||||
if args["reload"] != nil {
|
||||
if args["reload"].(bool) {
|
||||
if err = CmdReload(); err != nil {
|
||||
fmt.Printf("frps reload error: %v\n", err)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
fmt.Printf("reload success\n")
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if status command
|
||||
if args["status"] != nil {
|
||||
if args["status"].(bool) {
|
||||
if err = CmdStatus(); err != nil {
|
||||
fmt.Printf("frps get status error: %v\n", err)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if args["-L"] != nil {
|
||||
if args["-L"].(string) == "console" {
|
||||
config.ClientCommonCfg.LogWay = "console"
|
||||
} else {
|
||||
config.ClientCommonCfg.LogWay = "file"
|
||||
config.ClientCommonCfg.LogFile = args["-L"].(string)
|
||||
}
|
||||
}
|
||||
|
||||
if args["--log-level"] != nil {
|
||||
config.ClientCommonCfg.LogLevel = args["--log-level"].(string)
|
||||
}
|
||||
|
||||
if args["--server-addr"] != nil {
|
||||
addr := strings.Split(args["--server-addr"].(string), ":")
|
||||
if len(addr) != 2 {
|
||||
fmt.Println("--server-addr format error: example 0.0.0.0:7000")
|
||||
os.Exit(1)
|
||||
}
|
||||
serverPort, err := strconv.ParseInt(addr[1], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Println("--server-addr format error, example 0.0.0.0:7000")
|
||||
os.Exit(1)
|
||||
}
|
||||
config.ClientCommonCfg.ServerAddr = addr[0]
|
||||
config.ClientCommonCfg.ServerPort = int(serverPort)
|
||||
}
|
||||
|
||||
if args["-v"] != nil {
|
||||
if args["-v"].(bool) {
|
||||
fmt.Println(version.Full())
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromFile(config.ClientCommonCfg.User, conf, config.ClientCommonCfg.Start)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
log.InitLog(config.ClientCommonCfg.LogWay, config.ClientCommonCfg.LogFile,
|
||||
config.ClientCommonCfg.LogLevel, config.ClientCommonCfg.LogMaxDays)
|
||||
|
||||
svr := client.NewService(pxyCfgs, visitorCfgs)
|
||||
|
||||
// Capture the exit signal if we use kcp.
|
||||
if config.ClientCommonCfg.Protocol == "kcp" {
|
||||
go HandleSignal(svr)
|
||||
}
|
||||
|
||||
err = svr.Run()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func HandleSignal(svr *client.Service) {
|
||||
ch := make(chan os.Signal)
|
||||
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-ch
|
||||
svr.Close()
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func CmdReload() error {
|
||||
if config.ClientCommonCfg.AdminPort == 0 {
|
||||
return fmt.Errorf("admin_port shoud be set if you want to use reload feature")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "http://"+
|
||||
config.ClientCommonCfg.AdminAddr+":"+fmt.Sprintf("%d", config.ClientCommonCfg.AdminPort)+"/api/reload", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(config.ClientCommonCfg.AdminUser+":"+
|
||||
config.ClientCommonCfg.AdminPwd))
|
||||
|
||||
req.Header.Add("Authorization", authStr)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res := &client.GeneralResponse{}
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
|
||||
} else if res.Code != 0 {
|
||||
return fmt.Errorf(res.Msg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func CmdStatus() error {
|
||||
if config.ClientCommonCfg.AdminPort == 0 {
|
||||
return fmt.Errorf("admin_port shoud be set if you want to get proxy status")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "http://"+
|
||||
config.ClientCommonCfg.AdminAddr+":"+fmt.Sprintf("%d", config.ClientCommonCfg.AdminPort)+"/api/status", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(config.ClientCommonCfg.AdminUser+":"+
|
||||
config.ClientCommonCfg.AdminPwd))
|
||||
|
||||
req.Header.Add("Authorization", authStr)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res := &client.StatusResp{}
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
|
||||
}
|
||||
|
||||
fmt.Println("Proxy Status...")
|
||||
if len(res.Tcp) > 0 {
|
||||
fmt.Printf("TCP")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Tcp {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.Udp) > 0 {
|
||||
fmt.Printf("UDP")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Udp {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.Http) > 0 {
|
||||
fmt.Printf("HTTP")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Http {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.Https) > 0 {
|
||||
fmt.Printf("HTTPS")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Https {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.Stcp) > 0 {
|
||||
fmt.Printf("STCP")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Stcp {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.Xtcp) > 0 {
|
||||
fmt.Printf("XTCP")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Xtcp {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
sub.Execute()
|
||||
}
|
||||
|
96
cmd/frpc/sub/http.go
Normal file
96
cmd/frpc/sub/http.go
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// 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"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/models/consts"
|
||||
)
|
||||
|
||||
func init() {
|
||||
httpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
|
||||
httpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
|
||||
httpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
|
||||
httpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
|
||||
httpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||
httpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
|
||||
httpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
|
||||
|
||||
httpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
|
||||
httpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
|
||||
httpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
|
||||
httpCmd.PersistentFlags().StringVarP(&customDomains, "custom_domain", "d", "", "custom domain")
|
||||
httpCmd.PersistentFlags().StringVarP(&subDomain, "sd", "", "", "sub domain")
|
||||
httpCmd.PersistentFlags().StringVarP(&locations, "locations", "", "", "locations")
|
||||
httpCmd.PersistentFlags().StringVarP(&httpUser, "http_user", "", "", "http auth user")
|
||||
httpCmd.PersistentFlags().StringVarP(&httpPwd, "http_pwd", "", "", "http auth password")
|
||||
httpCmd.PersistentFlags().StringVarP(&hostHeaderRewrite, "host_header_rewrite", "", "", "host header rewrite")
|
||||
httpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||
httpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||
|
||||
rootCmd.AddCommand(httpCmd)
|
||||
}
|
||||
|
||||
var httpCmd = &cobra.Command{
|
||||
Use: "http",
|
||||
Short: "Run frpc with a single http proxy",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := parseClientCommonCfg(CfgFileTypeCmd, "")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cfg := &config.HttpProxyConf{}
|
||||
var prefix string
|
||||
if user != "" {
|
||||
prefix = user + "."
|
||||
}
|
||||
cfg.ProxyName = prefix + proxyName
|
||||
cfg.ProxyType = consts.HttpProxy
|
||||
cfg.LocalIp = localIp
|
||||
cfg.LocalPort = localPort
|
||||
cfg.CustomDomains = strings.Split(customDomains, ",")
|
||||
cfg.SubDomain = subDomain
|
||||
cfg.Locations = strings.Split(locations, ",")
|
||||
cfg.HttpUser = httpUser
|
||||
cfg.HttpPwd = httpPwd
|
||||
cfg.HostHeaderRewrite = hostHeaderRewrite
|
||||
cfg.UseEncryption = useEncryption
|
||||
cfg.UseCompression = useCompression
|
||||
|
||||
err = cfg.CheckForCli()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
proxyConfs := map[string]config.ProxyConf{
|
||||
cfg.ProxyName: cfg,
|
||||
}
|
||||
err = startService(proxyConfs, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
88
cmd/frpc/sub/https.go
Normal file
88
cmd/frpc/sub/https.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// 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"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/models/consts"
|
||||
)
|
||||
|
||||
func init() {
|
||||
httpsCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
|
||||
httpsCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
|
||||
httpsCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
|
||||
httpsCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
|
||||
httpsCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||
httpsCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
|
||||
httpsCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
|
||||
|
||||
httpsCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
|
||||
httpsCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
|
||||
httpsCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
|
||||
httpsCmd.PersistentFlags().StringVarP(&customDomains, "custom_domain", "d", "", "custom domain")
|
||||
httpsCmd.PersistentFlags().StringVarP(&subDomain, "sd", "", "", "sub domain")
|
||||
httpsCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||
httpsCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||
|
||||
rootCmd.AddCommand(httpsCmd)
|
||||
}
|
||||
|
||||
var httpsCmd = &cobra.Command{
|
||||
Use: "https",
|
||||
Short: "Run frpc with a single https proxy",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := parseClientCommonCfg(CfgFileTypeCmd, "")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cfg := &config.HttpsProxyConf{}
|
||||
var prefix string
|
||||
if user != "" {
|
||||
prefix = user + "."
|
||||
}
|
||||
cfg.ProxyName = prefix + proxyName
|
||||
cfg.ProxyType = consts.HttpsProxy
|
||||
cfg.LocalIp = localIp
|
||||
cfg.LocalPort = localPort
|
||||
cfg.CustomDomains = strings.Split(customDomains, ",")
|
||||
cfg.SubDomain = subDomain
|
||||
cfg.UseEncryption = useEncryption
|
||||
cfg.UseCompression = useCompression
|
||||
|
||||
err = cfg.CheckForCli()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
proxyConfs := map[string]config.ProxyConf{
|
||||
cfg.ProxyName: cfg,
|
||||
}
|
||||
err = startService(proxyConfs, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
92
cmd/frpc/sub/reload.go
Normal file
92
cmd/frpc/sub/reload.go
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// 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 (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fatedier/frp/client"
|
||||
"github.com/fatedier/frp/g"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(reloadCmd)
|
||||
}
|
||||
|
||||
var reloadCmd = &cobra.Command{
|
||||
Use: "reload",
|
||||
Short: "Hot-Reload frpc configuration",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := parseClientCommonCfg(CfgFileTypeIni, cfgFile)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = reload()
|
||||
if err != nil {
|
||||
fmt.Printf("frpc reload error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("reload success\n")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func reload() error {
|
||||
if g.GlbClientCfg.AdminPort == 0 {
|
||||
return fmt.Errorf("admin_port shoud be set if you want to use reload feature")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "http://"+
|
||||
g.GlbClientCfg.AdminAddr+":"+fmt.Sprintf("%d", g.GlbClientCfg.AdminPort)+"/api/reload", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(g.GlbClientCfg.AdminUser+":"+
|
||||
g.GlbClientCfg.AdminPwd))
|
||||
|
||||
req.Header.Add("Authorization", authStr)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res := &client.GeneralResponse{}
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
|
||||
} else if res.Code != 0 {
|
||||
return fmt.Errorf(res.Msg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
216
cmd/frpc/sub/root.go
Normal file
216
cmd/frpc/sub/root.go
Normal file
@@ -0,0 +1,216 @@
|
||||
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// 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 (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
ini "github.com/vaughan0/go-ini"
|
||||
|
||||
"github.com/fatedier/frp/client"
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
"github.com/fatedier/frp/utils/version"
|
||||
)
|
||||
|
||||
const (
|
||||
CfgFileTypeIni = iota
|
||||
CfgFileTypeCmd
|
||||
)
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
showVersion bool
|
||||
|
||||
serverAddr string
|
||||
user string
|
||||
protocol string
|
||||
token string
|
||||
logLevel string
|
||||
logFile string
|
||||
logMaxDays int
|
||||
|
||||
proxyName string
|
||||
localIp string
|
||||
localPort int
|
||||
remotePort int
|
||||
useEncryption bool
|
||||
useCompression bool
|
||||
customDomains string
|
||||
subDomain string
|
||||
httpUser string
|
||||
httpPwd string
|
||||
locations string
|
||||
hostHeaderRewrite string
|
||||
role string
|
||||
sk string
|
||||
serverName string
|
||||
bindAddr string
|
||||
bindPort int
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringVarP(&cfgFile, "", "c", "./frpc.ini", "config file of frpc")
|
||||
rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "frpc",
|
||||
Short: "frpc is the client of frp (https://github.com/fatedier/frp)",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if showVersion {
|
||||
fmt.Println(version.Full())
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not show command usage here.
|
||||
err := runClient(cfgFile)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func handleSignal(svr *client.Service) {
|
||||
ch := make(chan os.Signal)
|
||||
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-ch
|
||||
svr.Close()
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func parseClientCommonCfg(fileType int, filePath string) (err error) {
|
||||
if fileType == CfgFileTypeIni {
|
||||
err = parseClientCommonCfgFromIni(filePath)
|
||||
} else if fileType == CfgFileTypeCmd {
|
||||
err = parseClientCommonCfgFromCmd()
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
g.GlbClientCfg.CfgFile = cfgFile
|
||||
|
||||
err = g.GlbClientCfg.ClientCommonConf.Check()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func parseClientCommonCfgFromIni(filePath string) (err error) {
|
||||
b, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
content := string(b)
|
||||
|
||||
cfg, err := config.UnmarshalClientConfFromIni(&g.GlbClientCfg.ClientCommonConf, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.GlbClientCfg.ClientCommonConf = *cfg
|
||||
return
|
||||
}
|
||||
|
||||
func parseClientCommonCfgFromCmd() (err error) {
|
||||
strs := strings.Split(serverAddr, ":")
|
||||
if len(strs) < 2 {
|
||||
err = fmt.Errorf("invalid server_addr")
|
||||
return
|
||||
}
|
||||
if strs[0] != "" {
|
||||
g.GlbClientCfg.ServerAddr = strs[0]
|
||||
}
|
||||
g.GlbClientCfg.ServerPort, err = strconv.Atoi(strs[1])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("invalid server_addr")
|
||||
return
|
||||
}
|
||||
|
||||
g.GlbClientCfg.User = user
|
||||
g.GlbClientCfg.Protocol = protocol
|
||||
g.GlbClientCfg.Token = token
|
||||
g.GlbClientCfg.LogLevel = logLevel
|
||||
g.GlbClientCfg.LogFile = logFile
|
||||
g.GlbClientCfg.LogMaxDays = int64(logMaxDays)
|
||||
return nil
|
||||
}
|
||||
|
||||
func runClient(cfgFilePath string) (err error) {
|
||||
err = parseClientCommonCfg(CfgFileTypeIni, cfgFilePath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
conf, err := ini.LoadFile(cfgFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromIni(g.GlbClientCfg.User, conf, g.GlbClientCfg.Start)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = startService(pxyCfgs, visitorCfgs)
|
||||
return
|
||||
}
|
||||
|
||||
func startService(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf) (err error) {
|
||||
log.InitLog(g.GlbClientCfg.LogWay, g.GlbClientCfg.LogFile, g.GlbClientCfg.LogLevel, g.GlbClientCfg.LogMaxDays)
|
||||
if g.GlbClientCfg.DnsServer != "" {
|
||||
s := g.GlbClientCfg.DnsServer
|
||||
if !strings.Contains(s, ":") {
|
||||
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 := client.NewService(pxyCfgs, visitorCfgs)
|
||||
|
||||
// Capture the exit signal if we use kcp.
|
||||
if g.GlbClientCfg.Protocol == "kcp" {
|
||||
go handleSignal(svr)
|
||||
}
|
||||
|
||||
err = svr.Run()
|
||||
return
|
||||
}
|
146
cmd/frpc/sub/status.go
Normal file
146
cmd/frpc/sub/status.go
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// 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 (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/rodaine/table"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fatedier/frp/client"
|
||||
"github.com/fatedier/frp/g"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(statusCmd)
|
||||
}
|
||||
|
||||
var statusCmd = &cobra.Command{
|
||||
Use: "status",
|
||||
Short: "Overview of all proxies status",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := parseClientCommonCfg(CfgFileTypeIni, cfgFile)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = status()
|
||||
if err != nil {
|
||||
fmt.Printf("frpc get status error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func status() error {
|
||||
if g.GlbClientCfg.AdminPort == 0 {
|
||||
return fmt.Errorf("admin_port shoud be set if you want to get proxy status")
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("GET", "http://"+
|
||||
g.GlbClientCfg.AdminAddr+":"+fmt.Sprintf("%d", g.GlbClientCfg.AdminPort)+"/api/status", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(g.GlbClientCfg.AdminUser+":"+
|
||||
g.GlbClientCfg.AdminPwd))
|
||||
|
||||
req.Header.Add("Authorization", authStr)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res := &client.StatusResp{}
|
||||
err = json.Unmarshal(body, &res)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
|
||||
}
|
||||
|
||||
fmt.Println("Proxy Status...")
|
||||
if len(res.Tcp) > 0 {
|
||||
fmt.Printf("TCP")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Tcp {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.Udp) > 0 {
|
||||
fmt.Printf("UDP")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Udp {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.Http) > 0 {
|
||||
fmt.Printf("HTTP")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Http {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.Https) > 0 {
|
||||
fmt.Printf("HTTPS")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Https {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.Stcp) > 0 {
|
||||
fmt.Printf("STCP")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Stcp {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
if len(res.Xtcp) > 0 {
|
||||
fmt.Printf("XTCP")
|
||||
tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
|
||||
for _, ps := range res.Xtcp {
|
||||
tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
|
||||
}
|
||||
tbl.Print()
|
||||
fmt.Println("")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
104
cmd/frpc/sub/stcp.go
Normal file
104
cmd/frpc/sub/stcp.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// 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/models/config"
|
||||
"github.com/fatedier/frp/models/consts"
|
||||
)
|
||||
|
||||
func init() {
|
||||
stcpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
|
||||
stcpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
|
||||
stcpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
|
||||
stcpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
|
||||
stcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||
stcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
|
||||
stcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
|
||||
|
||||
stcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
|
||||
stcpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role")
|
||||
stcpCmd.PersistentFlags().StringVarP(&sk, "sk", "", "", "secret key")
|
||||
stcpCmd.PersistentFlags().StringVarP(&serverName, "server_name", "", "", "server name")
|
||||
stcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
|
||||
stcpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
|
||||
stcpCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "", "bind addr")
|
||||
stcpCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "", 0, "bind port")
|
||||
stcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||
stcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||
|
||||
rootCmd.AddCommand(stcpCmd)
|
||||
}
|
||||
|
||||
var stcpCmd = &cobra.Command{
|
||||
Use: "stcp",
|
||||
Short: "Run frpc with a single stcp proxy",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := parseClientCommonCfg(CfgFileTypeCmd, "")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cfg := &config.StcpProxyConf{}
|
||||
var prefix string
|
||||
if user != "" {
|
||||
prefix = user + "."
|
||||
}
|
||||
cfg.ProxyName = prefix + proxyName
|
||||
cfg.ProxyType = consts.StcpProxy
|
||||
cfg.Role = role
|
||||
cfg.Sk = sk
|
||||
cfg.ServerName = serverName
|
||||
cfg.LocalIp = localIp
|
||||
cfg.LocalPort = localPort
|
||||
cfg.BindAddr = bindAddr
|
||||
cfg.BindPort = bindPort
|
||||
cfg.UseEncryption = useEncryption
|
||||
cfg.UseCompression = useCompression
|
||||
|
||||
err = cfg.CheckForCli()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if cfg.Role == "server" {
|
||||
proxyConfs := map[string]config.ProxyConf{
|
||||
cfg.ProxyName: cfg,
|
||||
}
|
||||
err = startService(proxyConfs, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
visitorConfs := map[string]config.ProxyConf{
|
||||
cfg.ProxyName: cfg,
|
||||
}
|
||||
err = startService(nil, visitorConfs)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
85
cmd/frpc/sub/tcp.go
Normal file
85
cmd/frpc/sub/tcp.go
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// 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/models/config"
|
||||
"github.com/fatedier/frp/models/consts"
|
||||
)
|
||||
|
||||
func init() {
|
||||
tcpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
|
||||
tcpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
|
||||
tcpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
|
||||
tcpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
|
||||
tcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||
tcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
|
||||
tcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
|
||||
|
||||
tcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
|
||||
tcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
|
||||
tcpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
|
||||
tcpCmd.PersistentFlags().IntVarP(&remotePort, "remote_port", "r", 0, "remote port")
|
||||
tcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||
tcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||
|
||||
rootCmd.AddCommand(tcpCmd)
|
||||
}
|
||||
|
||||
var tcpCmd = &cobra.Command{
|
||||
Use: "tcp",
|
||||
Short: "Run frpc with a single tcp proxy",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := parseClientCommonCfg(CfgFileTypeCmd, "")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cfg := &config.TcpProxyConf{}
|
||||
var prefix string
|
||||
if user != "" {
|
||||
prefix = user + "."
|
||||
}
|
||||
cfg.ProxyName = prefix + proxyName
|
||||
cfg.ProxyType = consts.TcpProxy
|
||||
cfg.LocalIp = localIp
|
||||
cfg.LocalPort = localPort
|
||||
cfg.RemotePort = remotePort
|
||||
cfg.UseEncryption = useEncryption
|
||||
cfg.UseCompression = useCompression
|
||||
|
||||
err = cfg.CheckForCli()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
proxyConfs := map[string]config.ProxyConf{
|
||||
cfg.ProxyName: cfg,
|
||||
}
|
||||
err = startService(proxyConfs, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
85
cmd/frpc/sub/udp.go
Normal file
85
cmd/frpc/sub/udp.go
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// 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/models/config"
|
||||
"github.com/fatedier/frp/models/consts"
|
||||
)
|
||||
|
||||
func init() {
|
||||
udpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
|
||||
udpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
|
||||
udpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
|
||||
udpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
|
||||
udpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||
udpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
|
||||
udpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
|
||||
|
||||
udpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
|
||||
udpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
|
||||
udpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
|
||||
udpCmd.PersistentFlags().IntVarP(&remotePort, "remote_port", "r", 0, "remote port")
|
||||
udpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||
udpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||
|
||||
rootCmd.AddCommand(udpCmd)
|
||||
}
|
||||
|
||||
var udpCmd = &cobra.Command{
|
||||
Use: "udp",
|
||||
Short: "Run frpc with a single udp proxy",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := parseClientCommonCfg(CfgFileTypeCmd, "")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cfg := &config.UdpProxyConf{}
|
||||
var prefix string
|
||||
if user != "" {
|
||||
prefix = user + "."
|
||||
}
|
||||
cfg.ProxyName = prefix + proxyName
|
||||
cfg.ProxyType = consts.UdpProxy
|
||||
cfg.LocalIp = localIp
|
||||
cfg.LocalPort = localPort
|
||||
cfg.RemotePort = remotePort
|
||||
cfg.UseEncryption = useEncryption
|
||||
cfg.UseCompression = useCompression
|
||||
|
||||
err = cfg.CheckForCli()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
proxyConfs := map[string]config.ProxyConf{
|
||||
cfg.ProxyName: cfg,
|
||||
}
|
||||
err = startService(proxyConfs, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
104
cmd/frpc/sub/xtcp.go
Normal file
104
cmd/frpc/sub/xtcp.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// 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/models/config"
|
||||
"github.com/fatedier/frp/models/consts"
|
||||
)
|
||||
|
||||
func init() {
|
||||
xtcpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
|
||||
xtcpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
|
||||
xtcpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
|
||||
xtcpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
|
||||
xtcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||
xtcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
|
||||
xtcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
|
||||
|
||||
xtcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
|
||||
xtcpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role")
|
||||
xtcpCmd.PersistentFlags().StringVarP(&sk, "sk", "", "", "secret key")
|
||||
xtcpCmd.PersistentFlags().StringVarP(&serverName, "server_name", "", "", "server name")
|
||||
xtcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
|
||||
xtcpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
|
||||
xtcpCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "", "bind addr")
|
||||
xtcpCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "", 0, "bind port")
|
||||
xtcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||
xtcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||
|
||||
rootCmd.AddCommand(xtcpCmd)
|
||||
}
|
||||
|
||||
var xtcpCmd = &cobra.Command{
|
||||
Use: "xtcp",
|
||||
Short: "Run frpc with a single xtcp proxy",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
err := parseClientCommonCfg(CfgFileTypeCmd, "")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cfg := &config.XtcpProxyConf{}
|
||||
var prefix string
|
||||
if user != "" {
|
||||
prefix = user + "."
|
||||
}
|
||||
cfg.ProxyName = prefix + proxyName
|
||||
cfg.ProxyType = consts.XtcpProxy
|
||||
cfg.Role = role
|
||||
cfg.Sk = sk
|
||||
cfg.ServerName = serverName
|
||||
cfg.LocalIp = localIp
|
||||
cfg.LocalPort = localPort
|
||||
cfg.BindAddr = bindAddr
|
||||
cfg.BindPort = bindPort
|
||||
cfg.UseEncryption = useEncryption
|
||||
cfg.UseCompression = useCompression
|
||||
|
||||
err = cfg.CheckForCli()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if cfg.Role == "server" {
|
||||
proxyConfs := map[string]config.ProxyConf{
|
||||
cfg.ProxyName: cfg,
|
||||
}
|
||||
err = startService(proxyConfs, nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
visitorConfs := map[string]config.ProxyConf{
|
||||
cfg.ProxyName: cfg,
|
||||
}
|
||||
err = startService(nil, visitorConfs)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
101
cmd/frps/main.go
101
cmd/frps/main.go
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 fatedier, fatedier@gmail.com
|
||||
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -15,104 +15,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
docopt "github.com/docopt/docopt-go"
|
||||
ini "github.com/vaughan0/go-ini"
|
||||
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/server"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
"github.com/fatedier/frp/utils/version"
|
||||
"github.com/fatedier/golib/crypto"
|
||||
)
|
||||
|
||||
var usage string = `frps is the server of frp
|
||||
|
||||
Usage:
|
||||
frps [-c config_file] [-L log_file] [--log-level=<log_level>] [--addr=<bind_addr>]
|
||||
frps -h | --help
|
||||
frps -v | --version
|
||||
|
||||
Options:
|
||||
-c config_file set config file
|
||||
-L log_file set output log file, including console
|
||||
--log-level=<log_level> set log level: debug, info, warn, error
|
||||
--addr=<bind_addr> listen addr for client, example: 0.0.0.0:7000
|
||||
-h --help show this screen
|
||||
-v --version show version
|
||||
`
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
confFile := "./frps.ini"
|
||||
// the configures parsed from file will be replaced by those from command line if exist
|
||||
args, err := docopt.Parse(usage, nil, true, version.Full(), false)
|
||||
crypto.DefaultSalt = "frp"
|
||||
|
||||
if args["-c"] != nil {
|
||||
confFile = args["-c"].(string)
|
||||
}
|
||||
|
||||
conf, err := ini.LoadFile(confFile)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
config.ServerCommonCfg, err = config.LoadServerCommonConf(conf)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if args["-L"] != nil {
|
||||
if args["-L"].(string) == "console" {
|
||||
config.ServerCommonCfg.LogWay = "console"
|
||||
} else {
|
||||
config.ServerCommonCfg.LogWay = "file"
|
||||
config.ServerCommonCfg.LogFile = args["-L"].(string)
|
||||
}
|
||||
}
|
||||
|
||||
if args["--log-level"] != nil {
|
||||
config.ServerCommonCfg.LogLevel = args["--log-level"].(string)
|
||||
}
|
||||
|
||||
if args["--addr"] != nil {
|
||||
addr := strings.Split(args["--addr"].(string), ":")
|
||||
if len(addr) != 2 {
|
||||
fmt.Println("--addr format error: example 0.0.0.0:7000")
|
||||
os.Exit(1)
|
||||
}
|
||||
bindPort, err := strconv.ParseInt(addr[1], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Println("--addr format error, example 0.0.0.0:7000")
|
||||
os.Exit(1)
|
||||
}
|
||||
config.ServerCommonCfg.BindAddr = addr[0]
|
||||
config.ServerCommonCfg.BindPort = int(bindPort)
|
||||
}
|
||||
|
||||
if args["-v"] != nil {
|
||||
if args["-v"].(bool) {
|
||||
fmt.Println(version.Full())
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
log.InitLog(config.ServerCommonCfg.LogWay, config.ServerCommonCfg.LogFile,
|
||||
config.ServerCommonCfg.LogLevel, config.ServerCommonCfg.LogMaxDays)
|
||||
|
||||
svr, err := server.NewService()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log.Info("Start frps success")
|
||||
if config.ServerCommonCfg.PrivilegeMode == true {
|
||||
log.Info("PrivilegeMode is enabled, you should pay more attention to security issues")
|
||||
}
|
||||
server.ServerService = svr
|
||||
svr.Run()
|
||||
Execute()
|
||||
}
|
||||
|
194
cmd/frps/root.go
Normal file
194
cmd/frps/root.go
Normal file
@@ -0,0 +1,194 @@
|
||||
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// 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"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/server"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
"github.com/fatedier/frp/utils/version"
|
||||
)
|
||||
|
||||
const (
|
||||
CfgFileTypeIni = iota
|
||||
CfgFileTypeCmd
|
||||
)
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
showVersion bool
|
||||
|
||||
bindAddr string
|
||||
bindPort int
|
||||
bindUdpPort int
|
||||
kcpBindPort int
|
||||
proxyBindAddr string
|
||||
vhostHttpPort int
|
||||
vhostHttpsPort int
|
||||
dashboardAddr string
|
||||
dashboardPort int
|
||||
dashboardUser string
|
||||
dashboardPwd string
|
||||
assetsDir string
|
||||
logFile string
|
||||
logWay string
|
||||
logLevel string
|
||||
logMaxDays int64
|
||||
token string
|
||||
authTimeout int64
|
||||
subDomainHost string
|
||||
tcpMux bool
|
||||
allowPorts string
|
||||
maxPoolCount int64
|
||||
maxPortsPerClient int64
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringVarP(&cfgFile, "", "c", "", "config file of frps")
|
||||
rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
|
||||
|
||||
rootCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "0.0.0.0", "bind address")
|
||||
rootCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "p", 7000, "bind port")
|
||||
rootCmd.PersistentFlags().IntVarP(&bindUdpPort, "bind_udp_port", "", 0, "bind udp port")
|
||||
rootCmd.PersistentFlags().IntVarP(&kcpBindPort, "kcp_bind_port", "", 0, "kcp bind udp port")
|
||||
rootCmd.PersistentFlags().StringVarP(&proxyBindAddr, "proxy_bind_addr", "", "0.0.0.0", "proxy bind address")
|
||||
rootCmd.PersistentFlags().IntVarP(&vhostHttpPort, "vhost_http_port", "", 0, "vhost http port")
|
||||
rootCmd.PersistentFlags().IntVarP(&vhostHttpsPort, "vhost_https_port", "", 0, "vhost https port")
|
||||
rootCmd.PersistentFlags().StringVarP(&dashboardAddr, "dashboard_addr", "", "0.0.0.0", "dasboard address")
|
||||
rootCmd.PersistentFlags().IntVarP(&dashboardPort, "dashboard_port", "", 0, "dashboard port")
|
||||
rootCmd.PersistentFlags().StringVarP(&dashboardUser, "dashboard_user", "", "admin", "dashboard user")
|
||||
rootCmd.PersistentFlags().StringVarP(&dashboardPwd, "dashboard_pwd", "", "admin", "dashboard password")
|
||||
rootCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "log file")
|
||||
rootCmd.PersistentFlags().StringVarP(&logWay, "log_way", "", "console", "log way")
|
||||
rootCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||
rootCmd.PersistentFlags().Int64VarP(&logMaxDays, "log_max_days", "", 3, "log_max_days")
|
||||
rootCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
|
||||
rootCmd.PersistentFlags().Int64VarP(&authTimeout, "auth_timeout", "", 900, "auth timeout")
|
||||
rootCmd.PersistentFlags().StringVarP(&subDomainHost, "subdomain_host", "", "", "subdomain host")
|
||||
rootCmd.PersistentFlags().Int64VarP(&maxPortsPerClient, "max_ports_per_client", "", 0, "max ports per client")
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "frps",
|
||||
Short: "frps is the server of frp (https://github.com/fatedier/frp)",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if showVersion {
|
||||
fmt.Println(version.Full())
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
if cfgFile != "" {
|
||||
err = parseServerCommonCfg(CfgFileTypeIni, cfgFile)
|
||||
} else {
|
||||
err = parseServerCommonCfg(CfgFileTypeCmd, "")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = runServer()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func parseServerCommonCfg(fileType int, filePath string) (err error) {
|
||||
if fileType == CfgFileTypeIni {
|
||||
err = parseServerCommonCfgFromIni(filePath)
|
||||
} else if fileType == CfgFileTypeCmd {
|
||||
err = parseServerCommonCfgFromCmd()
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
g.GlbServerCfg.CfgFile = filePath
|
||||
|
||||
err = g.GlbServerCfg.ServerCommonConf.Check()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
config.InitServerCfg(&g.GlbServerCfg.ServerCommonConf)
|
||||
return
|
||||
}
|
||||
|
||||
func parseServerCommonCfgFromIni(filePath string) (err error) {
|
||||
b, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
content := string(b)
|
||||
|
||||
cfg, err := config.UnmarshalServerConfFromIni(&g.GlbServerCfg.ServerCommonConf, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.GlbServerCfg.ServerCommonConf = *cfg
|
||||
return
|
||||
}
|
||||
|
||||
func parseServerCommonCfgFromCmd() (err error) {
|
||||
g.GlbServerCfg.BindAddr = bindAddr
|
||||
g.GlbServerCfg.BindPort = bindPort
|
||||
g.GlbServerCfg.BindUdpPort = bindUdpPort
|
||||
g.GlbServerCfg.KcpBindPort = kcpBindPort
|
||||
g.GlbServerCfg.ProxyBindAddr = proxyBindAddr
|
||||
g.GlbServerCfg.VhostHttpPort = vhostHttpPort
|
||||
g.GlbServerCfg.VhostHttpsPort = vhostHttpsPort
|
||||
g.GlbServerCfg.DashboardAddr = dashboardAddr
|
||||
g.GlbServerCfg.DashboardPort = dashboardPort
|
||||
g.GlbServerCfg.DashboardUser = dashboardUser
|
||||
g.GlbServerCfg.DashboardPwd = dashboardPwd
|
||||
g.GlbServerCfg.LogFile = logFile
|
||||
g.GlbServerCfg.LogWay = logWay
|
||||
g.GlbServerCfg.LogLevel = logLevel
|
||||
g.GlbServerCfg.LogMaxDays = logMaxDays
|
||||
g.GlbServerCfg.Token = token
|
||||
g.GlbServerCfg.AuthTimeout = authTimeout
|
||||
g.GlbServerCfg.SubDomainHost = subDomainHost
|
||||
g.GlbServerCfg.MaxPortsPerClient = maxPortsPerClient
|
||||
return
|
||||
}
|
||||
|
||||
func runServer() (err error) {
|
||||
log.InitLog(g.GlbServerCfg.LogWay, g.GlbServerCfg.LogFile, g.GlbServerCfg.LogLevel,
|
||||
g.GlbServerCfg.LogMaxDays)
|
||||
svr, err := server.NewService()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("Start frps success")
|
||||
server.ServerService = svr
|
||||
svr.Run()
|
||||
return
|
||||
}
|
@@ -5,9 +5,10 @@
|
||||
server_addr = 0.0.0.0
|
||||
server_port = 7000
|
||||
|
||||
# if you want to connect frps by http proxy, you can set http_proxy here or in global environment variables
|
||||
# if you want to connect frps by http proxy or socks5 proxy, you can set http_proxy here or in global environment variables
|
||||
# it only works when protocol is tcp
|
||||
# http_proxy = http://user:pwd@192.168.1.128:8080
|
||||
# http_proxy = http://user:passwd@192.168.1.128:8080
|
||||
# http_proxy = socks5://user:passwd@192.168.1.128:1080
|
||||
|
||||
# console or real logFile path like ./frpc.log
|
||||
log_file = ./frpc.log
|
||||
@@ -18,13 +19,13 @@ log_level = info
|
||||
log_max_days = 3
|
||||
|
||||
# for authentication
|
||||
privilege_token = 12345678
|
||||
token = 12345678
|
||||
|
||||
# set admin address for control frpc's action by http api such as reload
|
||||
admin_addr = 127.0.0.1
|
||||
admin_port = 7400
|
||||
admin_user = admin
|
||||
admin_pwd = admin
|
||||
admin_passwd = admin
|
||||
|
||||
# connections will be established in advance, default value is zero
|
||||
pool_count = 5
|
||||
@@ -43,6 +44,9 @@ login_fail_exit = true
|
||||
# now it supports tcp and kcp, default is tcp
|
||||
protocol = tcp
|
||||
|
||||
# specify a dns server, so frpc will use this instead of default one
|
||||
dns_server = 8.8.8.8
|
||||
|
||||
# proxy names you want to start divided by ','
|
||||
# default is empty, means all proxies
|
||||
# start = ssh,dns
|
||||
@@ -70,9 +74,19 @@ remote_port = 6001
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 22
|
||||
# if remote_port is 0, frps will assgin a random port for you
|
||||
# if remote_port is 0, frps will assign a random port for you
|
||||
remote_port = 0
|
||||
|
||||
# if you want to expose multiple ports, add 'range:' prefix to the section name
|
||||
# frpc will generate multiple proxies such as 'tcp_port_6010', 'tcp_port_6011' and so on.
|
||||
[range:tcp_port]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 6010-6020,6022,6024-6028
|
||||
remote_port = 6010-6020,6022,6024-6028
|
||||
use_encryption = false
|
||||
use_compression = false
|
||||
|
||||
[dns]
|
||||
type = udp
|
||||
local_ip = 114.114.114.114
|
||||
@@ -81,6 +95,14 @@ remote_port = 6002
|
||||
use_encryption = false
|
||||
use_compression = false
|
||||
|
||||
[range:udp_port]
|
||||
type = udp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 6010-6020
|
||||
remote_port = 6010-6020
|
||||
use_encryption = false
|
||||
use_compression = false
|
||||
|
||||
# Resolve your domain names to [server_addr] so you can use http://web01.yourdomain.com to browse web01 and http://web02.yourdomain.com to browse web02
|
||||
[web01]
|
||||
type = http
|
||||
@@ -124,6 +146,22 @@ plugin = http_proxy
|
||||
plugin_http_user = abc
|
||||
plugin_http_passwd = abc
|
||||
|
||||
[plugin_socks5]
|
||||
type = tcp
|
||||
remote_port = 6005
|
||||
plugin = socks5
|
||||
plugin_user = abc
|
||||
plugin_passwd = abc
|
||||
|
||||
[plugin_static_file]
|
||||
type = tcp
|
||||
remote_port = 6006
|
||||
plugin = static_file
|
||||
plugin_local_path = /var/www/blog
|
||||
plugin_strip_prefix = static
|
||||
plugin_http_user = abc
|
||||
plugin_http_passwd = abc
|
||||
|
||||
[secret_tcp]
|
||||
# If the type is secret tcp, remote_port is useless
|
||||
# Who want to connect local port should deploy another frpc with stcp proxy and role is visitor
|
||||
|
@@ -16,6 +16,7 @@ kcp_bind_port = 7000
|
||||
# proxy_bind_addr = 127.0.0.1
|
||||
|
||||
# if you want to support virtual host, you must set the http port for listening (optional)
|
||||
# Note: http port and https port can be same with bind_port
|
||||
vhost_http_port = 80
|
||||
vhost_https_port = 443
|
||||
|
||||
@@ -25,7 +26,7 @@ vhost_https_port = 443
|
||||
dashboard_addr = 0.0.0.0
|
||||
dashboard_port = 7500
|
||||
|
||||
# dashboard user and pwd for basic auth protect, if not set, both default value is admin
|
||||
# dashboard user and passwd for basic auth protect, if not set, both default value is admin
|
||||
dashboard_user = admin
|
||||
dashboard_pwd = admin
|
||||
|
||||
@@ -39,19 +40,22 @@ log_level = info
|
||||
|
||||
log_max_days = 3
|
||||
|
||||
# privilege mode is the only supported mode since v0.10.0
|
||||
privilege_token = 12345678
|
||||
# auth token
|
||||
token = 12345678
|
||||
|
||||
# heartbeat configure, it's not recommended to modify the default value
|
||||
# the default value of heartbeat_timeout is 90
|
||||
# heartbeat_timeout = 90
|
||||
|
||||
# only allow frpc to bind ports you list, if you set nothing, there won't be any limit
|
||||
privilege_allow_ports = 2000-3000,3001,3003,4000-50000
|
||||
allow_ports = 2000-3000,3001,3003,4000-50000
|
||||
|
||||
# pool_count in each proxy will change to max_pool_count if they exceed the maximum value
|
||||
max_pool_count = 5
|
||||
|
||||
# max ports can be used for each client, default value is 0 means no limit
|
||||
max_ports_per_client = 0
|
||||
|
||||
# authentication_timeout means the timeout interval (seconds) when the frpc connects frps
|
||||
# if authentication_timeout is zero, the time is not verified, default is 900s
|
||||
authentication_timeout = 900
|
||||
|
@@ -1,135 +0,0 @@
|
||||
# Quick Start
|
||||
|
||||
frp is easier to use compared with other similar projects.
|
||||
|
||||
We will use two simple demo to demonstrate how to use frp.
|
||||
|
||||
1. How to create a connection to **server A**'s **ssh port** by **server B** with **public IP address** x.x.x.x(replace to the real IP address of your server).
|
||||
2. How to visit web service in **server A**'s **8000 port** and **8001 port** by **web01.yourdomain.com** and **web02.yourdomain.com** through **server B** with public ID address.
|
||||
|
||||
### Download SourceCode
|
||||
|
||||
`go get github.com/fatedier/frp` is recommended, then the code will be copied to the directory `$GOPATH/src/github.com/fatedier/frp`.
|
||||
|
||||
Or you can use `git clone https://github.com/fatedier/frp.git $GOPATH/src/github.com/fatedier/frp`.
|
||||
|
||||
If you want to try it quickly, download the compiled program and configuration files from [https://github.com/fatedier/frp/releases](https://github.com/fatedier/frp/releases).
|
||||
|
||||
### Compile
|
||||
|
||||
Enter the root directory and execute `make`, then wait until finished.
|
||||
|
||||
**bin** include all executable programs when **conf** include corresponding configuration files.
|
||||
|
||||
### Pre-requirement
|
||||
|
||||
* Go environment. Version of go >= 1.4.
|
||||
* Godep (if not exist, `go get` will be executed to download godep when compiling)
|
||||
|
||||
### Deploy
|
||||
|
||||
1. Move `./bin/frps` and `./conf/frps.ini` to any directory of **server B**.
|
||||
2. Move `./bin/frpc` and `./conf/frpc.ini` to any directory of **server A**.
|
||||
3. Modify all configuration files, details in next paragraph.
|
||||
4. Execute `nohup ./frps &` or `nohup ./frps -c ./frps.ini &` in **server B**.
|
||||
5. Execute `nohup ./frpc &` or `nohup ./frpc -c ./frpc.ini &` in **server A**.
|
||||
6. Use `ssh -oPort=6000 {user}@x.x.x.x` to test if frp is work(replace {user} to real username in **server A**), or visit custom domains by browser.
|
||||
|
||||
## Tcp port forwarding
|
||||
|
||||
### Configuration files
|
||||
|
||||
#### frps.ini
|
||||
|
||||
```ini
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
# for accept connections from frpc
|
||||
bind_port = 7000
|
||||
log_file = ./frps.log
|
||||
log_level = info
|
||||
|
||||
# ssh is the custom name of proxy and there can be many proxies with unique name in one configure file
|
||||
[ssh]
|
||||
auth_token = 123
|
||||
bind_addr = 0.0.0.0
|
||||
# finally we connect to server A by this port
|
||||
listen_port = 6000
|
||||
```
|
||||
|
||||
#### frpc.ini
|
||||
|
||||
```ini
|
||||
[common]
|
||||
# server address of frps
|
||||
server_addr = x.x.x.x
|
||||
server_port = 7000
|
||||
log_file = ./frpc.log
|
||||
log_level = info
|
||||
# for authentication
|
||||
auth_token = 123
|
||||
|
||||
# ssh is proxy name same with configure in frps.ini
|
||||
[ssh]
|
||||
# local port which need to be transferred
|
||||
local_port = 22
|
||||
# if use_encryption equals true, messages between frpc and frps will be encrypted, default is false
|
||||
use_encryption = true
|
||||
```
|
||||
|
||||
## Http port forwarding and Custom domains binding
|
||||
|
||||
If you only want to forward port one by one, you just need refer to [Tcp port forwarding](/doc/quick_start_en.md#Tcp-port-forwarding).If you want to visit different web pages deployed in different web servers by **server B**'s **80 port**, you should specify the type as **http**.
|
||||
|
||||
You also need to resolve your **A record** of your custom domain to [server_addr], or resolve your **CNAME record** to [server_addr] if [server_addr] is a domain.
|
||||
|
||||
After that, you can visit your web pages in local server by custom domains.
|
||||
|
||||
### Configuration files
|
||||
|
||||
#### frps.ini
|
||||
|
||||
```ini
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
bind_port = 7000
|
||||
# if you want to support vhost, specify one port for http services
|
||||
vhost_http_port = 80
|
||||
log_file = ./frps.log
|
||||
log_level = info
|
||||
|
||||
[web01]
|
||||
type = http
|
||||
auth_token = 123
|
||||
# # if proxy type equals http, custom_domains must be set separated by commas
|
||||
custom_domains = web01.yourdomain.com
|
||||
|
||||
[web02]
|
||||
type = http
|
||||
auth_token = 123
|
||||
custom_domains = web02.yourdomain.com
|
||||
```
|
||||
|
||||
#### frpc.ini
|
||||
|
||||
```ini
|
||||
[common]
|
||||
server_addr = x.x.x.x
|
||||
server_port = 7000
|
||||
log_file = ./frpc.log
|
||||
log_level = info
|
||||
auth_token = 123
|
||||
|
||||
# custom domains are set in frps.ini
|
||||
[web01]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 8000
|
||||
# encryption is optional, default is false
|
||||
use_encryption = true
|
||||
|
||||
[web02]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 8001
|
||||
```
|
@@ -1,137 +0,0 @@
|
||||
# frp 使用文档
|
||||
|
||||
相比于其他项目而言 frp 更易于部署和使用,这里我们用两个简单的示例来演示 frp 的使用过程。
|
||||
|
||||
1. 如何通过一台拥有公网IP地址的**服务器B**,访问处于公司内部网络环境中的**服务器A**的**ssh**端口,**服务器B**的IP地址为 x.x.x.x(测试时替换为真实的IP地址)。
|
||||
2. 如何利用一台拥有公网IP地址的**服务器B**,使通过 **web01.yourdomain.com** 可以访问内网环境中**服务器A**上**8000端口**的web服务,**web02.yourdomain.com** 可以访问**服务器A**上**8001端口**的web服务。
|
||||
|
||||
### 下载源码
|
||||
|
||||
推荐直接使用 `go get github.com/fatedier/frp` 下载源代码安装,执行命令后代码将会拷贝到 `$GOPATH/src/github.com/fatedier/frp` 目录下。
|
||||
|
||||
或者可以使用 `git clone https://github.com/fatedier/frp.git $GOPATH/src/github.com/fatedier/frp` 拷贝到相应目录下。
|
||||
|
||||
如果您想快速进行测试,也可以根据您服务器的操作系统及架构直接下载编译好的程序及示例配置文件,[https://github.com/fatedier/frp/releases](https://github.com/fatedier/frp/releases)。
|
||||
|
||||
### 编译
|
||||
|
||||
进入下载后的源码根目录,执行 `make` 命令,等待编译完成。
|
||||
|
||||
编译完成后, **bin** 目录下是编译好的可执行文件,**conf** 目录下是示例配置文件。
|
||||
|
||||
### 依赖
|
||||
|
||||
* go 1.4 以上版本
|
||||
* godep (如果检查不存在,编译时会通过 `go get` 命令安装)
|
||||
|
||||
### 部署
|
||||
|
||||
1. 将 ./bin/frps 和 ./conf/frps.ini 拷贝至**服务器B**任意目录。
|
||||
2. 将 ./bin/frpc 和 ./conf/frpc.ini 拷贝至**服务器A**任意目录。
|
||||
3. 根据要实现的功能修改两边的配置文件,详细内容见后续章节说明。
|
||||
4. 在服务器B执行 `nohup ./frps &` 或者 `nohup ./frps -c ./frps.ini &`。
|
||||
5. 在服务器A执行 `nohup ./frpc &` 或者 `nohup ./frpc -c ./frpc.ini &`。
|
||||
6. 通过 `ssh -oPort=6000 {user}@x.x.x.x` 测试是否能够成功连接**服务器A**({user}替换为**服务器A**上存在的真实用户),或通过浏览器访问自定义域名验证 http 服务是否转发成功。
|
||||
|
||||
## tcp 端口转发
|
||||
|
||||
转发 tcp 端口需要按照需求修改 frps 和 frpc 的配置文件。
|
||||
|
||||
### 配置文件
|
||||
|
||||
#### frps.ini
|
||||
|
||||
```ini
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
# 用于接收 frpc 连接的端口
|
||||
bind_port = 7000
|
||||
log_file = ./frps.log
|
||||
log_level = info
|
||||
|
||||
# ssh 为代理的自定义名称,可以有多个,不能重复,和frpc中名称对应
|
||||
[ssh]
|
||||
auth_token = 123
|
||||
bind_addr = 0.0.0.0
|
||||
# 最后将通过此端口访问后端服务
|
||||
listen_port = 6000
|
||||
```
|
||||
|
||||
#### frpc.ini
|
||||
|
||||
```ini
|
||||
[common]
|
||||
# frps 所在服务器绑定的IP地址
|
||||
server_addr = x.x.x.x
|
||||
server_port = 7000
|
||||
log_file = ./frpc.log
|
||||
log_level = info
|
||||
# 用于身份验证
|
||||
auth_token = 123
|
||||
|
||||
# ssh 需要和 frps.ini 中配置一致
|
||||
[ssh]
|
||||
# 需要转发的本地端口
|
||||
local_port = 22
|
||||
# 启用加密,frpc与frps之间通信加密,默认为 false
|
||||
use_encryption = true
|
||||
```
|
||||
|
||||
## http 端口转发,自定义域名绑定
|
||||
|
||||
如果只需要一对一的转发,例如**服务器B**的**80端口**转发**服务器A**的**8000端口**,则只需要配置 [tcp 端口转发](/doc/quick_start_zh.md#tcp-端口转发) 即可,如果需要使**服务器B**的**80端口**可以转发至**多个**web服务端口,则需要指定代理的类型为 http,并且在 frps 的配置文件中配置用于提供 http 转发服务的端口。
|
||||
|
||||
按照如下的内容修改配置文件后,需要将自定义域名的 **A 记录**解析到 [server_addr],如果 [server_addr] 是域名也可以将自定义域名的 **CNAME 记录**解析到 [server_addr]。
|
||||
|
||||
之后就可以通过自定义域名访问到本地的多个 web 服务。
|
||||
|
||||
### 配置文件
|
||||
|
||||
#### frps.ini
|
||||
|
||||
```ini
|
||||
[common]
|
||||
bind_addr = 0.0.0.0
|
||||
bind_port = 7000
|
||||
# 如果需要支持http类型的代理则需要指定一个端口
|
||||
vhost_http_port = 80
|
||||
log_file = ./frps.log
|
||||
log_level = info
|
||||
|
||||
[web01]
|
||||
# type 默认为 tcp,这里需要特别指定为 http
|
||||
type = http
|
||||
auth_token = 123
|
||||
# 自定义域名绑定,如果需要同时绑定多个以英文逗号分隔
|
||||
custom_domains = web01.yourdomain.com
|
||||
|
||||
[web02]
|
||||
type = http
|
||||
auth_token = 123
|
||||
custom_domains = web02.yourdomain.com
|
||||
```
|
||||
|
||||
#### frpc.ini
|
||||
|
||||
```ini
|
||||
[common]
|
||||
server_addr = x.x.x.x
|
||||
server_port = 7000
|
||||
log_file = ./frpc.log
|
||||
log_level = info
|
||||
auth_token = 123
|
||||
|
||||
|
||||
# 自定义域名在 frps.ini 中配置,方便做统一管理
|
||||
[web01]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 8000
|
||||
# 可选是否加密
|
||||
use_encryption = true
|
||||
|
||||
[web02]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 8001
|
||||
```
|
32
g/g.go
Normal file
32
g/g.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package g
|
||||
|
||||
import (
|
||||
"github.com/fatedier/frp/models/config"
|
||||
)
|
||||
|
||||
var (
|
||||
GlbClientCfg *ClientCfg
|
||||
GlbServerCfg *ServerCfg
|
||||
)
|
||||
|
||||
func init() {
|
||||
GlbClientCfg = &ClientCfg{
|
||||
ClientCommonConf: *config.GetDefaultClientConf(),
|
||||
}
|
||||
GlbServerCfg = &ServerCfg{
|
||||
ServerCommonConf: *config.GetDefaultServerConf(),
|
||||
}
|
||||
}
|
||||
|
||||
type ClientCfg struct {
|
||||
config.ClientCommonConf
|
||||
|
||||
CfgFile string
|
||||
ServerUdpPort int // this is configured by login response from frps
|
||||
}
|
||||
|
||||
type ServerCfg struct {
|
||||
config.ServerCommonConf
|
||||
|
||||
CfgFile string
|
||||
}
|
79
glide.lock
generated
79
glide.lock
generated
@@ -1,79 +0,0 @@
|
||||
hash: 4095d78a15bf0e7ffdd63331ce75d7199d663cc8710dcd08b9dcd09ba3183eac
|
||||
updated: 2018-01-23T14:48:38.764359+08:00
|
||||
imports:
|
||||
- name: github.com/armon/go-socks5
|
||||
version: e75332964ef517daa070d7c38a9466a0d687e0a5
|
||||
- name: github.com/davecgh/go-spew
|
||||
version: 346938d642f2ec3594ed81d874461961cd0faa76
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/docopt/docopt-go
|
||||
version: 784ddc588536785e7299f7272f39101f7faccc3f
|
||||
- name: github.com/fatedier/beego
|
||||
version: 6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8
|
||||
subpackages:
|
||||
- logs
|
||||
- name: github.com/fatedier/kcp-go
|
||||
version: cd167d2f15f451b0f33780ce862fca97adc0331e
|
||||
- name: github.com/golang/snappy
|
||||
version: 5979233c5d6225d4a8e438cdd0b411888449ddab
|
||||
- name: github.com/gorilla/websocket
|
||||
version: 292fd08b2560ad524ee37396253d71570339a821
|
||||
- name: github.com/julienschmidt/httprouter
|
||||
version: 8a45e95fc75cb77048068a62daed98cc22fdac7c
|
||||
- name: github.com/klauspost/cpuid
|
||||
version: 09cded8978dc9e80714c4d85b0322337b0a1e5e0
|
||||
- name: github.com/klauspost/reedsolomon
|
||||
version: dde6ad55c5e5a6379a4e82dcca32ee407346eb6d
|
||||
- name: github.com/pkg/errors
|
||||
version: c605e284fe17294bda444b34710735b29d1a9d90
|
||||
- name: github.com/pmezard/go-difflib
|
||||
version: 792786c7400a136282c1664665ae0a8db921c6c2
|
||||
subpackages:
|
||||
- difflib
|
||||
- name: github.com/rakyll/statik
|
||||
version: 274df120e9065bdd08eb1120e0375e3dc1ae8465
|
||||
subpackages:
|
||||
- fs
|
||||
- name: github.com/rodaine/table
|
||||
version: 212a2ad1c462ed4d5b5511ea2b480a573281dbbd
|
||||
- name: github.com/stretchr/testify
|
||||
version: 2402e8e7a02fc811447d11f881aa9746cdc57983
|
||||
subpackages:
|
||||
- assert
|
||||
- name: github.com/templexxx/cpufeat
|
||||
version: 3794dfbfb04749f896b521032f69383f24c3687e
|
||||
- name: github.com/templexxx/reedsolomon
|
||||
version: 7092926d7d05c415fabb892b1464a03f8228ab80
|
||||
- name: github.com/templexxx/xor
|
||||
version: 0af8e873c554da75f37f2049cdffda804533d44c
|
||||
- name: github.com/tjfoc/gmsm
|
||||
version: 21d76dee237dbbc8dfe1510000b9bf2733635aa1
|
||||
subpackages:
|
||||
- sm4
|
||||
- name: github.com/vaughan0/go-ini
|
||||
version: a98ad7ee00ec53921f08832bc06ecf7fd600e6a1
|
||||
- name: github.com/xtaci/kcp-go
|
||||
version: df437e2b8ec365a336200f9d9da53441cf72ed47
|
||||
- name: github.com/xtaci/smux
|
||||
version: 2de5471dfcbc029f5fe1392b83fe784127c4943e
|
||||
- name: golang.org/x/crypto
|
||||
version: e1a4589e7d3ea14a3352255d04b6f1a418845e5e
|
||||
subpackages:
|
||||
- blowfish
|
||||
- cast5
|
||||
- pbkdf2
|
||||
- salsa20
|
||||
- salsa20/salsa
|
||||
- tea
|
||||
- twofish
|
||||
- xtea
|
||||
- name: golang.org/x/net
|
||||
version: e4fa1c5465ad6111f206fc92186b8c83d64adbe1
|
||||
subpackages:
|
||||
- bpf
|
||||
- context
|
||||
- internal/iana
|
||||
- internal/socket
|
||||
- ipv4
|
||||
testImports: []
|
76
glide.yaml
76
glide.yaml
@@ -1,76 +0,0 @@
|
||||
package: github.com/fatedier/frp
|
||||
import:
|
||||
- package: github.com/armon/go-socks5
|
||||
version: e75332964ef517daa070d7c38a9466a0d687e0a5
|
||||
- package: github.com/davecgh/go-spew
|
||||
version: v1.1.0
|
||||
subpackages:
|
||||
- spew
|
||||
- package: github.com/docopt/docopt-go
|
||||
version: 0.6.2
|
||||
- package: github.com/fatedier/beego
|
||||
version: 6c6a4f5bd5eb5a39f7e289b8f345b55f75e7e3e8
|
||||
subpackages:
|
||||
- logs
|
||||
- package: github.com/fatedier/kcp-go
|
||||
version: cd167d2f15f451b0f33780ce862fca97adc0331e
|
||||
- package: github.com/golang/snappy
|
||||
version: 5979233c5d6225d4a8e438cdd0b411888449ddab
|
||||
- package: github.com/julienschmidt/httprouter
|
||||
version: 8a45e95fc75cb77048068a62daed98cc22fdac7c
|
||||
- package: github.com/klauspost/cpuid
|
||||
version: v1.0
|
||||
- package: github.com/klauspost/reedsolomon
|
||||
version: dde6ad55c5e5a6379a4e82dcca32ee407346eb6d
|
||||
- package: github.com/pkg/errors
|
||||
version: c605e284fe17294bda444b34710735b29d1a9d90
|
||||
- package: github.com/pmezard/go-difflib
|
||||
version: v1.0.0
|
||||
subpackages:
|
||||
- difflib
|
||||
- package: github.com/rakyll/statik
|
||||
version: v0.1.0
|
||||
subpackages:
|
||||
- fs
|
||||
- package: github.com/stretchr/testify
|
||||
version: 2402e8e7a02fc811447d11f881aa9746cdc57983
|
||||
subpackages:
|
||||
- assert
|
||||
- package: github.com/templexxx/cpufeat
|
||||
version: 3794dfbfb04749f896b521032f69383f24c3687e
|
||||
- package: github.com/templexxx/reedsolomon
|
||||
version: 7092926d7d05c415fabb892b1464a03f8228ab80
|
||||
- package: github.com/templexxx/xor
|
||||
version: 0.1.2
|
||||
- package: github.com/tjfoc/gmsm
|
||||
version: 21d76dee237dbbc8dfe1510000b9bf2733635aa1
|
||||
subpackages:
|
||||
- sm4
|
||||
- package: github.com/vaughan0/go-ini
|
||||
version: a98ad7ee00ec53921f08832bc06ecf7fd600e6a1
|
||||
- package: github.com/xtaci/kcp-go
|
||||
version: v3.17
|
||||
- package: github.com/xtaci/smux
|
||||
version: 2de5471dfcbc029f5fe1392b83fe784127c4943e
|
||||
- package: golang.org/x/crypto
|
||||
version: e1a4589e7d3ea14a3352255d04b6f1a418845e5e
|
||||
subpackages:
|
||||
- blowfish
|
||||
- cast5
|
||||
- pbkdf2
|
||||
- salsa20
|
||||
- salsa20/salsa
|
||||
- tea
|
||||
- twofish
|
||||
- xtea
|
||||
- package: golang.org/x/net
|
||||
version: e4fa1c5465ad6111f206fc92186b8c83d64adbe1
|
||||
subpackages:
|
||||
- bpf
|
||||
- context
|
||||
- internal/iana
|
||||
- internal/socket
|
||||
- ipv4
|
||||
- package: github.com/rodaine/table
|
||||
version: v1.0.0
|
||||
- package: github.com/gorilla/websocket
|
@@ -23,46 +23,41 @@ import (
|
||||
ini "github.com/vaughan0/go-ini"
|
||||
)
|
||||
|
||||
var ClientCommonCfg *ClientCommonConf
|
||||
|
||||
// client common config
|
||||
type ClientCommonConf struct {
|
||||
ConfigFile string
|
||||
ServerAddr string
|
||||
ServerPort int
|
||||
ServerUdpPort int // this is specified by login response message from frps
|
||||
HttpProxy string
|
||||
LogFile string
|
||||
LogWay string
|
||||
LogLevel string
|
||||
LogMaxDays int64
|
||||
PrivilegeToken string
|
||||
AdminAddr string
|
||||
AdminPort int
|
||||
AdminUser string
|
||||
AdminPwd string
|
||||
PoolCount int
|
||||
TcpMux bool
|
||||
User string
|
||||
LoginFailExit bool
|
||||
Start map[string]struct{}
|
||||
Protocol string
|
||||
HeartBeatInterval int64
|
||||
HeartBeatTimeout int64
|
||||
ServerAddr string `json:"server_addr"`
|
||||
ServerPort int `json:"server_port"`
|
||||
HttpProxy string `json:"http_proxy"`
|
||||
LogFile string `json:"log_file"`
|
||||
LogWay string `json:"log_way"`
|
||||
LogLevel string `json:"log_level"`
|
||||
LogMaxDays int64 `json:"log_max_days"`
|
||||
Token string `json:"token"`
|
||||
AdminAddr string `json:"admin_addr"`
|
||||
AdminPort int `json:"admin_port"`
|
||||
AdminUser string `json:"admin_user"`
|
||||
AdminPwd string `json:"admin_pwd"`
|
||||
PoolCount int `json:"pool_count"`
|
||||
TcpMux bool `json:"tcp_mux"`
|
||||
User string `json:"user"`
|
||||
DnsServer string `json:"dns_server"`
|
||||
LoginFailExit bool `json:"login_fail_exit"`
|
||||
Start map[string]struct{} `json:"start"`
|
||||
Protocol string `json:"protocol"`
|
||||
HeartBeatInterval int64 `json:"heartbeat_interval"`
|
||||
HeartBeatTimeout int64 `json:"heartbeat_timeout"`
|
||||
}
|
||||
|
||||
func GetDeaultClientCommonConf() *ClientCommonConf {
|
||||
func GetDefaultClientConf() *ClientCommonConf {
|
||||
return &ClientCommonConf{
|
||||
ConfigFile: "./frpc.ini",
|
||||
ServerAddr: "0.0.0.0",
|
||||
ServerPort: 7000,
|
||||
ServerUdpPort: 0,
|
||||
HttpProxy: "",
|
||||
HttpProxy: os.Getenv("http_proxy"),
|
||||
LogFile: "console",
|
||||
LogWay: "console",
|
||||
LogLevel: "info",
|
||||
LogMaxDays: 3,
|
||||
PrivilegeToken: "",
|
||||
Token: "",
|
||||
AdminAddr: "127.0.0.1",
|
||||
AdminPort: 0,
|
||||
AdminUser: "",
|
||||
@@ -70,6 +65,7 @@ func GetDeaultClientCommonConf() *ClientCommonConf {
|
||||
PoolCount: 1,
|
||||
TcpMux: true,
|
||||
User: "",
|
||||
DnsServer: "",
|
||||
LoginFailExit: true,
|
||||
Start: make(map[string]struct{}),
|
||||
Protocol: "tcp",
|
||||
@@ -78,21 +74,28 @@ func GetDeaultClientCommonConf() *ClientCommonConf {
|
||||
}
|
||||
}
|
||||
|
||||
func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
||||
func UnmarshalClientConfFromIni(defaultCfg *ClientCommonConf, content string) (cfg *ClientCommonConf, err error) {
|
||||
cfg = defaultCfg
|
||||
if cfg == nil {
|
||||
cfg = GetDefaultClientConf()
|
||||
}
|
||||
|
||||
conf, err := ini.Load(strings.NewReader(content))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("parse ini conf file error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
tmpStr string
|
||||
ok bool
|
||||
v int64
|
||||
)
|
||||
cfg = GetDeaultClientCommonConf()
|
||||
|
||||
tmpStr, ok = conf.Get("common", "server_addr")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "server_addr"); ok {
|
||||
cfg.ServerAddr = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "server_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "server_port"); ok {
|
||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid server_port")
|
||||
@@ -101,16 +104,11 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
||||
cfg.ServerPort = int(v)
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "http_proxy")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "http_proxy"); ok {
|
||||
cfg.HttpProxy = tmpStr
|
||||
} else {
|
||||
// get http_proxy from env
|
||||
cfg.HttpProxy = os.Getenv("http_proxy")
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "log_file")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "log_file"); ok {
|
||||
cfg.LogFile = tmpStr
|
||||
if cfg.LogFile == "console" {
|
||||
cfg.LogWay = "console"
|
||||
@@ -119,30 +117,25 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "log_level")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "log_level"); ok {
|
||||
cfg.LogLevel = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "log_max_days")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "log_max_days"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
||||
cfg.LogMaxDays = v
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "privilege_token")
|
||||
if ok {
|
||||
cfg.PrivilegeToken = tmpStr
|
||||
if tmpStr, ok = conf.Get("common", "token"); ok {
|
||||
cfg.Token = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "admin_addr")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "admin_addr"); ok {
|
||||
cfg.AdminAddr = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "admin_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "admin_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
||||
cfg.AdminPort = int(v)
|
||||
} else {
|
||||
@@ -151,55 +144,48 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "admin_user")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "admin_user"); ok {
|
||||
cfg.AdminUser = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "admin_pwd")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "admin_pwd"); ok {
|
||||
cfg.AdminPwd = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "pool_count")
|
||||
if ok {
|
||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||
if err != nil {
|
||||
cfg.PoolCount = 1
|
||||
} else {
|
||||
if tmpStr, ok = conf.Get("common", "pool_count"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
|
||||
cfg.PoolCount = int(v)
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "tcp_mux")
|
||||
if ok && tmpStr == "false" {
|
||||
if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" {
|
||||
cfg.TcpMux = false
|
||||
} else {
|
||||
cfg.TcpMux = true
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "user")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "user"); ok {
|
||||
cfg.User = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "start")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "dns_server"); ok {
|
||||
cfg.DnsServer = tmpStr
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "start"); ok {
|
||||
proxyNames := strings.Split(tmpStr, ",")
|
||||
for _, name := range proxyNames {
|
||||
cfg.Start[strings.TrimSpace(name)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "login_fail_exit")
|
||||
if ok && tmpStr == "false" {
|
||||
if tmpStr, ok = conf.Get("common", "login_fail_exit"); ok && tmpStr == "false" {
|
||||
cfg.LoginFailExit = false
|
||||
} else {
|
||||
cfg.LoginFailExit = true
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "protocol")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "protocol"); ok {
|
||||
// Now it only support tcp and kcp.
|
||||
if tmpStr != "kcp" {
|
||||
tmpStr = "tcp"
|
||||
@@ -207,10 +193,8 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
||||
cfg.Protocol = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "heartbeat_timeout")
|
||||
if ok {
|
||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||
if err != nil {
|
||||
if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout")
|
||||
return
|
||||
} else {
|
||||
@@ -218,17 +202,18 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "heartbeat_interval")
|
||||
if ok {
|
||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||
if err != nil {
|
||||
if tmpStr, ok = conf.Get("common", "heartbeat_interval"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
||||
return
|
||||
} else {
|
||||
cfg.HeartBeatInterval = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *ClientCommonConf) Check() (err error) {
|
||||
if cfg.HeartBeatInterval <= 0 {
|
||||
err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
|
||||
return
|
||||
|
@@ -22,11 +22,14 @@ import (
|
||||
|
||||
"github.com/fatedier/frp/models/consts"
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
"github.com/fatedier/frp/utils/util"
|
||||
|
||||
ini "github.com/vaughan0/go-ini"
|
||||
)
|
||||
|
||||
var proxyConfTypeMap map[string]reflect.Type
|
||||
var (
|
||||
proxyConfTypeMap map[string]reflect.Type
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxyConfTypeMap = make(map[string]reflect.Type)
|
||||
@@ -50,17 +53,16 @@ func NewConfByType(proxyType string) ProxyConf {
|
||||
}
|
||||
|
||||
type ProxyConf interface {
|
||||
GetName() string
|
||||
GetType() string
|
||||
GetBaseInfo() *BaseProxyConf
|
||||
LoadFromMsg(pMsg *msg.NewProxy)
|
||||
LoadFromFile(name string, conf ini.Section) error
|
||||
UnMarshalToMsg(pMsg *msg.NewProxy)
|
||||
Check() error
|
||||
UnmarshalFromMsg(pMsg *msg.NewProxy)
|
||||
UnmarshalFromIni(prefix string, name string, conf ini.Section) error
|
||||
MarshalToMsg(pMsg *msg.NewProxy)
|
||||
CheckForCli() error
|
||||
CheckForSvr() error
|
||||
Compare(conf ProxyConf) bool
|
||||
}
|
||||
|
||||
func NewProxyConf(pMsg *msg.NewProxy) (cfg ProxyConf, err error) {
|
||||
func NewProxyConfFromMsg(pMsg *msg.NewProxy) (cfg ProxyConf, err error) {
|
||||
if pMsg.ProxyType == "" {
|
||||
pMsg.ProxyType = consts.TcpProxy
|
||||
}
|
||||
@@ -70,12 +72,12 @@ func NewProxyConf(pMsg *msg.NewProxy) (cfg ProxyConf, err error) {
|
||||
err = fmt.Errorf("proxy [%s] type [%s] error", pMsg.ProxyName, pMsg.ProxyType)
|
||||
return
|
||||
}
|
||||
cfg.LoadFromMsg(pMsg)
|
||||
err = cfg.Check()
|
||||
cfg.UnmarshalFromMsg(pMsg)
|
||||
err = cfg.CheckForSvr()
|
||||
return
|
||||
}
|
||||
|
||||
func NewProxyConfFromFile(name string, section ini.Section) (cfg ProxyConf, err error) {
|
||||
func NewProxyConfFromIni(prefix string, name string, section ini.Section) (cfg ProxyConf, err error) {
|
||||
proxyType := section["type"]
|
||||
if proxyType == "" {
|
||||
proxyType = consts.TcpProxy
|
||||
@@ -86,7 +88,10 @@ func NewProxyConfFromFile(name string, section ini.Section) (cfg ProxyConf, err
|
||||
err = fmt.Errorf("proxy [%s] type [%s] error", name, proxyType)
|
||||
return
|
||||
}
|
||||
err = cfg.LoadFromFile(name, section)
|
||||
if err = cfg.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
err = cfg.CheckForCli()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -99,14 +104,6 @@ type BaseProxyConf struct {
|
||||
UseCompression bool `json:"use_compression"`
|
||||
}
|
||||
|
||||
func (cfg *BaseProxyConf) GetName() string {
|
||||
return cfg.ProxyName
|
||||
}
|
||||
|
||||
func (cfg *BaseProxyConf) GetType() string {
|
||||
return cfg.ProxyType
|
||||
}
|
||||
|
||||
func (cfg *BaseProxyConf) GetBaseInfo() *BaseProxyConf {
|
||||
return cfg
|
||||
}
|
||||
@@ -121,23 +118,19 @@ func (cfg *BaseProxyConf) compare(cmp *BaseProxyConf) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *BaseProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
func (cfg *BaseProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.ProxyName = pMsg.ProxyName
|
||||
cfg.ProxyType = pMsg.ProxyType
|
||||
cfg.UseEncryption = pMsg.UseEncryption
|
||||
cfg.UseCompression = pMsg.UseCompression
|
||||
}
|
||||
|
||||
func (cfg *BaseProxyConf) LoadFromFile(name string, section ini.Section) error {
|
||||
func (cfg *BaseProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) error {
|
||||
var (
|
||||
tmpStr string
|
||||
ok bool
|
||||
)
|
||||
if ClientCommonCfg.User != "" {
|
||||
cfg.ProxyName = ClientCommonCfg.User + "." + name
|
||||
} else {
|
||||
cfg.ProxyName = name
|
||||
}
|
||||
cfg.ProxyName = prefix + name
|
||||
cfg.ProxyType = section["type"]
|
||||
|
||||
tmpStr, ok = section["use_encryption"]
|
||||
@@ -152,7 +145,7 @@ func (cfg *BaseProxyConf) LoadFromFile(name string, section ini.Section) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *BaseProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
func (cfg *BaseProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
pMsg.ProxyName = cfg.ProxyName
|
||||
pMsg.ProxyType = cfg.ProxyType
|
||||
pMsg.UseEncryption = cfg.UseEncryption
|
||||
@@ -161,24 +154,21 @@ func (cfg *BaseProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
|
||||
// Bind info
|
||||
type BindInfoConf struct {
|
||||
BindAddr string `json:"bind_addr"`
|
||||
RemotePort int `json:"remote_port"`
|
||||
}
|
||||
|
||||
func (cfg *BindInfoConf) compare(cmp *BindInfoConf) bool {
|
||||
if cfg.BindAddr != cmp.BindAddr ||
|
||||
cfg.RemotePort != cmp.RemotePort {
|
||||
if cfg.RemotePort != cmp.RemotePort {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *BindInfoConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BindAddr = ServerCommonCfg.ProxyBindAddr
|
||||
func (cfg *BindInfoConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.RemotePort = pMsg.RemotePort
|
||||
}
|
||||
|
||||
func (cfg *BindInfoConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
func (cfg *BindInfoConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
var (
|
||||
tmpStr string
|
||||
ok bool
|
||||
@@ -196,14 +186,10 @@ func (cfg *BindInfoConf) LoadFromFile(name string, section ini.Section) (err err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *BindInfoConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
func (cfg *BindInfoConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
pMsg.RemotePort = cfg.RemotePort
|
||||
}
|
||||
|
||||
func (cfg *BindInfoConf) check() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Domain info
|
||||
type DomainConf struct {
|
||||
CustomDomains []string `json:"custom_domains"`
|
||||
@@ -218,12 +204,12 @@ func (cfg *DomainConf) compare(cmp *DomainConf) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *DomainConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
func (cfg *DomainConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.CustomDomains = pMsg.CustomDomains
|
||||
cfg.SubDomain = pMsg.SubDomain
|
||||
}
|
||||
|
||||
func (cfg *DomainConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
func (cfg *DomainConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
var (
|
||||
tmpStr string
|
||||
ok bool
|
||||
@@ -238,42 +224,60 @@ func (cfg *DomainConf) LoadFromFile(name string, section ini.Section) (err error
|
||||
if tmpStr, ok = section["subdomain"]; ok {
|
||||
cfg.SubDomain = tmpStr
|
||||
}
|
||||
|
||||
if len(cfg.CustomDomains) == 0 && cfg.SubDomain == "" {
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] custom_domains and subdomain should set at least one of them", name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *DomainConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
func (cfg *DomainConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
pMsg.CustomDomains = cfg.CustomDomains
|
||||
pMsg.SubDomain = cfg.SubDomain
|
||||
}
|
||||
|
||||
func (cfg *DomainConf) check() (err error) {
|
||||
if len(cfg.CustomDomains) == 0 && cfg.SubDomain == "" {
|
||||
err = fmt.Errorf("custom_domains and subdomain should set at least one of them")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *DomainConf) checkForCli() (err error) {
|
||||
if err = cfg.check(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *DomainConf) checkForSvr() (err error) {
|
||||
if err = cfg.check(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, domain := range cfg.CustomDomains {
|
||||
if ServerCommonCfg.SubDomainHost != "" && len(strings.Split(ServerCommonCfg.SubDomainHost, ".")) < len(strings.Split(domain, ".")) {
|
||||
if strings.Contains(domain, ServerCommonCfg.SubDomainHost) {
|
||||
return fmt.Errorf("custom domain [%s] should not belong to subdomain_host [%s]", domain, ServerCommonCfg.SubDomainHost)
|
||||
if subDomainHost != "" && len(strings.Split(subDomainHost, ".")) < len(strings.Split(domain, ".")) {
|
||||
if strings.Contains(domain, subDomainHost) {
|
||||
return fmt.Errorf("custom domain [%s] should not belong to subdomain_host [%s]", domain, subDomainHost)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.SubDomain != "" {
|
||||
if ServerCommonCfg.SubDomainHost == "" {
|
||||
return fmt.Errorf("subdomain is not supported because this feature is not enabled by frps")
|
||||
if subDomainHost == "" {
|
||||
return fmt.Errorf("subdomain is not supported because this feature is not enabled in remote frps")
|
||||
}
|
||||
if strings.Contains(cfg.SubDomain, ".") || strings.Contains(cfg.SubDomain, "*") {
|
||||
return fmt.Errorf("'.' and '*' is not supported in subdomain")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
// Local service info
|
||||
type LocalSvrConf struct {
|
||||
LocalIp string `json:"-"`
|
||||
LocalPort int `json:"-"`
|
||||
LocalIp string `json:"local_ip"`
|
||||
LocalPort int `json:"local_port"`
|
||||
|
||||
Plugin string `json:"plugin"`
|
||||
PluginParams map[string]string `json:"plugin_params"`
|
||||
}
|
||||
|
||||
func (cfg *LocalSvrConf) compare(cmp *LocalSvrConf) bool {
|
||||
@@ -281,30 +285,6 @@ func (cfg *LocalSvrConf) compare(cmp *LocalSvrConf) bool {
|
||||
cfg.LocalPort != cmp.LocalPort {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *LocalSvrConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
if cfg.LocalIp = section["local_ip"]; cfg.LocalIp == "" {
|
||||
cfg.LocalIp = "127.0.0.1"
|
||||
}
|
||||
|
||||
if tmpStr, ok := section["local_port"]; ok {
|
||||
if cfg.LocalPort, err = strconv.Atoi(tmpStr); err != nil {
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] local_port error", name)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] local_port not found", name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PluginConf struct {
|
||||
Plugin string `json:"-"`
|
||||
PluginParams map[string]string `json:"-"`
|
||||
}
|
||||
|
||||
func (cfg *PluginConf) compare(cmp *PluginConf) bool {
|
||||
if cfg.Plugin != cmp.Plugin ||
|
||||
len(cfg.PluginParams) != len(cmp.PluginParams) {
|
||||
return false
|
||||
@@ -318,7 +298,7 @@ func (cfg *PluginConf) compare(cmp *PluginConf) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *PluginConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
func (cfg *LocalSvrConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
cfg.Plugin = section["plugin"]
|
||||
cfg.PluginParams = make(map[string]string)
|
||||
if cfg.Plugin != "" {
|
||||
@@ -329,7 +309,17 @@ func (cfg *PluginConf) LoadFromFile(name string, section ini.Section) (err error
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] no plugin info found", name)
|
||||
if cfg.LocalIp = section["local_ip"]; cfg.LocalIp == "" {
|
||||
cfg.LocalIp = "127.0.0.1"
|
||||
}
|
||||
|
||||
if tmpStr, ok := section["local_port"]; ok {
|
||||
if cfg.LocalPort, err = strconv.Atoi(tmpStr); err != nil {
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] local_port error", name)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] local_port not found", name)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -340,7 +330,6 @@ type TcpProxyConf struct {
|
||||
BindInfoConf
|
||||
|
||||
LocalSvrConf
|
||||
PluginConf
|
||||
}
|
||||
|
||||
func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
@@ -351,43 +340,38 @@ func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
|
||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||
!cfg.BindInfoConf.compare(&cmpConf.BindInfoConf) ||
|
||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) {
|
||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *TcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
||||
cfg.BindInfoConf.LoadFromMsg(pMsg)
|
||||
func (cfg *TcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||
cfg.BindInfoConf.UnmarshalFromMsg(pMsg)
|
||||
}
|
||||
|
||||
func (cfg *TcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
||||
func (cfg *TcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.BindInfoConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.BindInfoConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *TcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
||||
cfg.BindInfoConf.UnMarshalToMsg(pMsg)
|
||||
func (cfg *TcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||
cfg.BindInfoConf.MarshalToMsg(pMsg)
|
||||
}
|
||||
|
||||
func (cfg *TcpProxyConf) Check() (err error) {
|
||||
err = cfg.BindInfoConf.check()
|
||||
return
|
||||
}
|
||||
func (cfg *TcpProxyConf) CheckForCli() error { return nil }
|
||||
|
||||
func (cfg *TcpProxyConf) CheckForSvr() error { return nil }
|
||||
|
||||
// UDP
|
||||
type UdpProxyConf struct {
|
||||
@@ -411,33 +395,32 @@ func (cfg *UdpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *UdpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
||||
cfg.BindInfoConf.LoadFromMsg(pMsg)
|
||||
func (cfg *UdpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||
cfg.BindInfoConf.UnmarshalFromMsg(pMsg)
|
||||
}
|
||||
|
||||
func (cfg *UdpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
||||
func (cfg *UdpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.BindInfoConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.BindInfoConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *UdpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
||||
cfg.BindInfoConf.UnMarshalToMsg(pMsg)
|
||||
func (cfg *UdpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||
cfg.BindInfoConf.MarshalToMsg(pMsg)
|
||||
}
|
||||
|
||||
func (cfg *UdpProxyConf) Check() (err error) {
|
||||
err = cfg.BindInfoConf.check()
|
||||
return
|
||||
}
|
||||
func (cfg *UdpProxyConf) CheckForCli() error { return nil }
|
||||
|
||||
func (cfg *UdpProxyConf) CheckForSvr() error { return nil }
|
||||
|
||||
// HTTP
|
||||
type HttpProxyConf struct {
|
||||
@@ -445,12 +428,11 @@ type HttpProxyConf struct {
|
||||
DomainConf
|
||||
|
||||
LocalSvrConf
|
||||
PluginConf
|
||||
|
||||
Locations []string `json:"locations"`
|
||||
HostHeaderRewrite string `json:"host_header_rewrite"`
|
||||
HttpUser string `json:"-"`
|
||||
HttpPwd string `json:"-"`
|
||||
HttpUser string `json:"http_user"`
|
||||
HttpPwd string `json:"http_pwd"`
|
||||
}
|
||||
|
||||
func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
@@ -462,7 +444,6 @@ func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||
!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
|
||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) ||
|
||||
strings.Join(cfg.Locations, " ") != strings.Join(cmpConf.Locations, " ") ||
|
||||
cfg.HostHeaderRewrite != cmpConf.HostHeaderRewrite ||
|
||||
cfg.HttpUser != cmpConf.HttpUser ||
|
||||
@@ -472,9 +453,9 @@ func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *HttpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
||||
cfg.DomainConf.LoadFromMsg(pMsg)
|
||||
func (cfg *HttpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||
cfg.DomainConf.UnmarshalFromMsg(pMsg)
|
||||
|
||||
cfg.Locations = pMsg.Locations
|
||||
cfg.HostHeaderRewrite = pMsg.HostHeaderRewrite
|
||||
@@ -482,18 +463,16 @@ func (cfg *HttpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.HttpPwd = pMsg.HttpPwd
|
||||
}
|
||||
|
||||
func (cfg *HttpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
||||
func (cfg *HttpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.DomainConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
tmpStr string
|
||||
@@ -511,9 +490,9 @@ func (cfg *HttpProxyConf) LoadFromFile(name string, section ini.Section) (err er
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *HttpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
||||
cfg.DomainConf.UnMarshalToMsg(pMsg)
|
||||
func (cfg *HttpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||
cfg.DomainConf.MarshalToMsg(pMsg)
|
||||
|
||||
pMsg.Locations = cfg.Locations
|
||||
pMsg.HostHeaderRewrite = cfg.HostHeaderRewrite
|
||||
@@ -521,11 +500,21 @@ func (cfg *HttpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
pMsg.HttpPwd = cfg.HttpPwd
|
||||
}
|
||||
|
||||
func (cfg *HttpProxyConf) Check() (err error) {
|
||||
if ServerCommonCfg.VhostHttpPort == 0 {
|
||||
func (cfg *HttpProxyConf) CheckForCli() (err error) {
|
||||
if err = cfg.DomainConf.checkForCli(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *HttpProxyConf) CheckForSvr() (err error) {
|
||||
if vhostHttpPort == 0 {
|
||||
return fmt.Errorf("type [http] not support when vhost_http_port is not set")
|
||||
}
|
||||
err = cfg.DomainConf.check()
|
||||
if err = cfg.DomainConf.checkForSvr(); err != nil {
|
||||
err = fmt.Errorf("proxy [%s] domain conf check error: %v", cfg.ProxyName, err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -535,7 +524,6 @@ type HttpsProxyConf struct {
|
||||
DomainConf
|
||||
|
||||
LocalSvrConf
|
||||
PluginConf
|
||||
}
|
||||
|
||||
func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool {
|
||||
@@ -546,43 +534,50 @@ func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool {
|
||||
|
||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||
!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
|
||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) {
|
||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (cfg *HttpsProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
||||
cfg.DomainConf.LoadFromMsg(pMsg)
|
||||
func (cfg *HttpsProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||
cfg.DomainConf.UnmarshalFromMsg(pMsg)
|
||||
}
|
||||
|
||||
func (cfg *HttpsProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
||||
func (cfg *HttpsProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.DomainConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *HttpsProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
||||
cfg.DomainConf.UnMarshalToMsg(pMsg)
|
||||
func (cfg *HttpsProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||
cfg.DomainConf.MarshalToMsg(pMsg)
|
||||
}
|
||||
|
||||
func (cfg *HttpsProxyConf) Check() (err error) {
|
||||
if ServerCommonCfg.VhostHttpsPort == 0 {
|
||||
func (cfg *HttpsProxyConf) CheckForCli() (err error) {
|
||||
if err = cfg.DomainConf.checkForCli(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *HttpsProxyConf) CheckForSvr() (err error) {
|
||||
if vhostHttpsPort == 0 {
|
||||
return fmt.Errorf("type [https] not support when vhost_https_port is not set")
|
||||
}
|
||||
err = cfg.DomainConf.check()
|
||||
if err = cfg.DomainConf.checkForSvr(); err != nil {
|
||||
err = fmt.Errorf("proxy [%s] domain conf check error: %v", cfg.ProxyName, err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -595,7 +590,6 @@ type StcpProxyConf struct {
|
||||
|
||||
// used in role server
|
||||
LocalSvrConf
|
||||
PluginConf
|
||||
|
||||
// used in role visitor
|
||||
ServerName string `json:"server_name"`
|
||||
@@ -611,7 +605,6 @@ func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
|
||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) ||
|
||||
cfg.Role != cmpConf.Role ||
|
||||
cfg.Sk != cmpConf.Sk ||
|
||||
cfg.ServerName != cmpConf.ServerName ||
|
||||
@@ -623,13 +616,13 @@ func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
}
|
||||
|
||||
// Only for role server.
|
||||
func (cfg *StcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
||||
func (cfg *StcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||
cfg.Sk = pMsg.Sk
|
||||
}
|
||||
|
||||
func (cfg *StcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
||||
func (cfg *StcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -660,21 +653,37 @@ func (cfg *StcpProxyConf) LoadFromFile(name string, section ini.Section) (err er
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name)
|
||||
}
|
||||
} else {
|
||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *StcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
||||
func (cfg *StcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||
pMsg.Sk = cfg.Sk
|
||||
}
|
||||
|
||||
func (cfg *StcpProxyConf) Check() (err error) {
|
||||
func (cfg *StcpProxyConf) CheckForCli() (err error) {
|
||||
if cfg.Role != "server" && cfg.Role != "visitor" {
|
||||
err = fmt.Errorf("role should be 'server' or 'visitor'")
|
||||
return
|
||||
}
|
||||
if cfg.Role == "visitor" {
|
||||
if cfg.BindAddr == "" {
|
||||
err = fmt.Errorf("bind_addr shouldn't be empty")
|
||||
return
|
||||
}
|
||||
if cfg.BindPort == 0 {
|
||||
err = fmt.Errorf("bind_port should be set")
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *StcpProxyConf) CheckForSvr() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -687,7 +696,6 @@ type XtcpProxyConf struct {
|
||||
|
||||
// used in role server
|
||||
LocalSvrConf
|
||||
PluginConf
|
||||
|
||||
// used in role visitor
|
||||
ServerName string `json:"server_name"`
|
||||
@@ -703,7 +711,6 @@ func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
|
||||
if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
|
||||
!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
|
||||
!cfg.PluginConf.compare(&cmpConf.PluginConf) ||
|
||||
cfg.Role != cmpConf.Role ||
|
||||
cfg.Sk != cmpConf.Sk ||
|
||||
cfg.ServerName != cmpConf.ServerName ||
|
||||
@@ -715,13 +722,13 @@ func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool {
|
||||
}
|
||||
|
||||
// Only for role server.
|
||||
func (cfg *XtcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.LoadFromMsg(pMsg)
|
||||
func (cfg *XtcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
|
||||
cfg.Sk = pMsg.Sk
|
||||
}
|
||||
|
||||
func (cfg *XtcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
|
||||
func (cfg *XtcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
|
||||
if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -752,27 +759,75 @@ func (cfg *XtcpProxyConf) LoadFromFile(name string, section ini.Section) (err er
|
||||
return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name)
|
||||
}
|
||||
} else {
|
||||
if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
|
||||
if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *XtcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
|
||||
func (cfg *XtcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
|
||||
cfg.BaseProxyConf.MarshalToMsg(pMsg)
|
||||
pMsg.Sk = cfg.Sk
|
||||
}
|
||||
|
||||
func (cfg *XtcpProxyConf) Check() (err error) {
|
||||
func (cfg *XtcpProxyConf) CheckForCli() (err error) {
|
||||
if cfg.Role != "server" && cfg.Role != "visitor" {
|
||||
err = fmt.Errorf("role should be 'server' or 'visitor'")
|
||||
return
|
||||
}
|
||||
if cfg.Role == "visitor" {
|
||||
if cfg.BindAddr == "" {
|
||||
err = fmt.Errorf("bind_addr shouldn't be empty")
|
||||
return
|
||||
}
|
||||
if cfg.BindPort == 0 {
|
||||
err = fmt.Errorf("bind_port should be set")
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *XtcpProxyConf) CheckForSvr() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func ParseRangeSection(name string, section ini.Section) (sections map[string]ini.Section, err error) {
|
||||
localPorts, errRet := util.ParseRangeNumbers(section["local_port"])
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("Parse conf error: range section [%s] local_port invalid, %v", name, errRet)
|
||||
return
|
||||
}
|
||||
|
||||
remotePorts, errRet := util.ParseRangeNumbers(section["remote_port"])
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("Parse conf error: range section [%s] remote_port invalid, %v", name, errRet)
|
||||
return
|
||||
}
|
||||
if len(localPorts) != len(remotePorts) {
|
||||
err = fmt.Errorf("Parse conf error: range section [%s] local ports number should be same with remote ports number", name)
|
||||
return
|
||||
}
|
||||
if len(localPorts) == 0 {
|
||||
err = fmt.Errorf("Parse conf error: range section [%s] local_port and remote_port is necessary", name)
|
||||
return
|
||||
}
|
||||
|
||||
sections = make(map[string]ini.Section)
|
||||
for i, port := range localPorts {
|
||||
subName := fmt.Sprintf("%s_%d", name, i)
|
||||
subSection := copySection(section)
|
||||
subSection["local_port"] = fmt.Sprintf("%d", port)
|
||||
subSection["remote_port"] = fmt.Sprintf("%d", remotePorts[i])
|
||||
sections[subName] = subSection
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// if len(startProxy) is 0, start all
|
||||
// otherwise just start proxies in startProxy map
|
||||
func LoadProxyConfFromFile(prefix string, conf ini.File, startProxy map[string]struct{}) (
|
||||
func LoadProxyConfFromIni(prefix string, conf ini.File, startProxy map[string]struct{}) (
|
||||
proxyConfs map[string]ProxyConf, visitorConfs map[string]ProxyConf, err error) {
|
||||
|
||||
if prefix != "" {
|
||||
@@ -786,22 +841,49 @@ func LoadProxyConfFromFile(prefix string, conf ini.File, startProxy map[string]s
|
||||
proxyConfs = make(map[string]ProxyConf)
|
||||
visitorConfs = make(map[string]ProxyConf)
|
||||
for name, section := range conf {
|
||||
if name == "common" {
|
||||
continue
|
||||
}
|
||||
|
||||
_, shouldStart := startProxy[name]
|
||||
if name != "common" && (startAll || shouldStart) {
|
||||
// some proxy or visotr configure may be used this prefix
|
||||
section["prefix"] = prefix
|
||||
cfg, err := NewProxyConfFromFile(name, section)
|
||||
if !startAll && !shouldStart {
|
||||
continue
|
||||
}
|
||||
|
||||
subSections := make(map[string]ini.Section)
|
||||
|
||||
if strings.HasPrefix(name, "range:") {
|
||||
// range section
|
||||
rangePrefix := strings.TrimSpace(strings.TrimPrefix(name, "range:"))
|
||||
subSections, err = ParseRangeSection(rangePrefix, section)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
subSections[name] = section
|
||||
}
|
||||
|
||||
for subName, subSection := range subSections {
|
||||
cfg, err := NewProxyConfFromIni(prefix, subName, subSection)
|
||||
if err != nil {
|
||||
return proxyConfs, visitorConfs, err
|
||||
}
|
||||
|
||||
role := section["role"]
|
||||
role := subSection["role"]
|
||||
if role == "visitor" {
|
||||
visitorConfs[prefix+name] = cfg
|
||||
visitorConfs[prefix+subName] = cfg
|
||||
} else {
|
||||
proxyConfs[prefix+name] = cfg
|
||||
proxyConfs[prefix+subName] = cfg
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func copySection(section ini.Section) (out ini.Section) {
|
||||
out = make(ini.Section)
|
||||
for k, v := range section {
|
||||
out[k] = v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@@ -20,50 +20,63 @@ import (
|
||||
"strings"
|
||||
|
||||
ini "github.com/vaughan0/go-ini"
|
||||
|
||||
"github.com/fatedier/frp/utils/util"
|
||||
)
|
||||
|
||||
var ServerCommonCfg *ServerCommonConf
|
||||
var (
|
||||
// server global configure used for generate proxy conf used in frps
|
||||
proxyBindAddr string
|
||||
subDomainHost string
|
||||
vhostHttpPort int
|
||||
vhostHttpsPort int
|
||||
)
|
||||
|
||||
func InitServerCfg(cfg *ServerCommonConf) {
|
||||
proxyBindAddr = cfg.ProxyBindAddr
|
||||
subDomainHost = cfg.SubDomainHost
|
||||
vhostHttpPort = cfg.VhostHttpPort
|
||||
vhostHttpsPort = cfg.VhostHttpsPort
|
||||
}
|
||||
|
||||
// common config
|
||||
type ServerCommonConf struct {
|
||||
ConfigFile string
|
||||
BindAddr string
|
||||
BindPort int
|
||||
BindUdpPort int
|
||||
KcpBindPort int
|
||||
ProxyBindAddr string
|
||||
BindAddr string `json:"bind_addr"`
|
||||
BindPort int `json:"bind_port"`
|
||||
BindUdpPort int `json:"bind_udp_port"`
|
||||
KcpBindPort int `json:"kcp_bind_port"`
|
||||
ProxyBindAddr string `json:"proxy_bind_addr"`
|
||||
|
||||
// If VhostHttpPort equals 0, don't listen a public port for http protocol.
|
||||
VhostHttpPort int
|
||||
VhostHttpPort int `json:"vhost_http_port"`
|
||||
|
||||
// if VhostHttpsPort equals 0, don't listen a public port for https protocol
|
||||
VhostHttpsPort int
|
||||
DashboardAddr string
|
||||
VhostHttpsPort int `json:"vhost_http_port"`
|
||||
DashboardAddr string `json:"dashboard_addr"`
|
||||
|
||||
// if DashboardPort equals 0, dashboard is not available
|
||||
DashboardPort int
|
||||
DashboardUser string
|
||||
DashboardPwd string
|
||||
AssetsDir string
|
||||
LogFile string
|
||||
LogWay string // console or file
|
||||
LogLevel string
|
||||
LogMaxDays int64
|
||||
PrivilegeMode bool
|
||||
PrivilegeToken string
|
||||
AuthTimeout int64
|
||||
SubDomainHost string
|
||||
TcpMux bool
|
||||
DashboardPort int `json:"dashboard_port"`
|
||||
DashboardUser string `json:"dashboard_user"`
|
||||
DashboardPwd string `json:"dashboard_pwd"`
|
||||
AssetsDir string `json:"asserts_dir"`
|
||||
LogFile string `json:"log_file"`
|
||||
LogWay string `json:"log_way"` // console or file
|
||||
LogLevel string `json:"log_level"`
|
||||
LogMaxDays int64 `json:"log_max_days"`
|
||||
Token string `json:"token"`
|
||||
AuthTimeout int64 `json:"auth_timeout"`
|
||||
SubDomainHost string `json:"subdomain_host"`
|
||||
TcpMux bool `json:"tcp_mux"`
|
||||
|
||||
PrivilegeAllowPorts map[int]struct{}
|
||||
MaxPoolCount int64
|
||||
HeartBeatTimeout int64
|
||||
UserConnTimeout int64
|
||||
AllowPorts map[int]struct{}
|
||||
MaxPoolCount int64 `json:"max_pool_count"`
|
||||
MaxPortsPerClient int64 `json:"max_ports_per_client"`
|
||||
HeartBeatTimeout int64 `json:"heart_beat_timeout"`
|
||||
UserConnTimeout int64 `json:"user_conn_timeout"`
|
||||
}
|
||||
|
||||
func GetDefaultServerCommonConf() *ServerCommonConf {
|
||||
func GetDefaultServerConf() *ServerCommonConf {
|
||||
return &ServerCommonConf{
|
||||
ConfigFile: "./frps.ini",
|
||||
BindAddr: "0.0.0.0",
|
||||
BindPort: 7000,
|
||||
BindUdpPort: 0,
|
||||
@@ -80,34 +93,40 @@ func GetDefaultServerCommonConf() *ServerCommonConf {
|
||||
LogWay: "console",
|
||||
LogLevel: "info",
|
||||
LogMaxDays: 3,
|
||||
PrivilegeMode: true,
|
||||
PrivilegeToken: "",
|
||||
Token: "",
|
||||
AuthTimeout: 900,
|
||||
SubDomainHost: "",
|
||||
TcpMux: true,
|
||||
PrivilegeAllowPorts: make(map[int]struct{}),
|
||||
AllowPorts: make(map[int]struct{}),
|
||||
MaxPoolCount: 5,
|
||||
MaxPortsPerClient: 0,
|
||||
HeartBeatTimeout: 90,
|
||||
UserConnTimeout: 10,
|
||||
}
|
||||
}
|
||||
|
||||
// Load server common configure.
|
||||
func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
func UnmarshalServerConfFromIni(defaultCfg *ServerCommonConf, content string) (cfg *ServerCommonConf, err error) {
|
||||
cfg = defaultCfg
|
||||
if cfg == nil {
|
||||
cfg = GetDefaultServerConf()
|
||||
}
|
||||
|
||||
conf, err := ini.Load(strings.NewReader(content))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("parse ini conf file error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
tmpStr string
|
||||
ok bool
|
||||
v int64
|
||||
)
|
||||
cfg = GetDefaultServerCommonConf()
|
||||
|
||||
tmpStr, ok = conf.Get("common", "bind_addr")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "bind_addr"); ok {
|
||||
cfg.BindAddr = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "bind_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "bind_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid bind_port")
|
||||
return
|
||||
@@ -116,8 +135,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "bind_udp_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "bind_udp_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid bind_udp_port")
|
||||
return
|
||||
@@ -126,8 +144,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "kcp_bind_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "kcp_bind_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid kcp_bind_port")
|
||||
return
|
||||
@@ -136,15 +153,13 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "proxy_bind_addr")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "proxy_bind_addr"); ok {
|
||||
cfg.ProxyBindAddr = tmpStr
|
||||
} else {
|
||||
cfg.ProxyBindAddr = cfg.BindAddr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "vhost_http_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "vhost_http_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid vhost_http_port")
|
||||
return
|
||||
@@ -155,8 +170,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
cfg.VhostHttpPort = 0
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "vhost_https_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "vhost_https_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid vhost_https_port")
|
||||
return
|
||||
@@ -167,15 +181,13 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
cfg.VhostHttpsPort = 0
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "dashboard_addr")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "dashboard_addr"); ok {
|
||||
cfg.DashboardAddr = tmpStr
|
||||
} else {
|
||||
cfg.DashboardAddr = cfg.BindAddr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "dashboard_port")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "dashboard_port"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid dashboard_port")
|
||||
return
|
||||
@@ -186,23 +198,19 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
cfg.DashboardPort = 0
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "dashboard_user")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "dashboard_user"); ok {
|
||||
cfg.DashboardUser = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "dashboard_pwd")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "dashboard_pwd"); ok {
|
||||
cfg.DashboardPwd = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "assets_dir")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "assets_dir"); ok {
|
||||
cfg.AssetsDir = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "log_file")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "log_file"); ok {
|
||||
cfg.LogFile = tmpStr
|
||||
if cfg.LogFile == "console" {
|
||||
cfg.LogWay = "console"
|
||||
@@ -211,84 +219,59 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "log_level")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "log_level"); ok {
|
||||
cfg.LogLevel = tmpStr
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "log_max_days")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "log_max_days"); ok {
|
||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||
if err == nil {
|
||||
cfg.LogMaxDays = v
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "privilege_mode")
|
||||
if ok {
|
||||
if tmpStr == "true" {
|
||||
cfg.PrivilegeMode = true
|
||||
}
|
||||
}
|
||||
cfg.Token, _ = conf.Get("common", "token")
|
||||
|
||||
// PrivilegeMode configure
|
||||
if cfg.PrivilegeMode == true {
|
||||
cfg.PrivilegeToken, _ = conf.Get("common", "privilege_token")
|
||||
|
||||
allowPortsStr, ok := conf.Get("common", "privilege_allow_ports")
|
||||
if ok {
|
||||
if allowPortsStr, ok := conf.Get("common", "allow_ports"); ok {
|
||||
// e.g. 1000-2000,2001,2002,3000-4000
|
||||
portRanges := strings.Split(allowPortsStr, ",")
|
||||
for _, portRangeStr := range portRanges {
|
||||
// 1000-2000 or 2001
|
||||
portArray := strings.Split(portRangeStr, "-")
|
||||
// length: only 1 or 2 is correct
|
||||
rangeType := len(portArray)
|
||||
if rangeType == 1 {
|
||||
// single port
|
||||
singlePort, errRet := strconv.ParseInt(portArray[0], 10, 64)
|
||||
ports, errRet := util.ParseRangeNumbers(allowPortsStr)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("Parse conf error: privilege_allow_ports is incorrect, %v", errRet)
|
||||
err = fmt.Errorf("Parse conf error: allow_ports: %v", errRet)
|
||||
return
|
||||
}
|
||||
cfg.PrivilegeAllowPorts[int(singlePort)] = struct{}{}
|
||||
} else if rangeType == 2 {
|
||||
// range ports
|
||||
min, errRet := strconv.ParseInt(portArray[0], 10, 64)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("Parse conf error: privilege_allow_ports is incorrect, %v", errRet)
|
||||
return
|
||||
}
|
||||
max, errRet := strconv.ParseInt(portArray[1], 10, 64)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("Parse conf error: privilege_allow_ports is incorrect, %v", errRet)
|
||||
return
|
||||
}
|
||||
if max < min {
|
||||
err = fmt.Errorf("Parse conf error: privilege_allow_ports range incorrect")
|
||||
return
|
||||
}
|
||||
for i := min; i <= max; i++ {
|
||||
cfg.PrivilegeAllowPorts[int(i)] = struct{}{}
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("Parse conf error: privilege_allow_ports is incorrect")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, port := range ports {
|
||||
cfg.AllowPorts[int(port)] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "max_pool_count")
|
||||
if ok {
|
||||
v, err = strconv.ParseInt(tmpStr, 10, 64)
|
||||
if err == nil && v >= 0 {
|
||||
if tmpStr, ok = conf.Get("common", "max_pool_count"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid max_pool_count")
|
||||
return
|
||||
} else {
|
||||
if v < 0 {
|
||||
err = fmt.Errorf("Parse conf error: invalid max_pool_count")
|
||||
return
|
||||
}
|
||||
cfg.MaxPoolCount = v
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "authentication_timeout")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "max_ports_per_client"); ok {
|
||||
if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
|
||||
err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
|
||||
return
|
||||
} else {
|
||||
if v < 0 {
|
||||
err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
|
||||
return
|
||||
}
|
||||
cfg.MaxPortsPerClient = v
|
||||
}
|
||||
}
|
||||
|
||||
if tmpStr, ok = conf.Get("common", "authentication_timeout"); ok {
|
||||
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("Parse conf error: authentication_timeout is incorrect")
|
||||
@@ -298,20 +281,17 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "subdomain_host")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "subdomain_host"); ok {
|
||||
cfg.SubDomainHost = strings.ToLower(strings.TrimSpace(tmpStr))
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "tcp_mux")
|
||||
if ok && tmpStr == "false" {
|
||||
if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" {
|
||||
cfg.TcpMux = false
|
||||
} else {
|
||||
cfg.TcpMux = true
|
||||
}
|
||||
|
||||
tmpStr, ok = conf.Get("common", "heartbeat_timeout")
|
||||
if ok {
|
||||
if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok {
|
||||
v, errRet := strconv.ParseInt(tmpStr, 10, 64)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect")
|
||||
@@ -322,3 +302,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (cfg *ServerCommonConf) Check() (err error) {
|
||||
return
|
||||
}
|
||||
|
@@ -14,8 +14,11 @@
|
||||
|
||||
package errors
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMsgType = errors.New("message type error")
|
||||
ErrCtlClosed = errors.New("control is closed")
|
||||
)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 fatedier, fatedier@gmail.com
|
||||
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -12,40 +12,35 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package pool
|
||||
package msg
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"io"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
jsonMsg "github.com/fatedier/golib/msg/json"
|
||||
)
|
||||
|
||||
func TestPutBuf(t *testing.T) {
|
||||
buf := make([]byte, 512)
|
||||
PutBuf(buf)
|
||||
type Message = jsonMsg.Message
|
||||
|
||||
buf = make([]byte, 1025)
|
||||
PutBuf(buf)
|
||||
var (
|
||||
msgCtl *jsonMsg.MsgCtl
|
||||
)
|
||||
|
||||
buf = make([]byte, 2*1025)
|
||||
PutBuf(buf)
|
||||
|
||||
buf = make([]byte, 5*1025)
|
||||
PutBuf(buf)
|
||||
func init() {
|
||||
msgCtl = jsonMsg.NewMsgCtl()
|
||||
for typeByte, msg := range msgTypeMap {
|
||||
msgCtl.RegisterMsg(typeByte, msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBuf(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
buf := GetBuf(200)
|
||||
assert.Len(buf, 200)
|
||||
|
||||
buf = GetBuf(1025)
|
||||
assert.Len(buf, 1025)
|
||||
|
||||
buf = GetBuf(2 * 1024)
|
||||
assert.Len(buf, 2*1024)
|
||||
|
||||
buf = GetBuf(5 * 2000)
|
||||
assert.Len(buf, 5*2000)
|
||||
func ReadMsg(c io.Reader) (msg Message, err error) {
|
||||
return msgCtl.ReadMsg(c)
|
||||
}
|
||||
|
||||
func ReadMsgInto(c io.Reader, msg Message) (err error) {
|
||||
return msgCtl.ReadMsgInto(c, msg)
|
||||
}
|
||||
|
||||
func WriteMsg(c io.Writer, msg interface{}) (err error) {
|
||||
return msgCtl.WriteMsg(c, msg)
|
||||
}
|
@@ -14,10 +14,7 @@
|
||||
|
||||
package msg
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
)
|
||||
import "net"
|
||||
|
||||
const (
|
||||
TypeLogin = 'o'
|
||||
@@ -40,40 +37,27 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
TypeMap map[byte]reflect.Type
|
||||
TypeStringMap map[reflect.Type]byte
|
||||
msgTypeMap = map[byte]interface{}{
|
||||
TypeLogin: Login{},
|
||||
TypeLoginResp: LoginResp{},
|
||||
TypeNewProxy: NewProxy{},
|
||||
TypeNewProxyResp: NewProxyResp{},
|
||||
TypeCloseProxy: CloseProxy{},
|
||||
TypeNewWorkConn: NewWorkConn{},
|
||||
TypeReqWorkConn: ReqWorkConn{},
|
||||
TypeStartWorkConn: StartWorkConn{},
|
||||
TypeNewVisitorConn: NewVisitorConn{},
|
||||
TypeNewVisitorConnResp: NewVisitorConnResp{},
|
||||
TypePing: Ping{},
|
||||
TypePong: Pong{},
|
||||
TypeUdpPacket: UdpPacket{},
|
||||
TypeNatHoleVisitor: NatHoleVisitor{},
|
||||
TypeNatHoleClient: NatHoleClient{},
|
||||
TypeNatHoleResp: NatHoleResp{},
|
||||
TypeNatHoleSid: NatHoleSid{},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
TypeMap = make(map[byte]reflect.Type)
|
||||
TypeStringMap = make(map[reflect.Type]byte)
|
||||
|
||||
TypeMap[TypeLogin] = reflect.TypeOf(Login{})
|
||||
TypeMap[TypeLoginResp] = reflect.TypeOf(LoginResp{})
|
||||
TypeMap[TypeNewProxy] = reflect.TypeOf(NewProxy{})
|
||||
TypeMap[TypeNewProxyResp] = reflect.TypeOf(NewProxyResp{})
|
||||
TypeMap[TypeCloseProxy] = reflect.TypeOf(CloseProxy{})
|
||||
TypeMap[TypeNewWorkConn] = reflect.TypeOf(NewWorkConn{})
|
||||
TypeMap[TypeReqWorkConn] = reflect.TypeOf(ReqWorkConn{})
|
||||
TypeMap[TypeStartWorkConn] = reflect.TypeOf(StartWorkConn{})
|
||||
TypeMap[TypeNewVisitorConn] = reflect.TypeOf(NewVisitorConn{})
|
||||
TypeMap[TypeNewVisitorConnResp] = reflect.TypeOf(NewVisitorConnResp{})
|
||||
TypeMap[TypePing] = reflect.TypeOf(Ping{})
|
||||
TypeMap[TypePong] = reflect.TypeOf(Pong{})
|
||||
TypeMap[TypeUdpPacket] = reflect.TypeOf(UdpPacket{})
|
||||
TypeMap[TypeNatHoleVisitor] = reflect.TypeOf(NatHoleVisitor{})
|
||||
TypeMap[TypeNatHoleClient] = reflect.TypeOf(NatHoleClient{})
|
||||
TypeMap[TypeNatHoleResp] = reflect.TypeOf(NatHoleResp{})
|
||||
TypeMap[TypeNatHoleSid] = reflect.TypeOf(NatHoleSid{})
|
||||
|
||||
for k, v := range TypeMap {
|
||||
TypeStringMap[v] = k
|
||||
}
|
||||
}
|
||||
|
||||
// Message wraps socket packages for communicating between frpc and frps.
|
||||
type Message interface{}
|
||||
|
||||
// When frpc start, client send this message to login to server.
|
||||
type Login struct {
|
||||
Version string `json:"version"`
|
||||
@@ -179,6 +163,7 @@ type NatHoleResp struct {
|
||||
Sid string `json:"sid"`
|
||||
VisitorAddr string `json:"visitor_addr"`
|
||||
ClientAddr string `json:"client_addr"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type NatHoleSid struct {
|
||||
|
@@ -1,87 +0,0 @@
|
||||
// Copyright 2016 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// 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 msg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/fatedier/frp/utils/errors"
|
||||
)
|
||||
|
||||
type TestStruct struct{}
|
||||
|
||||
func TestPack(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var (
|
||||
msg Message
|
||||
buffer []byte
|
||||
err error
|
||||
)
|
||||
|
||||
// error type
|
||||
msg = &TestStruct{}
|
||||
buffer, err = Pack(msg)
|
||||
assert.Error(err, errors.ErrMsgType.Error())
|
||||
|
||||
// correct
|
||||
msg = &Ping{}
|
||||
buffer, err = Pack(msg)
|
||||
assert.NoError(err)
|
||||
b := bytes.NewBuffer(nil)
|
||||
b.WriteByte(TypePing)
|
||||
binary.Write(b, binary.BigEndian, int64(2))
|
||||
b.WriteString("{}")
|
||||
assert.True(bytes.Equal(b.Bytes(), buffer))
|
||||
}
|
||||
|
||||
func TestUnPack(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var (
|
||||
msg Message
|
||||
err error
|
||||
)
|
||||
|
||||
// error message type
|
||||
msg, err = UnPack('-', []byte("{}"))
|
||||
assert.Error(err)
|
||||
|
||||
// correct
|
||||
msg, err = UnPack(TypePong, []byte("{}"))
|
||||
assert.NoError(err)
|
||||
assert.Equal(reflect.TypeOf(msg).Elem(), reflect.TypeOf(Pong{}))
|
||||
}
|
||||
|
||||
func TestUnPackInto(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var err error
|
||||
|
||||
// correct type
|
||||
pongMsg := &Pong{}
|
||||
err = UnPackInto([]byte("{}"), pongMsg)
|
||||
assert.NoError(err)
|
||||
|
||||
// wrong type
|
||||
loginMsg := &Login{}
|
||||
err = UnPackInto([]byte(`{"version": 123}`), loginMsg)
|
||||
assert.Error(err)
|
||||
}
|
@@ -1,97 +0,0 @@
|
||||
// Copyright 2016 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// 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 msg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestProcess(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var (
|
||||
msg Message
|
||||
resMsg Message
|
||||
err error
|
||||
)
|
||||
// empty struct
|
||||
msg = &Ping{}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
err = WriteMsg(buffer, msg)
|
||||
assert.NoError(err)
|
||||
|
||||
resMsg, err = ReadMsg(buffer)
|
||||
assert.NoError(err)
|
||||
assert.Equal(reflect.TypeOf(resMsg).Elem(), TypeMap[TypePing])
|
||||
|
||||
// normal message
|
||||
msg = &StartWorkConn{
|
||||
ProxyName: "test",
|
||||
}
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
err = WriteMsg(buffer, msg)
|
||||
assert.NoError(err)
|
||||
|
||||
resMsg, err = ReadMsg(buffer)
|
||||
assert.NoError(err)
|
||||
assert.Equal(reflect.TypeOf(resMsg).Elem(), TypeMap[TypeStartWorkConn])
|
||||
|
||||
startWorkConnMsg, ok := resMsg.(*StartWorkConn)
|
||||
assert.True(ok)
|
||||
assert.Equal("test", startWorkConnMsg.ProxyName)
|
||||
|
||||
// ReadMsgInto correct
|
||||
msg = &Pong{}
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
err = WriteMsg(buffer, msg)
|
||||
assert.NoError(err)
|
||||
|
||||
err = ReadMsgInto(buffer, msg)
|
||||
assert.NoError(err)
|
||||
|
||||
// ReadMsgInto error type
|
||||
content := []byte(`{"run_id": 123}`)
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
buffer.WriteByte(TypeNewWorkConn)
|
||||
binary.Write(buffer, binary.BigEndian, int64(len(content)))
|
||||
buffer.Write(content)
|
||||
|
||||
resMsg = &NewWorkConn{}
|
||||
err = ReadMsgInto(buffer, resMsg)
|
||||
assert.Error(err)
|
||||
|
||||
// message format error
|
||||
buffer = bytes.NewBuffer([]byte("1234"))
|
||||
|
||||
resMsg = &NewProxyResp{}
|
||||
err = ReadMsgInto(buffer, resMsg)
|
||||
assert.Error(err)
|
||||
|
||||
// MaxLength, real message length is 2
|
||||
MaxMsgLength = 1
|
||||
msg = &Ping{}
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
err = WriteMsg(buffer, msg)
|
||||
assert.NoError(err)
|
||||
|
||||
_, err = ReadMsg(buffer)
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
@@ -17,16 +17,15 @@ package plugin
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/fatedier/frp/utils/errors"
|
||||
frpIo "github.com/fatedier/frp/utils/io"
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
|
||||
frpIo "github.com/fatedier/golib/io"
|
||||
gnet "github.com/fatedier/golib/net"
|
||||
)
|
||||
|
||||
const PluginHttpProxy = "http_proxy"
|
||||
@@ -35,47 +34,6 @@ func init() {
|
||||
Register(PluginHttpProxy, NewHttpProxyPlugin)
|
||||
}
|
||||
|
||||
type Listener struct {
|
||||
conns chan net.Conn
|
||||
closed bool
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func NewProxyListener() *Listener {
|
||||
return &Listener{
|
||||
conns: make(chan net.Conn, 64),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Listener) Accept() (net.Conn, error) {
|
||||
conn, ok := <-l.conns
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("listener closed")
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (l *Listener) PutConn(conn net.Conn) error {
|
||||
err := errors.PanicToError(func() {
|
||||
l.conns <- conn
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (l *Listener) Close() error {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if !l.closed {
|
||||
close(l.conns)
|
||||
l.closed = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Listener) Addr() net.Addr {
|
||||
return (*net.TCPAddr)(nil)
|
||||
}
|
||||
|
||||
type HttpProxy struct {
|
||||
l *Listener
|
||||
s *http.Server
|
||||
@@ -109,15 +67,22 @@ func (hp *HttpProxy) Name() string {
|
||||
func (hp *HttpProxy) Handle(conn io.ReadWriteCloser, realConn frpNet.Conn) {
|
||||
wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
|
||||
|
||||
sc, rd := frpNet.NewShareConn(wrapConn)
|
||||
request, err := http.ReadRequest(bufio.NewReader(rd))
|
||||
sc, rd := gnet.NewSharedConn(wrapConn)
|
||||
firstBytes := make([]byte, 7)
|
||||
_, err := rd.Read(firstBytes)
|
||||
if err != nil {
|
||||
wrapConn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if request.Method == http.MethodConnect {
|
||||
hp.handleConnectReq(request, frpIo.WrapReadWriteCloser(rd, wrapConn, nil))
|
||||
if strings.ToUpper(string(firstBytes)) == "CONNECT" {
|
||||
bufRd := bufio.NewReader(sc)
|
||||
request, err := http.ReadRequest(bufRd)
|
||||
if err != nil {
|
||||
wrapConn.Close()
|
||||
return
|
||||
}
|
||||
hp.handleConnectReq(request, frpIo.WrapReadWriteCloser(bufRd, wrapConn, wrapConn.Close))
|
||||
return
|
||||
}
|
||||
|
||||
|
@@ -17,8 +17,12 @@ package plugin
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
|
||||
"github.com/fatedier/golib/errors"
|
||||
)
|
||||
|
||||
// Creators is used for create plugins to handle connections.
|
||||
@@ -45,3 +49,44 @@ type Plugin interface {
|
||||
Handle(conn io.ReadWriteCloser, realConn frpNet.Conn)
|
||||
Close() error
|
||||
}
|
||||
|
||||
type Listener struct {
|
||||
conns chan net.Conn
|
||||
closed bool
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func NewProxyListener() *Listener {
|
||||
return &Listener{
|
||||
conns: make(chan net.Conn, 64),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Listener) Accept() (net.Conn, error) {
|
||||
conn, ok := <-l.conns
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("listener closed")
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (l *Listener) PutConn(conn net.Conn) error {
|
||||
err := errors.PanicToError(func() {
|
||||
l.conns <- conn
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (l *Listener) Close() error {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if !l.closed {
|
||||
close(l.conns)
|
||||
l.closed = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Listener) Addr() net.Addr {
|
||||
return (*net.TCPAddr)(nil)
|
||||
}
|
||||
|
@@ -32,13 +32,23 @@ func init() {
|
||||
|
||||
type Socks5Plugin struct {
|
||||
Server *gosocks5.Server
|
||||
|
||||
user string
|
||||
passwd string
|
||||
}
|
||||
|
||||
func NewSocks5Plugin(params map[string]string) (p Plugin, err error) {
|
||||
sp := &Socks5Plugin{}
|
||||
sp.Server, err = gosocks5.New(&gosocks5.Config{
|
||||
user := params["plugin_user"]
|
||||
passwd := params["plugin_passwd"]
|
||||
|
||||
cfg := &gosocks5.Config{
|
||||
Logger: log.New(ioutil.Discard, "", log.LstdFlags),
|
||||
})
|
||||
}
|
||||
if user != "" || passwd != "" {
|
||||
cfg.Credentials = gosocks5.StaticCredentials(map[string]string{user: passwd})
|
||||
}
|
||||
sp := &Socks5Plugin{}
|
||||
sp.Server, err = gosocks5.New(cfg)
|
||||
p = sp
|
||||
return
|
||||
}
|
||||
|
87
models/plugin/static_file.go
Normal file
87
models/plugin/static_file.go
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright 2018 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// 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 plugin
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
)
|
||||
|
||||
const PluginStaticFile = "static_file"
|
||||
|
||||
func init() {
|
||||
Register(PluginStaticFile, NewStaticFilePlugin)
|
||||
}
|
||||
|
||||
type StaticFilePlugin struct {
|
||||
localPath string
|
||||
stripPrefix string
|
||||
httpUser string
|
||||
httpPasswd string
|
||||
|
||||
l *Listener
|
||||
s *http.Server
|
||||
}
|
||||
|
||||
func NewStaticFilePlugin(params map[string]string) (Plugin, error) {
|
||||
localPath := params["plugin_local_path"]
|
||||
stripPrefix := params["plugin_strip_prefix"]
|
||||
httpUser := params["plugin_http_user"]
|
||||
httpPasswd := params["plugin_http_passwd"]
|
||||
|
||||
listener := NewProxyListener()
|
||||
|
||||
sp := &StaticFilePlugin{
|
||||
localPath: localPath,
|
||||
stripPrefix: stripPrefix,
|
||||
httpUser: httpUser,
|
||||
httpPasswd: httpPasswd,
|
||||
|
||||
l: listener,
|
||||
}
|
||||
var prefix string
|
||||
if stripPrefix != "" {
|
||||
prefix = "/" + stripPrefix + "/"
|
||||
} else {
|
||||
prefix = "/"
|
||||
}
|
||||
router := httprouter.New()
|
||||
router.Handler("GET", prefix+"*filepath", frpNet.MakeHttpGzipHandler(
|
||||
frpNet.NewHttpBasicAuthWraper(http.StripPrefix(prefix, http.FileServer(http.Dir(localPath))), httpUser, httpPasswd)))
|
||||
sp.s = &http.Server{
|
||||
Handler: router,
|
||||
}
|
||||
go sp.s.Serve(listener)
|
||||
return sp, nil
|
||||
}
|
||||
|
||||
func (sp *StaticFilePlugin) Handle(conn io.ReadWriteCloser, realConn frpNet.Conn) {
|
||||
wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
|
||||
sp.l.PutConn(wrapConn)
|
||||
}
|
||||
|
||||
func (sp *StaticFilePlugin) Name() string {
|
||||
return PluginStaticFile
|
||||
}
|
||||
|
||||
func (sp *StaticFilePlugin) Close() error {
|
||||
sp.s.Close()
|
||||
sp.l.Close()
|
||||
return nil
|
||||
}
|
@@ -19,8 +19,9 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
|
||||
frpIo "github.com/fatedier/frp/utils/io"
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
|
||||
frpIo "github.com/fatedier/golib/io"
|
||||
)
|
||||
|
||||
const PluginUnixDomainSocket = "unix_domain_socket"
|
||||
|
@@ -21,8 +21,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
"github.com/fatedier/frp/utils/errors"
|
||||
"github.com/fatedier/frp/utils/pool"
|
||||
|
||||
"github.com/fatedier/golib/errors"
|
||||
"github.com/fatedier/golib/pool"
|
||||
)
|
||||
|
||||
func NewUdpPacket(buf []byte, laddr, raddr *net.UDPAddr) *msg.UdpPacket {
|
||||
@@ -82,6 +83,7 @@ func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UdpPacket, sendCh chan<-
|
||||
mu.Lock()
|
||||
delete(udpConnMap, addr)
|
||||
mu.Unlock()
|
||||
udpConn.Close()
|
||||
}()
|
||||
|
||||
buf := pool.GetBuf(1500)
|
||||
|
@@ -14,7 +14,7 @@ make -f ./Makefile.cross-compiles
|
||||
rm -rf ./packages
|
||||
mkdir ./packages
|
||||
|
||||
os_all='linux windows darwin'
|
||||
os_all='linux windows darwin freebsd'
|
||||
arch_all='386 amd64 arm mips64 mips64le mips mipsle'
|
||||
|
||||
for os in $os_all; do
|
||||
|
@@ -17,17 +17,21 @@ package server
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/models/consts"
|
||||
frpErr "github.com/fatedier/frp/models/errors"
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
"github.com/fatedier/frp/utils/crypto"
|
||||
"github.com/fatedier/frp/utils/errors"
|
||||
"github.com/fatedier/frp/utils/net"
|
||||
"github.com/fatedier/frp/utils/shutdown"
|
||||
"github.com/fatedier/frp/utils/version"
|
||||
|
||||
"github.com/fatedier/golib/control/shutdown"
|
||||
"github.com/fatedier/golib/crypto"
|
||||
"github.com/fatedier/golib/errors"
|
||||
)
|
||||
|
||||
type Control struct {
|
||||
@@ -55,6 +59,9 @@ type Control struct {
|
||||
// pool count
|
||||
poolCount int
|
||||
|
||||
// ports used, for limitations
|
||||
portsUsedNum int
|
||||
|
||||
// last time got the Ping message
|
||||
lastPing time.Time
|
||||
|
||||
@@ -84,6 +91,7 @@ func NewControl(svr *Service, ctlConn net.Conn, loginMsg *msg.Login) *Control {
|
||||
workConnCh: make(chan net.Conn, loginMsg.PoolCount+10),
|
||||
proxies: make(map[string]Proxy),
|
||||
poolCount: loginMsg.PoolCount,
|
||||
portsUsedNum: 0,
|
||||
lastPing: time.Now(),
|
||||
runId: loginMsg.RunId,
|
||||
status: consts.Working,
|
||||
@@ -99,7 +107,7 @@ func (ctl *Control) Start() {
|
||||
loginRespMsg := &msg.LoginResp{
|
||||
Version: version.Full(),
|
||||
RunId: ctl.runId,
|
||||
ServerUdpPort: config.ServerCommonCfg.BindUdpPort,
|
||||
ServerUdpPort: g.GlbServerCfg.BindUdpPort,
|
||||
Error: "",
|
||||
}
|
||||
msg.WriteMsg(ctl.conn, loginRespMsg)
|
||||
@@ -118,6 +126,7 @@ func (ctl *Control) RegisterWorkConn(conn net.Conn) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ctl.conn.Error("panic error: %v", err)
|
||||
ctl.conn.Error(string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -138,6 +147,7 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ctl.conn.Error("panic error: %v", err)
|
||||
ctl.conn.Error(string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -146,7 +156,7 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
|
||||
select {
|
||||
case workConn, ok = <-ctl.workConnCh:
|
||||
if !ok {
|
||||
err = errors.ErrCtlClosed
|
||||
err = frpErr.ErrCtlClosed
|
||||
return
|
||||
}
|
||||
ctl.conn.Debug("get work connection from pool")
|
||||
@@ -163,12 +173,12 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
|
||||
select {
|
||||
case workConn, ok = <-ctl.workConnCh:
|
||||
if !ok {
|
||||
err = errors.ErrCtlClosed
|
||||
err = frpErr.ErrCtlClosed
|
||||
ctl.conn.Warn("no work connections avaiable, %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
case <-time.After(time.Duration(config.ServerCommonCfg.UserConnTimeout) * time.Second):
|
||||
case <-time.After(time.Duration(g.GlbServerCfg.UserConnTimeout) * time.Second):
|
||||
err = fmt.Errorf("timeout trying to get work connection")
|
||||
ctl.conn.Warn("%v", err)
|
||||
return
|
||||
@@ -192,13 +202,14 @@ func (ctl *Control) writer() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ctl.conn.Error("panic error: %v", err)
|
||||
ctl.conn.Error(string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
|
||||
defer ctl.allShutdown.Start()
|
||||
defer ctl.writerShutdown.Done()
|
||||
|
||||
encWriter, err := crypto.NewWriter(ctl.conn, []byte(config.ServerCommonCfg.PrivilegeToken))
|
||||
encWriter, err := crypto.NewWriter(ctl.conn, []byte(g.GlbServerCfg.Token))
|
||||
if err != nil {
|
||||
ctl.conn.Error("crypto new writer error: %v", err)
|
||||
ctl.allShutdown.Start()
|
||||
@@ -221,13 +232,14 @@ func (ctl *Control) reader() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ctl.conn.Error("panic error: %v", err)
|
||||
ctl.conn.Error(string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
|
||||
defer ctl.allShutdown.Start()
|
||||
defer ctl.readerShutdown.Done()
|
||||
|
||||
encReader := crypto.NewReader(ctl.conn, []byte(config.ServerCommonCfg.PrivilegeToken))
|
||||
encReader := crypto.NewReader(ctl.conn, []byte(g.GlbServerCfg.Token))
|
||||
for {
|
||||
if m, err := msg.ReadMsg(encReader); err != nil {
|
||||
if err == io.EOF {
|
||||
@@ -247,6 +259,7 @@ func (ctl *Control) stoper() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ctl.conn.Error("panic error: %v", err)
|
||||
ctl.conn.Error(string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -261,13 +274,14 @@ func (ctl *Control) stoper() {
|
||||
ctl.conn.Close()
|
||||
ctl.readerShutdown.WaitDone()
|
||||
|
||||
ctl.mu.Lock()
|
||||
defer ctl.mu.Unlock()
|
||||
|
||||
close(ctl.workConnCh)
|
||||
for workConn := range ctl.workConnCh {
|
||||
workConn.Close()
|
||||
}
|
||||
|
||||
ctl.mu.Lock()
|
||||
defer ctl.mu.Unlock()
|
||||
for _, pxy := range ctl.proxies {
|
||||
pxy.Close()
|
||||
ctl.svr.DelProxy(pxy.GetName())
|
||||
@@ -284,6 +298,7 @@ func (ctl *Control) manager() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
ctl.conn.Error("panic error: %v", err)
|
||||
ctl.conn.Error(string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -296,9 +311,9 @@ func (ctl *Control) manager() {
|
||||
for {
|
||||
select {
|
||||
case <-heartbeat.C:
|
||||
if time.Since(ctl.lastPing) > time.Duration(config.ServerCommonCfg.HeartBeatTimeout)*time.Second {
|
||||
if time.Since(ctl.lastPing) > time.Duration(g.GlbServerCfg.HeartBeatTimeout)*time.Second {
|
||||
ctl.conn.Warn("heartbeat timeout")
|
||||
ctl.allShutdown.Start()
|
||||
return
|
||||
}
|
||||
case rawMsg, ok := <-ctl.readCh:
|
||||
if !ok {
|
||||
@@ -336,7 +351,7 @@ func (ctl *Control) manager() {
|
||||
func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err error) {
|
||||
var pxyConf config.ProxyConf
|
||||
// Load configures from NewProxy message and check.
|
||||
pxyConf, err = config.NewProxyConf(pxyMsg)
|
||||
pxyConf, err = config.NewProxyConfFromMsg(pxyMsg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -348,6 +363,26 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err
|
||||
return remoteAddr, err
|
||||
}
|
||||
|
||||
// Check ports used number in each client
|
||||
if g.GlbServerCfg.MaxPortsPerClient > 0 {
|
||||
ctl.mu.Lock()
|
||||
if ctl.portsUsedNum+pxy.GetUsedPortsNum() > int(g.GlbServerCfg.MaxPortsPerClient) {
|
||||
ctl.mu.Unlock()
|
||||
err = fmt.Errorf("exceed the max_ports_per_client")
|
||||
return
|
||||
}
|
||||
ctl.portsUsedNum = ctl.portsUsedNum + pxy.GetUsedPortsNum()
|
||||
ctl.mu.Unlock()
|
||||
|
||||
defer func() {
|
||||
if err != nil {
|
||||
ctl.mu.Lock()
|
||||
ctl.portsUsedNum = ctl.portsUsedNum - pxy.GetUsedPortsNum()
|
||||
ctl.mu.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
remoteAddr, err = pxy.Run()
|
||||
if err != nil {
|
||||
return
|
||||
@@ -371,16 +406,21 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err
|
||||
|
||||
func (ctl *Control) CloseProxy(closeMsg *msg.CloseProxy) (err error) {
|
||||
ctl.mu.Lock()
|
||||
defer ctl.mu.Unlock()
|
||||
|
||||
pxy, ok := ctl.proxies[closeMsg.ProxyName]
|
||||
if !ok {
|
||||
ctl.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
if g.GlbServerCfg.MaxPortsPerClient > 0 {
|
||||
ctl.portsUsedNum = ctl.portsUsedNum - pxy.GetUsedPortsNum()
|
||||
}
|
||||
pxy.Close()
|
||||
ctl.svr.DelProxy(pxy.GetName())
|
||||
delete(ctl.proxies, closeMsg.ProxyName)
|
||||
ctl.mu.Unlock()
|
||||
|
||||
StatsCloseProxy(pxy.GetName(), pxy.GetConf().GetBaseInfo().ProxyType)
|
||||
return
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/assets"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/g"
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
@@ -36,10 +36,14 @@ func RunDashboardServer(addr string, port int) (err error) {
|
||||
// url router
|
||||
router := httprouter.New()
|
||||
|
||||
user, passwd := config.ServerCommonCfg.DashboardUser, config.ServerCommonCfg.DashboardPwd
|
||||
user, passwd := g.GlbServerCfg.DashboardUser, g.GlbServerCfg.DashboardPwd
|
||||
|
||||
// api, see dashboard_api.go
|
||||
router.GET("/api/serverinfo", frpNet.HttprouterBasicAuth(apiServerInfo, user, passwd))
|
||||
router.GET("/api/proxy/tcp/:name", frpNet.HttprouterBasicAuth(apiProxyTcpByName, user, passwd))
|
||||
router.GET("/api/proxy/udp/:name", frpNet.HttprouterBasicAuth(apiProxyUdpByName, user, passwd))
|
||||
router.GET("/api/proxy/http/:name", frpNet.HttprouterBasicAuth(apiProxyHttpByName, user, passwd))
|
||||
router.GET("/api/proxy/https/:name", frpNet.HttprouterBasicAuth(apiProxyHttpsByName, user, passwd))
|
||||
router.GET("/api/proxy/tcp", frpNet.HttprouterBasicAuth(apiProxyTcp, user, passwd))
|
||||
router.GET("/api/proxy/udp", frpNet.HttprouterBasicAuth(apiProxyUdp, user, passwd))
|
||||
router.GET("/api/proxy/http", frpNet.HttprouterBasicAuth(apiProxyHttp, user, passwd))
|
||||
|
@@ -18,6 +18,7 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/models/consts"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
@@ -36,11 +37,15 @@ type ServerInfoResp struct {
|
||||
GeneralResponse
|
||||
|
||||
Version string `json:"version"`
|
||||
BindPort int `json:"bind_port"`
|
||||
BindUdpPort int `json:"bind_udp_port"`
|
||||
VhostHttpPort int `json:"vhost_http_port"`
|
||||
VhostHttpsPort int `json:"vhost_https_port"`
|
||||
KcpBindPort int `json:"kcp_bind_port"`
|
||||
AuthTimeout int64 `json:"auth_timeout"`
|
||||
SubdomainHost string `json:"subdomain_host"`
|
||||
MaxPoolCount int64 `json:"max_pool_count"`
|
||||
MaxPortsPerClient int64 `json:"max_ports_per_client"`
|
||||
HeartBeatTimeout int64 `json:"heart_beat_timeout"`
|
||||
|
||||
TotalTrafficIn int64 `json:"total_traffic_in"`
|
||||
@@ -60,15 +65,19 @@ func apiServerInfo(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
|
||||
}()
|
||||
|
||||
log.Info("Http request: [/api/serverinfo]")
|
||||
cfg := config.ServerCommonCfg
|
||||
cfg := &g.GlbServerCfg.ServerCommonConf
|
||||
serverStats := StatsGetServer()
|
||||
res = ServerInfoResp{
|
||||
Version: version.Full(),
|
||||
BindPort: cfg.BindPort,
|
||||
BindUdpPort: cfg.BindUdpPort,
|
||||
VhostHttpPort: cfg.VhostHttpPort,
|
||||
VhostHttpsPort: cfg.VhostHttpsPort,
|
||||
KcpBindPort: cfg.KcpBindPort,
|
||||
AuthTimeout: cfg.AuthTimeout,
|
||||
SubdomainHost: cfg.SubDomainHost,
|
||||
MaxPoolCount: cfg.MaxPoolCount,
|
||||
MaxPortsPerClient: cfg.MaxPortsPerClient,
|
||||
HeartBeatTimeout: cfg.HeartBeatTimeout,
|
||||
|
||||
TotalTrafficIn: serverStats.TotalTrafficIn,
|
||||
@@ -189,6 +198,119 @@ func getProxyStatsByType(proxyType string) (proxyInfos []*ProxyStatsInfo) {
|
||||
return
|
||||
}
|
||||
|
||||
// Get proxy info by name.
|
||||
type GetProxyStatsResp struct {
|
||||
GeneralResponse
|
||||
|
||||
Name string `json:"name"`
|
||||
Conf config.ProxyConf `json:"conf"`
|
||||
TodayTrafficIn int64 `json:"today_traffic_in"`
|
||||
TodayTrafficOut int64 `json:"today_traffic_out"`
|
||||
CurConns int64 `json:"cur_conns"`
|
||||
LastStartTime string `json:"last_start_time"`
|
||||
LastCloseTime string `json:"last_close_time"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// api/proxy/tcp/:name
|
||||
func apiProxyTcpByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
var (
|
||||
buf []byte
|
||||
res GetProxyStatsResp
|
||||
)
|
||||
name := params.ByName("name")
|
||||
|
||||
defer func() {
|
||||
log.Info("Http response [/api/proxy/tcp/:name]: code [%d]", res.Code)
|
||||
}()
|
||||
log.Info("Http request: [/api/proxy/tcp/:name]")
|
||||
|
||||
res = getProxyStatsByTypeAndName(consts.TcpProxy, name)
|
||||
|
||||
buf, _ = json.Marshal(&res)
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
// api/proxy/udp/:name
|
||||
func apiProxyUdpByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
var (
|
||||
buf []byte
|
||||
res GetProxyStatsResp
|
||||
)
|
||||
name := params.ByName("name")
|
||||
|
||||
defer func() {
|
||||
log.Info("Http response [/api/proxy/udp/:name]: code [%d]", res.Code)
|
||||
}()
|
||||
log.Info("Http request: [/api/proxy/udp/:name]")
|
||||
|
||||
res = getProxyStatsByTypeAndName(consts.UdpProxy, name)
|
||||
|
||||
buf, _ = json.Marshal(&res)
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
// api/proxy/http/:name
|
||||
func apiProxyHttpByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
var (
|
||||
buf []byte
|
||||
res GetProxyStatsResp
|
||||
)
|
||||
name := params.ByName("name")
|
||||
|
||||
defer func() {
|
||||
log.Info("Http response [/api/proxy/http/:name]: code [%d]", res.Code)
|
||||
}()
|
||||
log.Info("Http request: [/api/proxy/http/:name]")
|
||||
|
||||
res = getProxyStatsByTypeAndName(consts.HttpProxy, name)
|
||||
|
||||
buf, _ = json.Marshal(&res)
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
// api/proxy/https/:name
|
||||
func apiProxyHttpsByName(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
|
||||
var (
|
||||
buf []byte
|
||||
res GetProxyStatsResp
|
||||
)
|
||||
name := params.ByName("name")
|
||||
|
||||
defer func() {
|
||||
log.Info("Http response [/api/proxy/https/:name]: code [%d]", res.Code)
|
||||
}()
|
||||
log.Info("Http request: [/api/proxy/https/:name]")
|
||||
|
||||
res = getProxyStatsByTypeAndName(consts.HttpsProxy, name)
|
||||
|
||||
buf, _ = json.Marshal(&res)
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
func getProxyStatsByTypeAndName(proxyType string, proxyName string) (proxyInfo GetProxyStatsResp) {
|
||||
proxyInfo.Name = proxyName
|
||||
ps := StatsGetProxiesByTypeAndName(proxyType, proxyName)
|
||||
if ps == nil {
|
||||
proxyInfo.Code = 1
|
||||
proxyInfo.Msg = "no proxy info found"
|
||||
} else {
|
||||
if pxy, ok := ServerService.pxyManager.GetByName(proxyName); ok {
|
||||
proxyInfo.Conf = pxy.GetConf()
|
||||
proxyInfo.Status = consts.Online
|
||||
} else {
|
||||
proxyInfo.Status = consts.Offline
|
||||
}
|
||||
proxyInfo.TodayTrafficIn = ps.TodayTrafficIn
|
||||
proxyInfo.TodayTrafficOut = ps.TodayTrafficOut
|
||||
proxyInfo.CurConns = ps.CurConns
|
||||
proxyInfo.LastStartTime = ps.LastStartTime
|
||||
proxyInfo.LastCloseTime = ps.LastCloseTime
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// api/proxy/traffic/:name
|
||||
type GetProxyTrafficResp struct {
|
||||
GeneralResponse
|
||||
|
@@ -19,9 +19,10 @@ import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
frpIo "github.com/fatedier/frp/utils/io"
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
"github.com/fatedier/frp/utils/util"
|
||||
|
||||
frpIo "github.com/fatedier/golib/io"
|
||||
)
|
||||
|
||||
type ControlManager struct {
|
||||
|
@@ -18,7 +18,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
"github.com/fatedier/frp/utils/metric"
|
||||
)
|
||||
@@ -92,19 +92,19 @@ func StatsClearUselessInfo() {
|
||||
}
|
||||
|
||||
func StatsNewClient() {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.ClientCounts.Inc(1)
|
||||
}
|
||||
}
|
||||
|
||||
func StatsCloseClient() {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.ClientCounts.Dec(1)
|
||||
}
|
||||
}
|
||||
|
||||
func StatsNewProxy(name string, proxyType string) {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.mu.Lock()
|
||||
defer globalStats.mu.Unlock()
|
||||
counter, ok := globalStats.ProxyTypeCounts[proxyType]
|
||||
@@ -130,7 +130,7 @@ func StatsNewProxy(name string, proxyType string) {
|
||||
}
|
||||
|
||||
func StatsCloseProxy(proxyName string, proxyType string) {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.mu.Lock()
|
||||
defer globalStats.mu.Unlock()
|
||||
if counter, ok := globalStats.ProxyTypeCounts[proxyType]; ok {
|
||||
@@ -143,7 +143,7 @@ func StatsCloseProxy(proxyName string, proxyType string) {
|
||||
}
|
||||
|
||||
func StatsOpenConnection(name string) {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.CurConns.Inc(1)
|
||||
|
||||
globalStats.mu.Lock()
|
||||
@@ -157,7 +157,7 @@ func StatsOpenConnection(name string) {
|
||||
}
|
||||
|
||||
func StatsCloseConnection(name string) {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.CurConns.Dec(1)
|
||||
|
||||
globalStats.mu.Lock()
|
||||
@@ -171,7 +171,7 @@ func StatsCloseConnection(name string) {
|
||||
}
|
||||
|
||||
func StatsAddTrafficIn(name string, trafficIn int64) {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.TotalTrafficIn.Inc(trafficIn)
|
||||
|
||||
globalStats.mu.Lock()
|
||||
@@ -186,7 +186,7 @@ func StatsAddTrafficIn(name string, trafficIn int64) {
|
||||
}
|
||||
|
||||
func StatsAddTrafficOut(name string, trafficOut int64) {
|
||||
if config.ServerCommonCfg.DashboardPort != 0 {
|
||||
if g.GlbServerCfg.DashboardPort != 0 {
|
||||
globalStats.TotalTrafficOut.Inc(trafficOut)
|
||||
|
||||
globalStats.mu.Lock()
|
||||
@@ -263,6 +263,37 @@ func StatsGetProxiesByType(proxyType string) []*ProxyStats {
|
||||
return res
|
||||
}
|
||||
|
||||
func StatsGetProxiesByTypeAndName(proxyType string, proxyName string) (res *ProxyStats) {
|
||||
globalStats.mu.Lock()
|
||||
defer globalStats.mu.Unlock()
|
||||
|
||||
for name, proxyStats := range globalStats.ProxyStatistics {
|
||||
if proxyStats.ProxyType != proxyType {
|
||||
continue
|
||||
}
|
||||
|
||||
if name != proxyName {
|
||||
continue
|
||||
}
|
||||
|
||||
res = &ProxyStats{
|
||||
Name: name,
|
||||
Type: proxyStats.ProxyType,
|
||||
TodayTrafficIn: proxyStats.TrafficIn.TodayCount(),
|
||||
TodayTrafficOut: proxyStats.TrafficOut.TodayCount(),
|
||||
CurConns: proxyStats.CurConns.Count(),
|
||||
}
|
||||
if !proxyStats.LastStartTime.IsZero() {
|
||||
res.LastStartTime = proxyStats.LastStartTime.Format("01-02 15:04:05")
|
||||
}
|
||||
if !proxyStats.LastCloseTime.IsZero() {
|
||||
res.LastCloseTime = proxyStats.LastCloseTime.Format("01-02 15:04:05")
|
||||
}
|
||||
break
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ProxyTrafficInfo struct {
|
||||
Name string
|
||||
TrafficIn []int64
|
||||
|
@@ -8,10 +8,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
"github.com/fatedier/frp/utils/errors"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
"github.com/fatedier/frp/utils/pool"
|
||||
"github.com/fatedier/frp/utils/util"
|
||||
|
||||
"github.com/fatedier/golib/errors"
|
||||
"github.com/fatedier/golib/pool"
|
||||
)
|
||||
|
||||
// Timeout seconds.
|
||||
@@ -105,10 +106,21 @@ func (nc *NatHoleController) HandleVisitor(m *msg.NatHoleVisitor, raddr *net.UDP
|
||||
}
|
||||
nc.mu.Lock()
|
||||
clientCfg, ok := nc.clientCfgs[m.ProxyName]
|
||||
if !ok || m.SignKey != util.GetAuthKey(clientCfg.Sk, m.Timestamp) {
|
||||
if !ok {
|
||||
nc.mu.Unlock()
|
||||
errInfo := fmt.Sprintf("xtcp server for [%s] doesn't exist", m.ProxyName)
|
||||
log.Debug(errInfo)
|
||||
nc.listener.WriteToUDP(nc.GenNatHoleResponse(nil, errInfo), raddr)
|
||||
return
|
||||
}
|
||||
if m.SignKey != util.GetAuthKey(clientCfg.Sk, m.Timestamp) {
|
||||
nc.mu.Unlock()
|
||||
errInfo := fmt.Sprintf("xtcp connection of [%s] auth failed", m.ProxyName)
|
||||
log.Debug(errInfo)
|
||||
nc.listener.WriteToUDP(nc.GenNatHoleResponse(nil, errInfo), raddr)
|
||||
return
|
||||
}
|
||||
|
||||
nc.sessions[sid] = session
|
||||
nc.mu.Unlock()
|
||||
log.Trace("handle visitor message, sid [%s]", sid)
|
||||
@@ -129,7 +141,7 @@ func (nc *NatHoleController) HandleVisitor(m *msg.NatHoleVisitor, raddr *net.UDP
|
||||
// Wait client connections.
|
||||
select {
|
||||
case <-session.NotifyCh:
|
||||
resp := nc.GenNatHoleResponse(raddr, session)
|
||||
resp := nc.GenNatHoleResponse(session, "")
|
||||
log.Trace("send nat hole response to visitor")
|
||||
nc.listener.WriteToUDP(resp, raddr)
|
||||
case <-time.After(time.Duration(NatHoleTimeout) * time.Second):
|
||||
@@ -148,16 +160,27 @@ func (nc *NatHoleController) HandleClient(m *msg.NatHoleClient, raddr *net.UDPAd
|
||||
session.ClientAddr = raddr
|
||||
session.NotifyCh <- struct{}{}
|
||||
|
||||
resp := nc.GenNatHoleResponse(raddr, session)
|
||||
resp := nc.GenNatHoleResponse(session, "")
|
||||
log.Trace("send nat hole response to client")
|
||||
nc.listener.WriteToUDP(resp, raddr)
|
||||
}
|
||||
|
||||
func (nc *NatHoleController) GenNatHoleResponse(raddr *net.UDPAddr, session *NatHoleSession) []byte {
|
||||
func (nc *NatHoleController) GenNatHoleResponse(session *NatHoleSession, errInfo string) []byte {
|
||||
var (
|
||||
sid string
|
||||
visitorAddr string
|
||||
clientAddr string
|
||||
)
|
||||
if session != nil {
|
||||
sid = session.Sid
|
||||
visitorAddr = session.VisitorAddr.String()
|
||||
clientAddr = session.ClientAddr.String()
|
||||
}
|
||||
m := &msg.NatHoleResp{
|
||||
Sid: session.Sid,
|
||||
VisitorAddr: session.VisitorAddr.String(),
|
||||
ClientAddr: session.ClientAddr.String(),
|
||||
Sid: sid,
|
||||
VisitorAddr: visitorAddr,
|
||||
ClientAddr: clientAddr,
|
||||
Error: errInfo,
|
||||
}
|
||||
b := bytes.NewBuffer(nil)
|
||||
err := msg.WriteMsg(b, m)
|
||||
|
@@ -23,15 +23,17 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
"github.com/fatedier/frp/models/proto/udp"
|
||||
"github.com/fatedier/frp/utils/errors"
|
||||
frpIo "github.com/fatedier/frp/utils/io"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
"github.com/fatedier/frp/utils/util"
|
||||
"github.com/fatedier/frp/utils/vhost"
|
||||
|
||||
"github.com/fatedier/golib/errors"
|
||||
frpIo "github.com/fatedier/golib/io"
|
||||
)
|
||||
|
||||
type Proxy interface {
|
||||
@@ -40,6 +42,7 @@ type Proxy interface {
|
||||
GetName() string
|
||||
GetConf() config.ProxyConf
|
||||
GetWorkConnFromPool() (workConn frpNet.Conn, err error)
|
||||
GetUsedPortsNum() int
|
||||
Close()
|
||||
log.Logger
|
||||
}
|
||||
@@ -48,6 +51,8 @@ type BaseProxy struct {
|
||||
name string
|
||||
ctl *Control
|
||||
listeners []frpNet.Listener
|
||||
usedPortsNum int
|
||||
|
||||
mu sync.RWMutex
|
||||
log.Logger
|
||||
}
|
||||
@@ -60,6 +65,10 @@ func (pxy *BaseProxy) GetControl() *Control {
|
||||
return pxy.ctl
|
||||
}
|
||||
|
||||
func (pxy *BaseProxy) GetUsedPortsNum() int {
|
||||
return pxy.usedPortsNum
|
||||
}
|
||||
|
||||
func (pxy *BaseProxy) Close() {
|
||||
pxy.Info("proxy closing")
|
||||
for _, l := range pxy.listeners {
|
||||
@@ -119,13 +128,14 @@ func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, frpNet.Con
|
||||
|
||||
func NewProxy(ctl *Control, pxyConf config.ProxyConf) (pxy Proxy, err error) {
|
||||
basePxy := BaseProxy{
|
||||
name: pxyConf.GetName(),
|
||||
name: pxyConf.GetBaseInfo().ProxyName,
|
||||
ctl: ctl,
|
||||
listeners: make([]frpNet.Listener, 0),
|
||||
Logger: log.NewPrefixLogger(ctl.runId),
|
||||
}
|
||||
switch cfg := pxyConf.(type) {
|
||||
case *config.TcpProxyConf:
|
||||
basePxy.usedPortsNum = 1
|
||||
pxy = &TcpProxy{
|
||||
BaseProxy: basePxy,
|
||||
cfg: cfg,
|
||||
@@ -141,6 +151,7 @@ func NewProxy(ctl *Control, pxyConf config.ProxyConf) (pxy Proxy, err error) {
|
||||
cfg: cfg,
|
||||
}
|
||||
case *config.UdpProxyConf:
|
||||
basePxy.usedPortsNum = 1
|
||||
pxy = &UdpProxy{
|
||||
BaseProxy: basePxy,
|
||||
cfg: cfg,
|
||||
@@ -182,7 +193,7 @@ func (pxy *TcpProxy) Run() (remoteAddr string, err error) {
|
||||
|
||||
remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
|
||||
pxy.cfg.RemotePort = pxy.realPort
|
||||
listener, errRet := frpNet.ListenTcp(config.ServerCommonCfg.ProxyBindAddr, pxy.realPort)
|
||||
listener, errRet := frpNet.ListenTcp(g.GlbServerCfg.ProxyBindAddr, pxy.realPort)
|
||||
if errRet != nil {
|
||||
err = errRet
|
||||
return
|
||||
@@ -235,7 +246,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
|
||||
}
|
||||
tmpDomain := routeConfig.Domain
|
||||
tmpLocation := routeConfig.Location
|
||||
addrs = append(addrs, util.CanonicalAddr(tmpDomain, int(config.ServerCommonCfg.VhostHttpPort)))
|
||||
addrs = append(addrs, util.CanonicalAddr(tmpDomain, int(g.GlbServerCfg.VhostHttpPort)))
|
||||
pxy.closeFuncs = append(pxy.closeFuncs, func() {
|
||||
pxy.ctl.svr.httpReverseProxy.UnRegister(tmpDomain, tmpLocation)
|
||||
})
|
||||
@@ -244,7 +255,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
|
||||
}
|
||||
|
||||
if pxy.cfg.SubDomain != "" {
|
||||
routeConfig.Domain = pxy.cfg.SubDomain + "." + config.ServerCommonCfg.SubDomainHost
|
||||
routeConfig.Domain = pxy.cfg.SubDomain + "." + g.GlbServerCfg.SubDomainHost
|
||||
for _, location := range locations {
|
||||
routeConfig.Location = location
|
||||
err = pxy.ctl.svr.httpReverseProxy.Register(routeConfig)
|
||||
@@ -253,7 +264,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
|
||||
}
|
||||
tmpDomain := routeConfig.Domain
|
||||
tmpLocation := routeConfig.Location
|
||||
addrs = append(addrs, util.CanonicalAddr(tmpDomain, int(config.ServerCommonCfg.VhostHttpPort)))
|
||||
addrs = append(addrs, util.CanonicalAddr(tmpDomain, g.GlbServerCfg.VhostHttpPort))
|
||||
pxy.closeFuncs = append(pxy.closeFuncs, func() {
|
||||
pxy.ctl.svr.httpReverseProxy.UnRegister(tmpDomain, tmpLocation)
|
||||
})
|
||||
@@ -277,7 +288,7 @@ func (pxy *HttpProxy) GetRealConn() (workConn frpNet.Conn, err error) {
|
||||
|
||||
var rwc io.ReadWriteCloser = tmpConn
|
||||
if pxy.cfg.UseEncryption {
|
||||
rwc, err = frpIo.WithEncryption(rwc, []byte(config.ServerCommonCfg.PrivilegeToken))
|
||||
rwc, err = frpIo.WithEncryption(rwc, []byte(g.GlbServerCfg.Token))
|
||||
if err != nil {
|
||||
pxy.Error("create encryption stream error: %v", err)
|
||||
return
|
||||
@@ -325,11 +336,11 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
|
||||
l.AddLogPrefix(pxy.name)
|
||||
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
|
||||
pxy.listeners = append(pxy.listeners, l)
|
||||
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(config.ServerCommonCfg.VhostHttpsPort)))
|
||||
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, g.GlbServerCfg.VhostHttpsPort))
|
||||
}
|
||||
|
||||
if pxy.cfg.SubDomain != "" {
|
||||
routeConfig.Domain = pxy.cfg.SubDomain + "." + config.ServerCommonCfg.SubDomainHost
|
||||
routeConfig.Domain = pxy.cfg.SubDomain + "." + g.GlbServerCfg.SubDomainHost
|
||||
l, errRet := pxy.ctl.svr.VhostHttpsMuxer.Listen(routeConfig)
|
||||
if errRet != nil {
|
||||
err = errRet
|
||||
@@ -338,7 +349,7 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
|
||||
l.AddLogPrefix(pxy.name)
|
||||
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
|
||||
pxy.listeners = append(pxy.listeners, l)
|
||||
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(config.ServerCommonCfg.VhostHttpsPort)))
|
||||
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(g.GlbServerCfg.VhostHttpsPort)))
|
||||
}
|
||||
|
||||
pxy.startListenHandler(pxy, HandleUserTcpConnection)
|
||||
@@ -469,7 +480,7 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
|
||||
|
||||
remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
|
||||
pxy.cfg.RemotePort = pxy.realPort
|
||||
addr, errRet := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", config.ServerCommonCfg.ProxyBindAddr, pxy.realPort))
|
||||
addr, errRet := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", g.GlbServerCfg.ProxyBindAddr, pxy.realPort))
|
||||
if errRet != nil {
|
||||
err = errRet
|
||||
return
|
||||
@@ -635,7 +646,7 @@ func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn) {
|
||||
var local io.ReadWriteCloser = workConn
|
||||
cfg := pxy.GetConf().GetBaseInfo()
|
||||
if cfg.UseEncryption {
|
||||
local, err = frpIo.WithEncryption(local, []byte(config.ServerCommonCfg.PrivilegeToken))
|
||||
local, err = frpIo.WithEncryption(local, []byte(g.GlbServerCfg.Token))
|
||||
if err != nil {
|
||||
pxy.Error("create encryption stream error: %v", err)
|
||||
return
|
||||
|
@@ -16,12 +16,13 @@ package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/assets"
|
||||
"github.com/fatedier/frp/models/config"
|
||||
"github.com/fatedier/frp/g"
|
||||
"github.com/fatedier/frp/models/msg"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
@@ -29,7 +30,8 @@ import (
|
||||
"github.com/fatedier/frp/utils/version"
|
||||
"github.com/fatedier/frp/utils/vhost"
|
||||
|
||||
"github.com/xtaci/smux"
|
||||
"github.com/fatedier/golib/net/mux"
|
||||
fmux "github.com/hashicorp/yamux"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -40,6 +42,9 @@ var ServerService *Service
|
||||
|
||||
// Server service.
|
||||
type Service struct {
|
||||
// Dispatch connections to different handlers listen on same port.
|
||||
muxer *mux.Mux
|
||||
|
||||
// Accept connections from client.
|
||||
listener frpNet.Listener
|
||||
|
||||
@@ -71,13 +76,13 @@ type Service struct {
|
||||
}
|
||||
|
||||
func NewService() (svr *Service, err error) {
|
||||
cfg := config.ServerCommonCfg
|
||||
cfg := &g.GlbServerCfg.ServerCommonConf
|
||||
svr = &Service{
|
||||
ctlManager: NewControlManager(),
|
||||
pxyManager: NewProxyManager(),
|
||||
visitorManager: NewVisitorManager(),
|
||||
tcpPortManager: NewPortManager("tcp", cfg.ProxyBindAddr, cfg.PrivilegeAllowPorts),
|
||||
udpPortManager: NewPortManager("udp", cfg.ProxyBindAddr, cfg.PrivilegeAllowPorts),
|
||||
tcpPortManager: NewPortManager("tcp", cfg.ProxyBindAddr, cfg.AllowPorts),
|
||||
udpPortManager: NewPortManager("udp", cfg.ProxyBindAddr, cfg.AllowPorts),
|
||||
}
|
||||
|
||||
// Init assets.
|
||||
@@ -87,12 +92,33 @@ func NewService() (svr *Service, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
httpMuxOn bool
|
||||
httpsMuxOn bool
|
||||
)
|
||||
if cfg.BindAddr == cfg.ProxyBindAddr {
|
||||
if cfg.BindPort == cfg.VhostHttpPort {
|
||||
httpMuxOn = true
|
||||
}
|
||||
if cfg.BindPort == cfg.VhostHttpsPort {
|
||||
httpsMuxOn = true
|
||||
}
|
||||
if httpMuxOn || httpsMuxOn {
|
||||
svr.muxer = mux.NewMux()
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for accepting connections from client.
|
||||
svr.listener, err = frpNet.ListenTcp(cfg.BindAddr, cfg.BindPort)
|
||||
ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", cfg.BindAddr, cfg.BindPort))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Create server listener error, %v", err)
|
||||
return
|
||||
}
|
||||
if svr.muxer != nil {
|
||||
go svr.muxer.Serve(ln)
|
||||
ln = svr.muxer.DefaultListener()
|
||||
}
|
||||
svr.listener = frpNet.WrapLogListener(ln)
|
||||
log.Info("frps tcp listen on %s:%d", cfg.BindAddr, cfg.BindPort)
|
||||
|
||||
// Listen for accepting connections from client using kcp protocol.
|
||||
@@ -116,24 +142,33 @@ func NewService() (svr *Service, err error) {
|
||||
Handler: rp,
|
||||
}
|
||||
var l net.Listener
|
||||
if httpMuxOn {
|
||||
l = svr.muxer.ListenHttp(0)
|
||||
} else {
|
||||
l, err = net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Create vhost http listener error, %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
go server.Serve(l)
|
||||
log.Info("http service listen on %s:%d", cfg.ProxyBindAddr, cfg.VhostHttpPort)
|
||||
}
|
||||
|
||||
// Create https vhost muxer.
|
||||
if cfg.VhostHttpsPort > 0 {
|
||||
var l frpNet.Listener
|
||||
l, err = frpNet.ListenTcp(cfg.ProxyBindAddr, cfg.VhostHttpsPort)
|
||||
var l net.Listener
|
||||
if httpsMuxOn {
|
||||
l = svr.muxer.ListenHttps(0)
|
||||
} else {
|
||||
l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", cfg.ProxyBindAddr, cfg.VhostHttpsPort))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Create vhost https listener error, %v", err)
|
||||
err = fmt.Errorf("Create server listener error, %v", err)
|
||||
return
|
||||
}
|
||||
svr.VhostHttpsMuxer, err = vhost.NewHttpsMuxer(l, 30*time.Second)
|
||||
}
|
||||
|
||||
svr.VhostHttpsMuxer, err = vhost.NewHttpsMuxer(frpNet.WrapLogListener(l), 30*time.Second)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Create vhost httpsMuxer error, %v", err)
|
||||
return
|
||||
@@ -170,7 +205,7 @@ func (svr *Service) Run() {
|
||||
if svr.natHoleController != nil {
|
||||
go svr.natHoleController.Run()
|
||||
}
|
||||
if config.ServerCommonCfg.KcpBindPort > 0 {
|
||||
if g.GlbServerCfg.KcpBindPort > 0 {
|
||||
go svr.HandleListener(svr.kcpListener)
|
||||
}
|
||||
svr.HandleListener(svr.listener)
|
||||
@@ -233,8 +268,10 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
|
||||
}
|
||||
}
|
||||
|
||||
if config.ServerCommonCfg.TcpMux {
|
||||
session, err := smux.Server(frpConn, nil)
|
||||
if g.GlbServerCfg.TcpMux {
|
||||
fmuxCfg := fmux.DefaultConfig()
|
||||
fmuxCfg.LogOutput = ioutil.Discard
|
||||
session, err := fmux.Server(frpConn, fmuxCfg)
|
||||
if err != nil {
|
||||
log.Warn("Failed to create mux connection: %v", err)
|
||||
frpConn.Close()
|
||||
@@ -244,7 +281,7 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
|
||||
for {
|
||||
stream, err := session.AcceptStream()
|
||||
if err != nil {
|
||||
log.Warn("Accept new mux stream error: %v", err)
|
||||
log.Debug("Accept new mux stream error: %v", err)
|
||||
session.Close()
|
||||
return
|
||||
}
|
||||
@@ -270,11 +307,11 @@ func (svr *Service) RegisterControl(ctlConn frpNet.Conn, loginMsg *msg.Login) (e
|
||||
|
||||
// Check auth.
|
||||
nowTime := time.Now().Unix()
|
||||
if config.ServerCommonCfg.AuthTimeout != 0 && nowTime-loginMsg.Timestamp > config.ServerCommonCfg.AuthTimeout {
|
||||
if g.GlbServerCfg.AuthTimeout != 0 && nowTime-loginMsg.Timestamp > g.GlbServerCfg.AuthTimeout {
|
||||
err = fmt.Errorf("authorization timeout")
|
||||
return
|
||||
}
|
||||
if util.GetAuthKey(config.ServerCommonCfg.PrivilegeToken, loginMsg.Timestamp) != loginMsg.PrivilegeKey {
|
||||
if util.GetAuthKey(g.GlbServerCfg.Token, loginMsg.Timestamp) != loginMsg.PrivilegeKey {
|
||||
err = fmt.Errorf("authorization failed")
|
||||
return
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@ server_port = 10700
|
||||
log_file = ./frpc.log
|
||||
# debug, info, warn, error
|
||||
log_level = debug
|
||||
privilege_token = 123456
|
||||
token = 123456
|
||||
admin_port = 10600
|
||||
admin_user = abc
|
||||
admin_pwd = abc
|
||||
@@ -161,3 +161,9 @@ remote_port = 0
|
||||
type = tcp
|
||||
plugin = http_proxy
|
||||
remote_port = 0
|
||||
|
||||
[range:range_tcp]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 30000-30001,30003
|
||||
remote_port = 30000-30001,30003
|
||||
|
@@ -4,7 +4,7 @@ server_port = 10700
|
||||
log_file = ./frpc_visitor.log
|
||||
# debug, info, warn, error
|
||||
log_level = debug
|
||||
privilege_token = 123456
|
||||
token = 123456
|
||||
|
||||
[stcp_visitor]
|
||||
type = stcp
|
||||
|
@@ -4,6 +4,6 @@ bind_port = 10700
|
||||
vhost_http_port = 10804
|
||||
log_file = ./frps.log
|
||||
log_level = debug
|
||||
privilege_token = 123456
|
||||
privilege_allow_ports = 10000-20000,20002,30000-40000
|
||||
token = 123456
|
||||
allow_ports = 10000-20000,20002,30000-50000
|
||||
subdomain_host = sub.com
|
||||
|
@@ -12,7 +12,8 @@ import (
|
||||
|
||||
"github.com/fatedier/frp/client"
|
||||
"github.com/fatedier/frp/server"
|
||||
"github.com/fatedier/frp/utils/net"
|
||||
|
||||
gnet "github.com/fatedier/golib/net"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -53,8 +54,9 @@ var (
|
||||
ProxyUdpPortNotAllowed string = "udp_port_not_allowed"
|
||||
ProxyUdpPortNormal string = "udp_port_normal"
|
||||
ProxyUdpRandomPort string = "udp_random_port"
|
||||
|
||||
ProxyHttpProxy string = "http_proxy"
|
||||
|
||||
ProxyRangeTcpPrefix string = "range_tcp"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -208,7 +210,7 @@ func TestWebSocket(t *testing.T) {
|
||||
assert.Equal(TEST_HTTP_NORMAL_STR, string(msg))
|
||||
}
|
||||
|
||||
func TestPrivilegeAllowPorts(t *testing.T) {
|
||||
func TestAllowPorts(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
// Port not allowed
|
||||
status, err := getProxyStatus(ProxyTcpPortNotAllowed)
|
||||
@@ -278,7 +280,7 @@ func TestPluginHttpProxy(t *testing.T) {
|
||||
}
|
||||
|
||||
// connect method
|
||||
conn, err := net.ConnectTcpServerByHttpProxy("http://"+addr, fmt.Sprintf("127.0.0.1:%d", TEST_TCP_FRP_PORT))
|
||||
conn, err := gnet.DialTcpByProxy("http://"+addr, fmt.Sprintf("127.0.0.1:%d", TEST_TCP_FRP_PORT))
|
||||
if assert.NoError(err) {
|
||||
res, err := sendTcpMsgByConn(conn, TEST_TCP_ECHO_STR)
|
||||
assert.NoError(err)
|
||||
@@ -286,3 +288,15 @@ func TestPluginHttpProxy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRangePortsMapping(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
name := fmt.Sprintf("%s_%d", ProxyRangeTcpPrefix, i)
|
||||
status, err := getProxyStatus(name)
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(client.ProxyStatusRunning, status.Status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,41 +0,0 @@
|
||||
// Copyright 2017 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// 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 crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCrypto(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
text := "1234567890abcdefghigklmnopqrstuvwxyzeeeeeeeeeeeeeeeeeeeeeewwwwwwwwwwwwwwwwwwwwwwwwwwzzzzzzzzzzzzzzzzzzzzzzzzdddddddddddddddddddddddddddddddddddddrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrllllllllllllllllllllllllllllllllllqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeewwwwwwwwwwwwwwwwwwwwww"
|
||||
key := "123456"
|
||||
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
encWriter, err := NewWriter(buffer, []byte(key))
|
||||
assert.NoError(err)
|
||||
decReader := NewReader(buffer, []byte(key))
|
||||
|
||||
encWriter.Write([]byte(text))
|
||||
|
||||
c := bytes.NewBuffer(nil)
|
||||
io.Copy(c, decReader)
|
||||
assert.Equal(text, string(c.Bytes()))
|
||||
}
|
@@ -1,16 +0,0 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPanicToError(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
err := PanicToError(func() {
|
||||
panic("test error")
|
||||
})
|
||||
assert.Contains(err.Error(), "test error")
|
||||
}
|
@@ -1,145 +0,0 @@
|
||||
// Copyright 2017 fatedier, fatedier@gmail.com
|
||||
//
|
||||
// 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 io
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestJoin(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
var (
|
||||
n int
|
||||
err error
|
||||
)
|
||||
text1 := "A document that gives tips for writing clear, idiomatic Go code. A must read for any new Go programmer. It augments the tour and the language specification, both of which should be read first."
|
||||
text2 := "A document that specifies the conditions under which reads of a variable in one goroutine can be guaranteed to observe values produced by writes to the same variable in a different goroutine."
|
||||
|
||||
// Forward bytes directly.
|
||||
pr, pw := io.Pipe()
|
||||
pr2, pw2 := io.Pipe()
|
||||
pr3, pw3 := io.Pipe()
|
||||
pr4, pw4 := io.Pipe()
|
||||
|
||||
conn1 := WrapReadWriteCloser(pr, pw2, nil)
|
||||
conn2 := WrapReadWriteCloser(pr2, pw, nil)
|
||||
conn3 := WrapReadWriteCloser(pr3, pw4, nil)
|
||||
conn4 := WrapReadWriteCloser(pr4, pw3, nil)
|
||||
|
||||
go func() {
|
||||
Join(conn2, conn3)
|
||||
}()
|
||||
|
||||
buf1 := make([]byte, 1024)
|
||||
buf2 := make([]byte, 1024)
|
||||
|
||||
conn1.Write([]byte(text1))
|
||||
conn4.Write([]byte(text2))
|
||||
|
||||
n, err = conn4.Read(buf1)
|
||||
assert.NoError(err)
|
||||
assert.Equal(text1, string(buf1[:n]))
|
||||
|
||||
n, err = conn1.Read(buf2)
|
||||
assert.NoError(err)
|
||||
assert.Equal(text2, string(buf2[:n]))
|
||||
|
||||
conn1.Close()
|
||||
conn2.Close()
|
||||
conn3.Close()
|
||||
conn4.Close()
|
||||
}
|
||||
|
||||
func TestWithCompression(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
// Forward compression bytes.
|
||||
pr, pw := io.Pipe()
|
||||
pr2, pw2 := io.Pipe()
|
||||
|
||||
conn1 := WrapReadWriteCloser(pr, pw2, nil)
|
||||
conn2 := WrapReadWriteCloser(pr2, pw, nil)
|
||||
|
||||
compressionStream1 := WithCompression(conn1)
|
||||
compressionStream2 := WithCompression(conn2)
|
||||
|
||||
var (
|
||||
n int
|
||||
err error
|
||||
)
|
||||
|
||||
text := "1234567812345678"
|
||||
buf := make([]byte, 256)
|
||||
|
||||
go compressionStream1.Write([]byte(text))
|
||||
n, err = compressionStream2.Read(buf)
|
||||
assert.NoError(err)
|
||||
assert.Equal(text, string(buf[:n]))
|
||||
|
||||
go compressionStream2.Write([]byte(text))
|
||||
n, err = compressionStream1.Read(buf)
|
||||
assert.NoError(err)
|
||||
assert.Equal(text, string(buf[:n]))
|
||||
}
|
||||
|
||||
func TestWithEncryption(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
var (
|
||||
n int
|
||||
err error
|
||||
)
|
||||
text1 := "Go is expressive, concise, clean, and efficient. Its concurrency mechanisms make it easy to write programs that get the most out of multicore and networked machines, while its novel type system enables flexible and modular program construction. Go compiles quickly to machine code yet has the convenience of garbage collection and the power of run-time reflection. It's a fast, statically typed, compiled language that feels like a dynamically typed, interpreted language."
|
||||
text2 := "An interactive introduction to Go in three sections. The first section covers basic syntax and data structures; the second discusses methods and interfaces; and the third introduces Go's concurrency primitives. Each section concludes with a few exercises so you can practice what you've learned. You can take the tour online or install it locally with"
|
||||
key := "authkey"
|
||||
|
||||
// Forward enrypted bytes.
|
||||
pr, pw := io.Pipe()
|
||||
pr2, pw2 := io.Pipe()
|
||||
pr3, pw3 := io.Pipe()
|
||||
pr4, pw4 := io.Pipe()
|
||||
pr5, pw5 := io.Pipe()
|
||||
pr6, pw6 := io.Pipe()
|
||||
|
||||
conn1 := WrapReadWriteCloser(pr, pw2, nil)
|
||||
conn2 := WrapReadWriteCloser(pr2, pw, nil)
|
||||
conn3 := WrapReadWriteCloser(pr3, pw4, nil)
|
||||
conn4 := WrapReadWriteCloser(pr4, pw3, nil)
|
||||
conn5 := WrapReadWriteCloser(pr5, pw6, nil)
|
||||
conn6 := WrapReadWriteCloser(pr6, pw5, nil)
|
||||
|
||||
encryptStream1, err := WithEncryption(conn3, []byte(key))
|
||||
assert.NoError(err)
|
||||
encryptStream2, err := WithEncryption(conn4, []byte(key))
|
||||
assert.NoError(err)
|
||||
|
||||
go Join(conn2, encryptStream1)
|
||||
go Join(encryptStream2, conn5)
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
|
||||
conn1.Write([]byte(text1))
|
||||
conn6.Write([]byte(text2))
|
||||
|
||||
n, err = conn6.Read(buf)
|
||||
assert.NoError(err)
|
||||
assert.Equal(text1, string(buf[:n]))
|
||||
|
||||
n, err = conn1.Read(buf)
|
||||
assert.NoError(err)
|
||||
}
|
@@ -15,17 +15,17 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
|
||||
kcp "github.com/xtaci/kcp-go"
|
||||
gnet "github.com/fatedier/golib/net"
|
||||
kcp "github.com/fatedier/kcp-go"
|
||||
)
|
||||
|
||||
// Conn is the interface of connections used in frp.
|
||||
@@ -121,10 +121,14 @@ func ConnectServer(protocol string, addr string) (c Conn, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func ConnectServerByHttpProxy(httpProxy string, protocol string, addr string) (c Conn, err error) {
|
||||
func ConnectServerByProxy(proxyUrl string, protocol string, addr string) (c Conn, err error) {
|
||||
switch protocol {
|
||||
case "tcp":
|
||||
return ConnectTcpServerByHttpProxy(httpProxy, addr)
|
||||
var conn net.Conn
|
||||
if conn, err = gnet.DialTcpByProxy(proxyUrl, addr); err != nil {
|
||||
return
|
||||
}
|
||||
return WrapConn(conn), nil
|
||||
case "kcp":
|
||||
// http proxy is not supported for kcp
|
||||
return ConnectServer(protocol, addr)
|
||||
@@ -133,51 +137,10 @@ func ConnectServerByHttpProxy(httpProxy string, protocol string, addr string) (c
|
||||
}
|
||||
}
|
||||
|
||||
type SharedConn struct {
|
||||
Conn
|
||||
sync.Mutex
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
// the bytes you read in io.Reader, will be reserved in SharedConn
|
||||
func NewShareConn(conn Conn) (*SharedConn, io.Reader) {
|
||||
sc := &SharedConn{
|
||||
Conn: conn,
|
||||
buf: bytes.NewBuffer(make([]byte, 0, 1024)),
|
||||
}
|
||||
return sc, io.TeeReader(conn, sc.buf)
|
||||
}
|
||||
|
||||
func (sc *SharedConn) Read(p []byte) (n int, err error) {
|
||||
sc.Lock()
|
||||
if sc.buf == nil {
|
||||
sc.Unlock()
|
||||
return sc.Conn.Read(p)
|
||||
}
|
||||
sc.Unlock()
|
||||
n, err = sc.buf.Read(p)
|
||||
|
||||
if err == io.EOF {
|
||||
sc.Lock()
|
||||
sc.buf = nil
|
||||
sc.Unlock()
|
||||
var n2 int
|
||||
n2, err = sc.Conn.Read(p[n:])
|
||||
|
||||
n += n2
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (sc *SharedConn) WriteBuff(buffer []byte) (err error) {
|
||||
sc.buf.Reset()
|
||||
_, err = sc.buf.Write(buffer)
|
||||
return err
|
||||
}
|
||||
|
||||
type StatsConn struct {
|
||||
Conn
|
||||
|
||||
closed int64 // 1 means closed
|
||||
totalRead int64
|
||||
totalWrite int64
|
||||
statsFunc func(totalRead, totalWrite int64)
|
||||
@@ -203,9 +166,12 @@ func (statsConn *StatsConn) Write(p []byte) (n int, err error) {
|
||||
}
|
||||
|
||||
func (statsConn *StatsConn) Close() (err error) {
|
||||
old := atomic.SwapInt64(&statsConn.closed, 1)
|
||||
if old != 1 {
|
||||
err = statsConn.Conn.Close()
|
||||
if statsConn.statsFunc != nil {
|
||||
statsConn.statsFunc(statsConn.totalRead, statsConn.totalWrite)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@@ -19,8 +19,9 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/fatedier/frp/utils/errors"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
|
||||
"github.com/fatedier/golib/errors"
|
||||
)
|
||||
|
||||
type Listener interface {
|
||||
|
@@ -15,12 +15,8 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
)
|
||||
@@ -93,7 +89,7 @@ type TcpConn struct {
|
||||
log.Logger
|
||||
}
|
||||
|
||||
func NewTcpConn(conn *net.TCPConn) (c *TcpConn) {
|
||||
func NewTcpConn(conn net.Conn) (c *TcpConn) {
|
||||
c = &TcpConn{
|
||||
Conn: conn,
|
||||
Logger: log.NewPrefixLogger(""),
|
||||
@@ -113,54 +109,3 @@ func ConnectTcpServer(addr string) (c Conn, err error) {
|
||||
c = NewTcpConn(conn)
|
||||
return
|
||||
}
|
||||
|
||||
// ConnectTcpServerByHttpProxy try to connect remote server by http proxy.
|
||||
// If httpProxy is empty, it will connect server directly.
|
||||
func ConnectTcpServerByHttpProxy(httpProxy string, serverAddr string) (c Conn, err error) {
|
||||
if httpProxy == "" {
|
||||
return ConnectTcpServer(serverAddr)
|
||||
}
|
||||
|
||||
var proxyUrl *url.URL
|
||||
if proxyUrl, err = url.Parse(httpProxy); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var proxyAuth string
|
||||
if proxyUrl.User != nil {
|
||||
username := proxyUrl.User.Username()
|
||||
passwd, _ := proxyUrl.User.Password()
|
||||
proxyAuth = "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+passwd))
|
||||
}
|
||||
|
||||
if proxyUrl.Scheme != "http" {
|
||||
err = fmt.Errorf("Proxy URL scheme must be http, not [%s]", proxyUrl.Scheme)
|
||||
return
|
||||
}
|
||||
|
||||
if c, err = ConnectTcpServer(proxyUrl.Host); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
req, err := http.NewRequest("CONNECT", "http://"+serverAddr, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if proxyAuth != "" {
|
||||
req.Header.Set("Proxy-Authorization", proxyAuth)
|
||||
}
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0")
|
||||
req.Write(c)
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(c), req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
err = fmt.Errorf("ConnectTcpServer using proxy error, StatusCode [%d]", resp.StatusCode)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
@@ -22,7 +22,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
"github.com/fatedier/frp/utils/pool"
|
||||
|
||||
"github.com/fatedier/golib/pool"
|
||||
)
|
||||
|
||||
type UdpPacket struct {
|
||||
|
@@ -1,21 +0,0 @@
|
||||
package shutdown
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestShutdown(t *testing.T) {
|
||||
s := New()
|
||||
go func() {
|
||||
time.Sleep(time.Millisecond)
|
||||
s.Start()
|
||||
}()
|
||||
s.WaitStart()
|
||||
|
||||
go func() {
|
||||
time.Sleep(time.Millisecond)
|
||||
s.Done()
|
||||
}()
|
||||
s.WaitDone()
|
||||
}
|
@@ -19,6 +19,8 @@ import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RandId return a rand string used in frp.
|
||||
@@ -54,3 +56,48 @@ func CanonicalAddr(host string, port int) (addr string) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ParseRangeNumbers(rangeStr string) (numbers []int64, err error) {
|
||||
rangeStr = strings.TrimSpace(rangeStr)
|
||||
numbers = make([]int64, 0)
|
||||
// e.g. 1000-2000,2001,2002,3000-4000
|
||||
numRanges := strings.Split(rangeStr, ",")
|
||||
for _, numRangeStr := range numRanges {
|
||||
// 1000-2000 or 2001
|
||||
numArray := strings.Split(numRangeStr, "-")
|
||||
// length: only 1 or 2 is correct
|
||||
rangeType := len(numArray)
|
||||
if rangeType == 1 {
|
||||
// single number
|
||||
singleNum, errRet := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("range number is invalid, %v", errRet)
|
||||
return
|
||||
}
|
||||
numbers = append(numbers, singleNum)
|
||||
} else if rangeType == 2 {
|
||||
// range numbers
|
||||
min, errRet := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("range number is invalid, %v", errRet)
|
||||
return
|
||||
}
|
||||
max, errRet := strconv.ParseInt(strings.TrimSpace(numArray[1]), 10, 64)
|
||||
if errRet != nil {
|
||||
err = fmt.Errorf("range number is invalid, %v", errRet)
|
||||
return
|
||||
}
|
||||
if max < min {
|
||||
err = fmt.Errorf("range number is invalid")
|
||||
return
|
||||
}
|
||||
for i := min; i <= max; i++ {
|
||||
numbers = append(numbers, i)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("range number is invalid")
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@@ -20,3 +20,29 @@ func TestGetAuthKey(t *testing.T) {
|
||||
t.Log(key)
|
||||
assert.Equal("6df41a43725f0c770fd56379e12acf8c", key)
|
||||
}
|
||||
|
||||
func TestParseRangeNumbers(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
numbers, err := ParseRangeNumbers("2-5")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal([]int64{2, 3, 4, 5}, numbers)
|
||||
}
|
||||
|
||||
numbers, err = ParseRangeNumbers("1")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal([]int64{1}, numbers)
|
||||
}
|
||||
|
||||
numbers, err = ParseRangeNumbers("3-5,8")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal([]int64{3, 4, 5, 8}, numbers)
|
||||
}
|
||||
|
||||
numbers, err = ParseRangeNumbers(" 3-5,8, 10-12 ")
|
||||
if assert.NoError(err) {
|
||||
assert.Equal([]int64{3, 4, 5, 8, 10, 11, 12}, numbers)
|
||||
}
|
||||
|
||||
_, err = ParseRangeNumbers("3-a")
|
||||
assert.Error(err)
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var version string = "0.15.1"
|
||||
var version string = "0.19.1"
|
||||
|
||||
func Full() string {
|
||||
return version
|
||||
@@ -48,8 +48,8 @@ func Minor(v string) int64 {
|
||||
|
||||
// add every case there if server will not accept client's protocol and return false
|
||||
func Compat(client string) (ok bool, msg string) {
|
||||
if LessThan(client, "0.10.0") {
|
||||
return false, "Please upgrade your frpc version to at least 0.10.0"
|
||||
if LessThan(client, "0.18.0") {
|
||||
return false, "Please upgrade your frpc version to at least 0.18.0"
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
@@ -61,5 +61,5 @@ func TestCompact(t *testing.T) {
|
||||
assert.True(ok)
|
||||
|
||||
ok, _ = Compat("0.10.0")
|
||||
assert.True(ok)
|
||||
assert.False(ok)
|
||||
}
|
||||
|
@@ -26,7 +26,9 @@ import (
|
||||
"time"
|
||||
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
"github.com/fatedier/frp/utils/pool"
|
||||
|
||||
gnet "github.com/fatedier/golib/net"
|
||||
"github.com/fatedier/golib/pool"
|
||||
)
|
||||
|
||||
type HttpMuxer struct {
|
||||
@@ -35,11 +37,11 @@ type HttpMuxer struct {
|
||||
|
||||
func GetHttpRequestInfo(c frpNet.Conn) (_ frpNet.Conn, _ map[string]string, err error) {
|
||||
reqInfoMap := make(map[string]string, 0)
|
||||
sc, rd := frpNet.NewShareConn(c)
|
||||
sc, rd := gnet.NewSharedConn(c)
|
||||
|
||||
request, err := http.ReadRequest(bufio.NewReader(rd))
|
||||
if err != nil {
|
||||
return sc, reqInfoMap, err
|
||||
return nil, reqInfoMap, err
|
||||
}
|
||||
// hostName
|
||||
tmpArr := strings.Split(request.Host, ":")
|
||||
@@ -53,7 +55,7 @@ func GetHttpRequestInfo(c frpNet.Conn) (_ frpNet.Conn, _ map[string]string, err
|
||||
reqInfoMap["Authorization"] = authStr
|
||||
}
|
||||
request.Body.Close()
|
||||
return sc, reqInfoMap, nil
|
||||
return frpNet.WrapConn(sc), reqInfoMap, nil
|
||||
}
|
||||
|
||||
func NewHttpMuxer(listener frpNet.Listener, timeout time.Duration) (*HttpMuxer, error) {
|
||||
@@ -62,14 +64,14 @@ func NewHttpMuxer(listener frpNet.Listener, timeout time.Duration) (*HttpMuxer,
|
||||
}
|
||||
|
||||
func ModifyHttpRequest(c frpNet.Conn, rewriteHost string) (_ frpNet.Conn, err error) {
|
||||
sc, rd := frpNet.NewShareConn(c)
|
||||
sc, rd := gnet.NewSharedConn(c)
|
||||
var buff []byte
|
||||
remoteIP := strings.Split(c.RemoteAddr().String(), ":")[0]
|
||||
if buff, err = hostNameRewrite(rd, rewriteHost, remoteIP); err != nil {
|
||||
return sc, err
|
||||
return nil, err
|
||||
}
|
||||
err = sc.WriteBuff(buff)
|
||||
return sc, err
|
||||
err = sc.ResetBuf(buff)
|
||||
return frpNet.WrapConn(sc), err
|
||||
}
|
||||
|
||||
func hostNameRewrite(request io.Reader, rewriteHost string, remoteIP string) (_ []byte, err error) {
|
||||
|
@@ -21,7 +21,9 @@ import (
|
||||
"time"
|
||||
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
"github.com/fatedier/frp/utils/pool"
|
||||
|
||||
gnet "github.com/fatedier/golib/net"
|
||||
"github.com/fatedier/golib/pool"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -55,14 +57,17 @@ func readHandshake(rd io.Reader) (host string, err error) {
|
||||
data := pool.GetBuf(1024)
|
||||
origin := data
|
||||
defer pool.PutBuf(origin)
|
||||
length, err := rd.Read(data)
|
||||
|
||||
_, err = io.ReadFull(rd, data[:47])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
length, err := rd.Read(data[47:])
|
||||
if err != nil {
|
||||
return
|
||||
} else {
|
||||
if length < 47 {
|
||||
err = fmt.Errorf("readHandshake: proto length[%d] is too short", length)
|
||||
return
|
||||
}
|
||||
length += 47
|
||||
}
|
||||
data = data[:length]
|
||||
if uint8(data[5]) != typeClientHello {
|
||||
@@ -108,7 +113,7 @@ func readHandshake(rd io.Reader) (host string, err error) {
|
||||
return
|
||||
}
|
||||
if len(data) < 2 {
|
||||
err = fmt.Errorf("readHandshake: extension dataLen[%d] is too short")
|
||||
err = fmt.Errorf("readHandshake: extension dataLen[%d] is too short", len(data))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -177,14 +182,14 @@ func readHandshake(rd io.Reader) (host string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func GetHttpsHostname(c frpNet.Conn) (sc frpNet.Conn, _ map[string]string, err error) {
|
||||
func GetHttpsHostname(c frpNet.Conn) (_ frpNet.Conn, _ map[string]string, err error) {
|
||||
reqInfoMap := make(map[string]string, 0)
|
||||
sc, rd := frpNet.NewShareConn(c)
|
||||
sc, rd := gnet.NewSharedConn(c)
|
||||
host, err := readHandshake(rd)
|
||||
if err != nil {
|
||||
return sc, reqInfoMap, err
|
||||
return nil, reqInfoMap, err
|
||||
}
|
||||
reqInfoMap["Host"] = host
|
||||
reqInfoMap["Scheme"] = "https"
|
||||
return sc, reqInfoMap, nil
|
||||
return frpNet.WrapConn(sc), reqInfoMap, nil
|
||||
}
|
||||
|
@@ -26,7 +26,8 @@ import (
|
||||
"time"
|
||||
|
||||
frpLog "github.com/fatedier/frp/utils/log"
|
||||
"github.com/fatedier/frp/utils/pool"
|
||||
|
||||
"github.com/fatedier/golib/pool"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -48,7 +49,6 @@ func getHostFromAddr(addr string) (host string) {
|
||||
|
||||
type HttpReverseProxy struct {
|
||||
proxy *ReverseProxy
|
||||
tr *http.Transport
|
||||
|
||||
vhostRouter *VhostRouters
|
||||
|
||||
|
@@ -17,7 +17,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
frpIo "github.com/fatedier/frp/utils/io"
|
||||
frpIo "github.com/fatedier/golib/io"
|
||||
)
|
||||
|
||||
// onExitFlushLoop is a callback set by tests to detect the state of the
|
||||
|
@@ -18,9 +18,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatedier/frp/utils/errors"
|
||||
"github.com/fatedier/frp/utils/log"
|
||||
frpNet "github.com/fatedier/frp/utils/net"
|
||||
|
||||
"github.com/fatedier/golib/errors"
|
||||
)
|
||||
|
||||
type muxFunc func(frpNet.Conn) (frpNet.Conn, map[string]string, error)
|
||||
|
119
vendor/github.com/armon/go-socks5/auth_test.go
generated
vendored
119
vendor/github.com/armon/go-socks5/auth_test.go
generated
vendored
@@ -1,119 +0,0 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNoAuth(t *testing.T) {
|
||||
req := bytes.NewBuffer(nil)
|
||||
req.Write([]byte{1, NoAuth})
|
||||
var resp bytes.Buffer
|
||||
|
||||
s, _ := New(&Config{})
|
||||
ctx, err := s.authenticate(&resp, req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if ctx.Method != NoAuth {
|
||||
t.Fatal("Invalid Context Method")
|
||||
}
|
||||
|
||||
out := resp.Bytes()
|
||||
if !bytes.Equal(out, []byte{socks5Version, NoAuth}) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPasswordAuth_Valid(t *testing.T) {
|
||||
req := bytes.NewBuffer(nil)
|
||||
req.Write([]byte{2, NoAuth, UserPassAuth})
|
||||
req.Write([]byte{1, 3, 'f', 'o', 'o', 3, 'b', 'a', 'r'})
|
||||
var resp bytes.Buffer
|
||||
|
||||
cred := StaticCredentials{
|
||||
"foo": "bar",
|
||||
}
|
||||
|
||||
cator := UserPassAuthenticator{Credentials: cred}
|
||||
|
||||
s, _ := New(&Config{AuthMethods: []Authenticator{cator}})
|
||||
|
||||
ctx, err := s.authenticate(&resp, req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if ctx.Method != UserPassAuth {
|
||||
t.Fatal("Invalid Context Method")
|
||||
}
|
||||
|
||||
val, ok := ctx.Payload["Username"]
|
||||
if !ok {
|
||||
t.Fatal("Missing key Username in auth context's payload")
|
||||
}
|
||||
|
||||
if val != "foo" {
|
||||
t.Fatal("Invalid Username in auth context's payload")
|
||||
}
|
||||
|
||||
out := resp.Bytes()
|
||||
if !bytes.Equal(out, []byte{socks5Version, UserPassAuth, 1, authSuccess}) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPasswordAuth_Invalid(t *testing.T) {
|
||||
req := bytes.NewBuffer(nil)
|
||||
req.Write([]byte{2, NoAuth, UserPassAuth})
|
||||
req.Write([]byte{1, 3, 'f', 'o', 'o', 3, 'b', 'a', 'z'})
|
||||
var resp bytes.Buffer
|
||||
|
||||
cred := StaticCredentials{
|
||||
"foo": "bar",
|
||||
}
|
||||
cator := UserPassAuthenticator{Credentials: cred}
|
||||
s, _ := New(&Config{AuthMethods: []Authenticator{cator}})
|
||||
|
||||
ctx, err := s.authenticate(&resp, req)
|
||||
if err != UserAuthFailed {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if ctx != nil {
|
||||
t.Fatal("Invalid Context Method")
|
||||
}
|
||||
|
||||
out := resp.Bytes()
|
||||
if !bytes.Equal(out, []byte{socks5Version, UserPassAuth, 1, authFailure}) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoSupportedAuth(t *testing.T) {
|
||||
req := bytes.NewBuffer(nil)
|
||||
req.Write([]byte{1, NoAuth})
|
||||
var resp bytes.Buffer
|
||||
|
||||
cred := StaticCredentials{
|
||||
"foo": "bar",
|
||||
}
|
||||
cator := UserPassAuthenticator{Credentials: cred}
|
||||
|
||||
s, _ := New(&Config{AuthMethods: []Authenticator{cator}})
|
||||
|
||||
ctx, err := s.authenticate(&resp, req)
|
||||
if err != NoSupportedAuth {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if ctx != nil {
|
||||
t.Fatal("Invalid Context Method")
|
||||
}
|
||||
|
||||
out := resp.Bytes()
|
||||
if !bytes.Equal(out, []byte{socks5Version, noAcceptable}) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
24
vendor/github.com/armon/go-socks5/credentials_test.go
generated
vendored
24
vendor/github.com/armon/go-socks5/credentials_test.go
generated
vendored
@@ -1,24 +0,0 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStaticCredentials(t *testing.T) {
|
||||
creds := StaticCredentials{
|
||||
"foo": "bar",
|
||||
"baz": "",
|
||||
}
|
||||
|
||||
if !creds.Valid("foo", "bar") {
|
||||
t.Fatalf("expect valid")
|
||||
}
|
||||
|
||||
if !creds.Valid("baz", "") {
|
||||
t.Fatalf("expect valid")
|
||||
}
|
||||
|
||||
if creds.Valid("foo", "") {
|
||||
t.Fatalf("expect invalid")
|
||||
}
|
||||
}
|
169
vendor/github.com/armon/go-socks5/request_test.go
generated
vendored
169
vendor/github.com/armon/go-socks5/request_test.go
generated
vendored
@@ -1,169 +0,0 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type MockConn struct {
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
func (m *MockConn) Write(b []byte) (int, error) {
|
||||
return m.buf.Write(b)
|
||||
}
|
||||
|
||||
func (m *MockConn) RemoteAddr() net.Addr {
|
||||
return &net.TCPAddr{IP: []byte{127, 0, 0, 1}, Port: 65432}
|
||||
}
|
||||
|
||||
func TestRequest_Connect(t *testing.T) {
|
||||
// Create a local listener
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
go func() {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadAtLeast(conn, buf, 4); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf, []byte("ping")) {
|
||||
t.Fatalf("bad: %v", buf)
|
||||
}
|
||||
conn.Write([]byte("pong"))
|
||||
}()
|
||||
lAddr := l.Addr().(*net.TCPAddr)
|
||||
|
||||
// Make server
|
||||
s := &Server{config: &Config{
|
||||
Rules: PermitAll(),
|
||||
Resolver: DNSResolver{},
|
||||
Logger: log.New(os.Stdout, "", log.LstdFlags),
|
||||
}}
|
||||
|
||||
// Create the connect request
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.Write([]byte{5, 1, 0, 1, 127, 0, 0, 1})
|
||||
|
||||
port := []byte{0, 0}
|
||||
binary.BigEndian.PutUint16(port, uint16(lAddr.Port))
|
||||
buf.Write(port)
|
||||
|
||||
// Send a ping
|
||||
buf.Write([]byte("ping"))
|
||||
|
||||
// Handle the request
|
||||
resp := &MockConn{}
|
||||
req, err := NewRequest(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if err := s.handleRequest(req, resp); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Verify response
|
||||
out := resp.buf.Bytes()
|
||||
expected := []byte{
|
||||
5,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
127, 0, 0, 1,
|
||||
0, 0,
|
||||
'p', 'o', 'n', 'g',
|
||||
}
|
||||
|
||||
// Ignore the port for both
|
||||
out[8] = 0
|
||||
out[9] = 0
|
||||
|
||||
if !bytes.Equal(out, expected) {
|
||||
t.Fatalf("bad: %v %v", out, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRequest_Connect_RuleFail(t *testing.T) {
|
||||
// Create a local listener
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
go func() {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadAtLeast(conn, buf, 4); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf, []byte("ping")) {
|
||||
t.Fatalf("bad: %v", buf)
|
||||
}
|
||||
conn.Write([]byte("pong"))
|
||||
}()
|
||||
lAddr := l.Addr().(*net.TCPAddr)
|
||||
|
||||
// Make server
|
||||
s := &Server{config: &Config{
|
||||
Rules: PermitNone(),
|
||||
Resolver: DNSResolver{},
|
||||
Logger: log.New(os.Stdout, "", log.LstdFlags),
|
||||
}}
|
||||
|
||||
// Create the connect request
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.Write([]byte{5, 1, 0, 1, 127, 0, 0, 1})
|
||||
|
||||
port := []byte{0, 0}
|
||||
binary.BigEndian.PutUint16(port, uint16(lAddr.Port))
|
||||
buf.Write(port)
|
||||
|
||||
// Send a ping
|
||||
buf.Write([]byte("ping"))
|
||||
|
||||
// Handle the request
|
||||
resp := &MockConn{}
|
||||
req, err := NewRequest(buf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if err := s.handleRequest(req, resp); !strings.Contains(err.Error(), "blocked by rules") {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Verify response
|
||||
out := resp.buf.Bytes()
|
||||
expected := []byte{
|
||||
5,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0, 0, 0, 0,
|
||||
0, 0,
|
||||
}
|
||||
|
||||
if !bytes.Equal(out, expected) {
|
||||
t.Fatalf("bad: %v %v", out, expected)
|
||||
}
|
||||
}
|
21
vendor/github.com/armon/go-socks5/resolver_test.go
generated
vendored
21
vendor/github.com/armon/go-socks5/resolver_test.go
generated
vendored
@@ -1,21 +0,0 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestDNSResolver(t *testing.T) {
|
||||
d := DNSResolver{}
|
||||
ctx := context.Background()
|
||||
|
||||
_, addr, err := d.Resolve(ctx, "localhost")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !addr.IsLoopback() {
|
||||
t.Fatalf("expected loopback")
|
||||
}
|
||||
}
|
24
vendor/github.com/armon/go-socks5/ruleset_test.go
generated
vendored
24
vendor/github.com/armon/go-socks5/ruleset_test.go
generated
vendored
@@ -1,24 +0,0 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestPermitCommand(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
r := &PermitCommand{true, false, false}
|
||||
|
||||
if _, ok := r.Allow(ctx, &Request{Command: ConnectCommand}); !ok {
|
||||
t.Fatalf("expect connect")
|
||||
}
|
||||
|
||||
if _, ok := r.Allow(ctx, &Request{Command: BindCommand}); ok {
|
||||
t.Fatalf("do not expect bind")
|
||||
}
|
||||
|
||||
if _, ok := r.Allow(ctx, &Request{Command: AssociateCommand}); ok {
|
||||
t.Fatalf("do not expect associate")
|
||||
}
|
||||
}
|
110
vendor/github.com/armon/go-socks5/socks5_test.go
generated
vendored
110
vendor/github.com/armon/go-socks5/socks5_test.go
generated
vendored
@@ -1,110 +0,0 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSOCKS5_Connect(t *testing.T) {
|
||||
// Create a local listener
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
go func() {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
buf := make([]byte, 4)
|
||||
if _, err := io.ReadAtLeast(conn, buf, 4); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(buf, []byte("ping")) {
|
||||
t.Fatalf("bad: %v", buf)
|
||||
}
|
||||
conn.Write([]byte("pong"))
|
||||
}()
|
||||
lAddr := l.Addr().(*net.TCPAddr)
|
||||
|
||||
// Create a socks server
|
||||
creds := StaticCredentials{
|
||||
"foo": "bar",
|
||||
}
|
||||
cator := UserPassAuthenticator{Credentials: creds}
|
||||
conf := &Config{
|
||||
AuthMethods: []Authenticator{cator},
|
||||
Logger: log.New(os.Stdout, "", log.LstdFlags),
|
||||
}
|
||||
serv, err := New(conf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Start listening
|
||||
go func() {
|
||||
if err := serv.ListenAndServe("tcp", "127.0.0.1:12365"); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// Get a local conn
|
||||
conn, err := net.Dial("tcp", "127.0.0.1:12365")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Connect, auth and connec to local
|
||||
req := bytes.NewBuffer(nil)
|
||||
req.Write([]byte{5})
|
||||
req.Write([]byte{2, NoAuth, UserPassAuth})
|
||||
req.Write([]byte{1, 3, 'f', 'o', 'o', 3, 'b', 'a', 'r'})
|
||||
req.Write([]byte{5, 1, 0, 1, 127, 0, 0, 1})
|
||||
|
||||
port := []byte{0, 0}
|
||||
binary.BigEndian.PutUint16(port, uint16(lAddr.Port))
|
||||
req.Write(port)
|
||||
|
||||
// Send a ping
|
||||
req.Write([]byte("ping"))
|
||||
|
||||
// Send all the bytes
|
||||
conn.Write(req.Bytes())
|
||||
|
||||
// Verify response
|
||||
expected := []byte{
|
||||
socks5Version, UserPassAuth,
|
||||
1, authSuccess,
|
||||
5,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
127, 0, 0, 1,
|
||||
0, 0,
|
||||
'p', 'o', 'n', 'g',
|
||||
}
|
||||
out := make([]byte, len(expected))
|
||||
|
||||
conn.SetDeadline(time.Now().Add(time.Second))
|
||||
if _, err := io.ReadAtLeast(conn, out, len(out)); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Ignore the port
|
||||
out[12] = 0
|
||||
out[13] = 0
|
||||
|
||||
if !bytes.Equal(out, expected) {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
22
vendor/github.com/davecgh/go-spew/.gitignore
generated
vendored
22
vendor/github.com/davecgh/go-spew/.gitignore
generated
vendored
@@ -1,22 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
14
vendor/github.com/davecgh/go-spew/.travis.yml
generated
vendored
14
vendor/github.com/davecgh/go-spew/.travis.yml
generated
vendored
@@ -1,14 +0,0 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.5.4
|
||||
- 1.6.3
|
||||
- 1.7
|
||||
install:
|
||||
- go get -v golang.org/x/tools/cmd/cover
|
||||
script:
|
||||
- go test -v -tags=safe ./spew
|
||||
- go test -v -tags=testcgo ./spew -covermode=count -coverprofile=profile.cov
|
||||
after_success:
|
||||
- go get -v github.com/mattn/goveralls
|
||||
- export PATH=$PATH:$HOME/gopath/bin
|
||||
- goveralls -coverprofile=profile.cov -service=travis-ci
|
205
vendor/github.com/davecgh/go-spew/README.md
generated
vendored
205
vendor/github.com/davecgh/go-spew/README.md
generated
vendored
@@ -1,205 +0,0 @@
|
||||
go-spew
|
||||
=======
|
||||
|
||||
[]
|
||||
(https://travis-ci.org/davecgh/go-spew) [![ISC License]
|
||||
(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) [![Coverage Status]
|
||||
(https://img.shields.io/coveralls/davecgh/go-spew.svg)]
|
||||
(https://coveralls.io/r/davecgh/go-spew?branch=master)
|
||||
|
||||
|
||||
Go-spew implements a deep pretty printer for Go data structures to aid in
|
||||
debugging. A comprehensive suite of tests with 100% test coverage is provided
|
||||
to ensure proper functionality. See `test_coverage.txt` for the gocov coverage
|
||||
report. Go-spew is licensed under the liberal ISC license, so it may be used in
|
||||
open source or commercial projects.
|
||||
|
||||
If you're interested in reading about how this package came to life and some
|
||||
of the challenges involved in providing a deep pretty printer, there is a blog
|
||||
post about it
|
||||
[here](https://web.archive.org/web/20160304013555/https://blog.cyphertite.com/go-spew-a-journey-into-dumping-go-data-structures/).
|
||||
|
||||
## Documentation
|
||||
|
||||
[]
|
||||
(http://godoc.org/github.com/davecgh/go-spew/spew)
|
||||
|
||||
Full `go doc` style documentation for the project can be viewed online without
|
||||
installing this package by using the excellent GoDoc site here:
|
||||
http://godoc.org/github.com/davecgh/go-spew/spew
|
||||
|
||||
You can also view the documentation locally once the package is installed with
|
||||
the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
|
||||
http://localhost:6060/pkg/github.com/davecgh/go-spew/spew
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ go get -u github.com/davecgh/go-spew/spew
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
Add this import line to the file you're working in:
|
||||
|
||||
```Go
|
||||
import "github.com/davecgh/go-spew/spew"
|
||||
```
|
||||
|
||||
To dump a variable with full newlines, indentation, type, and pointer
|
||||
information use Dump, Fdump, or Sdump:
|
||||
|
||||
```Go
|
||||
spew.Dump(myVar1, myVar2, ...)
|
||||
spew.Fdump(someWriter, myVar1, myVar2, ...)
|
||||
str := spew.Sdump(myVar1, myVar2, ...)
|
||||
```
|
||||
|
||||
Alternatively, if you would prefer to use format strings with a compacted inline
|
||||
printing style, use the convenience wrappers Printf, Fprintf, etc with %v (most
|
||||
compact), %+v (adds pointer addresses), %#v (adds types), or %#+v (adds types
|
||||
and pointer addresses):
|
||||
|
||||
```Go
|
||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
```
|
||||
|
||||
## Debugging a Web Application Example
|
||||
|
||||
Here is an example of how you can use `spew.Sdump()` to help debug a web application. Please be sure to wrap your output using the `html.EscapeString()` function for safety reasons. You should also only use this debugging technique in a development environment, never in production.
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"html"
|
||||
"net/http"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
fmt.Fprintf(w, "Hi there, %s!", r.URL.Path[1:])
|
||||
fmt.Fprintf(w, "<!--\n" + html.EscapeString(spew.Sdump(w)) + "\n-->")
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", handler)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
```
|
||||
|
||||
## Sample Dump Output
|
||||
|
||||
```
|
||||
(main.Foo) {
|
||||
unexportedField: (*main.Bar)(0xf84002e210)({
|
||||
flag: (main.Flag) flagTwo,
|
||||
data: (uintptr) <nil>
|
||||
}),
|
||||
ExportedField: (map[interface {}]interface {}) {
|
||||
(string) "one": (bool) true
|
||||
}
|
||||
}
|
||||
([]uint8) {
|
||||
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
||||
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
||||
00000020 31 32 |12|
|
||||
}
|
||||
```
|
||||
|
||||
## Sample Formatter Output
|
||||
|
||||
Double pointer to a uint8:
|
||||
```
|
||||
%v: <**>5
|
||||
%+v: <**>(0xf8400420d0->0xf8400420c8)5
|
||||
%#v: (**uint8)5
|
||||
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
|
||||
```
|
||||
|
||||
Pointer to circular struct with a uint8 field and a pointer to itself:
|
||||
```
|
||||
%v: <*>{1 <*><shown>}
|
||||
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
|
||||
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
|
||||
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
Configuration of spew is handled by fields in the ConfigState type. For
|
||||
convenience, all of the top-level functions use a global state available via the
|
||||
spew.Config global.
|
||||
|
||||
It is also possible to create a ConfigState instance that provides methods
|
||||
equivalent to the top-level functions. This allows concurrent configuration
|
||||
options. See the ConfigState documentation for more details.
|
||||
|
||||
```
|
||||
* Indent
|
||||
String to use for each indentation level for Dump functions.
|
||||
It is a single space by default. A popular alternative is "\t".
|
||||
|
||||
* MaxDepth
|
||||
Maximum number of levels to descend into nested data structures.
|
||||
There is no limit by default.
|
||||
|
||||
* DisableMethods
|
||||
Disables invocation of error and Stringer interface methods.
|
||||
Method invocation is enabled by default.
|
||||
|
||||
* DisablePointerMethods
|
||||
Disables invocation of error and Stringer interface methods on types
|
||||
which only accept pointer receivers from non-pointer variables. This option
|
||||
relies on access to the unsafe package, so it will not have any effect when
|
||||
running in environments without access to the unsafe package such as Google
|
||||
App Engine or with the "safe" build tag specified.
|
||||
Pointer method invocation is enabled by default.
|
||||
|
||||
* DisablePointerAddresses
|
||||
DisablePointerAddresses specifies whether to disable the printing of
|
||||
pointer addresses. This is useful when diffing data structures in tests.
|
||||
|
||||
* DisableCapacities
|
||||
DisableCapacities specifies whether to disable the printing of capacities
|
||||
for arrays, slices, maps and channels. This is useful when diffing data
|
||||
structures in tests.
|
||||
|
||||
* ContinueOnMethod
|
||||
Enables recursion into types after invoking error and Stringer interface
|
||||
methods. Recursion after method invocation is disabled by default.
|
||||
|
||||
* SortKeys
|
||||
Specifies map keys should be sorted before being printed. Use
|
||||
this to have a more deterministic, diffable output. Note that
|
||||
only native types (bool, int, uint, floats, uintptr and string)
|
||||
and types which implement error or Stringer interfaces are supported,
|
||||
with other types sorted according to the reflect.Value.String() output
|
||||
which guarantees display stability. Natural map order is used by
|
||||
default.
|
||||
|
||||
* SpewKeys
|
||||
SpewKeys specifies that, as a last resort attempt, map keys should be
|
||||
spewed to strings and sorted by those strings. This is only considered
|
||||
if SortKeys is true.
|
||||
|
||||
```
|
||||
|
||||
## Unsafe Package Dependency
|
||||
|
||||
This package relies on the unsafe package to perform some of the more advanced
|
||||
features, however it also supports a "limited" mode which allows it to work in
|
||||
environments where the unsafe package is not available. By default, it will
|
||||
operate in this mode on Google App Engine and when compiled with GopherJS. The
|
||||
"safe" build tag may also be specified to force the package to build without
|
||||
using the unsafe package.
|
||||
|
||||
## License
|
||||
|
||||
Go-spew is licensed under the [copyfree](http://copyfree.org) ISC License.
|
22
vendor/github.com/davecgh/go-spew/cov_report.sh
generated
vendored
22
vendor/github.com/davecgh/go-spew/cov_report.sh
generated
vendored
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This script uses gocov to generate a test coverage report.
|
||||
# The gocov tool my be obtained with the following command:
|
||||
# go get github.com/axw/gocov/gocov
|
||||
#
|
||||
# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH.
|
||||
|
||||
# Check for gocov.
|
||||
if ! type gocov >/dev/null 2>&1; then
|
||||
echo >&2 "This script requires the gocov tool."
|
||||
echo >&2 "You may obtain it with the following command:"
|
||||
echo >&2 "go get github.com/axw/gocov/gocov"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Only run the cgo tests if gcc is installed.
|
||||
if type gcc >/dev/null 2>&1; then
|
||||
(cd spew && gocov test -tags testcgo | gocov report)
|
||||
else
|
||||
(cd spew && gocov test | gocov report)
|
||||
fi
|
298
vendor/github.com/davecgh/go-spew/spew/common_test.go
generated
vendored
298
vendor/github.com/davecgh/go-spew/spew/common_test.go
generated
vendored
@@ -1,298 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
// custom type to test Stinger interface on non-pointer receiver.
|
||||
type stringer string
|
||||
|
||||
// String implements the Stringer interface for testing invocation of custom
|
||||
// stringers on types with non-pointer receivers.
|
||||
func (s stringer) String() string {
|
||||
return "stringer " + string(s)
|
||||
}
|
||||
|
||||
// custom type to test Stinger interface on pointer receiver.
|
||||
type pstringer string
|
||||
|
||||
// String implements the Stringer interface for testing invocation of custom
|
||||
// stringers on types with only pointer receivers.
|
||||
func (s *pstringer) String() string {
|
||||
return "stringer " + string(*s)
|
||||
}
|
||||
|
||||
// xref1 and xref2 are cross referencing structs for testing circular reference
|
||||
// detection.
|
||||
type xref1 struct {
|
||||
ps2 *xref2
|
||||
}
|
||||
type xref2 struct {
|
||||
ps1 *xref1
|
||||
}
|
||||
|
||||
// indirCir1, indirCir2, and indirCir3 are used to generate an indirect circular
|
||||
// reference for testing detection.
|
||||
type indirCir1 struct {
|
||||
ps2 *indirCir2
|
||||
}
|
||||
type indirCir2 struct {
|
||||
ps3 *indirCir3
|
||||
}
|
||||
type indirCir3 struct {
|
||||
ps1 *indirCir1
|
||||
}
|
||||
|
||||
// embed is used to test embedded structures.
|
||||
type embed struct {
|
||||
a string
|
||||
}
|
||||
|
||||
// embedwrap is used to test embedded structures.
|
||||
type embedwrap struct {
|
||||
*embed
|
||||
e *embed
|
||||
}
|
||||
|
||||
// panicer is used to intentionally cause a panic for testing spew properly
|
||||
// handles them
|
||||
type panicer int
|
||||
|
||||
func (p panicer) String() string {
|
||||
panic("test panic")
|
||||
}
|
||||
|
||||
// customError is used to test custom error interface invocation.
|
||||
type customError int
|
||||
|
||||
func (e customError) Error() string {
|
||||
return fmt.Sprintf("error: %d", int(e))
|
||||
}
|
||||
|
||||
// stringizeWants converts a slice of wanted test output into a format suitable
|
||||
// for a test error message.
|
||||
func stringizeWants(wants []string) string {
|
||||
s := ""
|
||||
for i, want := range wants {
|
||||
if i > 0 {
|
||||
s += fmt.Sprintf("want%d: %s", i+1, want)
|
||||
} else {
|
||||
s += "want: " + want
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// testFailed returns whether or not a test failed by checking if the result
|
||||
// of the test is in the slice of wanted strings.
|
||||
func testFailed(result string, wants []string) bool {
|
||||
for _, want := range wants {
|
||||
if result == want {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type sortableStruct struct {
|
||||
x int
|
||||
}
|
||||
|
||||
func (ss sortableStruct) String() string {
|
||||
return fmt.Sprintf("ss.%d", ss.x)
|
||||
}
|
||||
|
||||
type unsortableStruct struct {
|
||||
x int
|
||||
}
|
||||
|
||||
type sortTestCase struct {
|
||||
input []reflect.Value
|
||||
expected []reflect.Value
|
||||
}
|
||||
|
||||
func helpTestSortValues(tests []sortTestCase, cs *spew.ConfigState, t *testing.T) {
|
||||
getInterfaces := func(values []reflect.Value) []interface{} {
|
||||
interfaces := []interface{}{}
|
||||
for _, v := range values {
|
||||
interfaces = append(interfaces, v.Interface())
|
||||
}
|
||||
return interfaces
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
spew.SortValues(test.input, cs)
|
||||
// reflect.DeepEqual cannot really make sense of reflect.Value,
|
||||
// probably because of all the pointer tricks. For instance,
|
||||
// v(2.0) != v(2.0) on a 32-bits system. Turn them into interface{}
|
||||
// instead.
|
||||
input := getInterfaces(test.input)
|
||||
expected := getInterfaces(test.expected)
|
||||
if !reflect.DeepEqual(input, expected) {
|
||||
t.Errorf("Sort mismatch:\n %v != %v", input, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestSortValues ensures the sort functionality for relect.Value based sorting
|
||||
// works as intended.
|
||||
func TestSortValues(t *testing.T) {
|
||||
v := reflect.ValueOf
|
||||
|
||||
a := v("a")
|
||||
b := v("b")
|
||||
c := v("c")
|
||||
embedA := v(embed{"a"})
|
||||
embedB := v(embed{"b"})
|
||||
embedC := v(embed{"c"})
|
||||
tests := []sortTestCase{
|
||||
// No values.
|
||||
{
|
||||
[]reflect.Value{},
|
||||
[]reflect.Value{},
|
||||
},
|
||||
// Bools.
|
||||
{
|
||||
[]reflect.Value{v(false), v(true), v(false)},
|
||||
[]reflect.Value{v(false), v(false), v(true)},
|
||||
},
|
||||
// Ints.
|
||||
{
|
||||
[]reflect.Value{v(2), v(1), v(3)},
|
||||
[]reflect.Value{v(1), v(2), v(3)},
|
||||
},
|
||||
// Uints.
|
||||
{
|
||||
[]reflect.Value{v(uint8(2)), v(uint8(1)), v(uint8(3))},
|
||||
[]reflect.Value{v(uint8(1)), v(uint8(2)), v(uint8(3))},
|
||||
},
|
||||
// Floats.
|
||||
{
|
||||
[]reflect.Value{v(2.0), v(1.0), v(3.0)},
|
||||
[]reflect.Value{v(1.0), v(2.0), v(3.0)},
|
||||
},
|
||||
// Strings.
|
||||
{
|
||||
[]reflect.Value{b, a, c},
|
||||
[]reflect.Value{a, b, c},
|
||||
},
|
||||
// Array
|
||||
{
|
||||
[]reflect.Value{v([3]int{3, 2, 1}), v([3]int{1, 3, 2}), v([3]int{1, 2, 3})},
|
||||
[]reflect.Value{v([3]int{1, 2, 3}), v([3]int{1, 3, 2}), v([3]int{3, 2, 1})},
|
||||
},
|
||||
// Uintptrs.
|
||||
{
|
||||
[]reflect.Value{v(uintptr(2)), v(uintptr(1)), v(uintptr(3))},
|
||||
[]reflect.Value{v(uintptr(1)), v(uintptr(2)), v(uintptr(3))},
|
||||
},
|
||||
// SortableStructs.
|
||||
{
|
||||
// Note: not sorted - DisableMethods is set.
|
||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||
},
|
||||
// UnsortableStructs.
|
||||
{
|
||||
// Note: not sorted - SpewKeys is false.
|
||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||
},
|
||||
// Invalid.
|
||||
{
|
||||
[]reflect.Value{embedB, embedA, embedC},
|
||||
[]reflect.Value{embedB, embedA, embedC},
|
||||
},
|
||||
}
|
||||
cs := spew.ConfigState{DisableMethods: true, SpewKeys: false}
|
||||
helpTestSortValues(tests, &cs, t)
|
||||
}
|
||||
|
||||
// TestSortValuesWithMethods ensures the sort functionality for relect.Value
|
||||
// based sorting works as intended when using string methods.
|
||||
func TestSortValuesWithMethods(t *testing.T) {
|
||||
v := reflect.ValueOf
|
||||
|
||||
a := v("a")
|
||||
b := v("b")
|
||||
c := v("c")
|
||||
tests := []sortTestCase{
|
||||
// Ints.
|
||||
{
|
||||
[]reflect.Value{v(2), v(1), v(3)},
|
||||
[]reflect.Value{v(1), v(2), v(3)},
|
||||
},
|
||||
// Strings.
|
||||
{
|
||||
[]reflect.Value{b, a, c},
|
||||
[]reflect.Value{a, b, c},
|
||||
},
|
||||
// SortableStructs.
|
||||
{
|
||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||
[]reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
|
||||
},
|
||||
// UnsortableStructs.
|
||||
{
|
||||
// Note: not sorted - SpewKeys is false.
|
||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||
},
|
||||
}
|
||||
cs := spew.ConfigState{DisableMethods: false, SpewKeys: false}
|
||||
helpTestSortValues(tests, &cs, t)
|
||||
}
|
||||
|
||||
// TestSortValuesWithSpew ensures the sort functionality for relect.Value
|
||||
// based sorting works as intended when using spew to stringify keys.
|
||||
func TestSortValuesWithSpew(t *testing.T) {
|
||||
v := reflect.ValueOf
|
||||
|
||||
a := v("a")
|
||||
b := v("b")
|
||||
c := v("c")
|
||||
tests := []sortTestCase{
|
||||
// Ints.
|
||||
{
|
||||
[]reflect.Value{v(2), v(1), v(3)},
|
||||
[]reflect.Value{v(1), v(2), v(3)},
|
||||
},
|
||||
// Strings.
|
||||
{
|
||||
[]reflect.Value{b, a, c},
|
||||
[]reflect.Value{a, b, c},
|
||||
},
|
||||
// SortableStructs.
|
||||
{
|
||||
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
|
||||
[]reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
|
||||
},
|
||||
// UnsortableStructs.
|
||||
{
|
||||
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
|
||||
[]reflect.Value{v(unsortableStruct{1}), v(unsortableStruct{2}), v(unsortableStruct{3})},
|
||||
},
|
||||
}
|
||||
cs := spew.ConfigState{DisableMethods: true, SpewKeys: true}
|
||||
helpTestSortValues(tests, &cs, t)
|
||||
}
|
1042
vendor/github.com/davecgh/go-spew/spew/dump_test.go
generated
vendored
1042
vendor/github.com/davecgh/go-spew/spew/dump_test.go
generated
vendored
File diff suppressed because it is too large
Load Diff
99
vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go
generated
vendored
99
vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go
generated
vendored
@@ -1,99 +0,0 @@
|
||||
// Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when both cgo is supported and "-tags testcgo" is added to the go test
|
||||
// command line. This means the cgo tests are only added (and hence run) when
|
||||
// specifially requested. This configuration is used because spew itself
|
||||
// does not require cgo to run even though it does handle certain cgo types
|
||||
// specially. Rather than forcing all clients to require cgo and an external
|
||||
// C compiler just to run the tests, this scheme makes them optional.
|
||||
// +build cgo,testcgo
|
||||
|
||||
package spew_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/davecgh/go-spew/spew/testdata"
|
||||
)
|
||||
|
||||
func addCgoDumpTests() {
|
||||
// C char pointer.
|
||||
v := testdata.GetCgoCharPointer()
|
||||
nv := testdata.GetCgoNullCharPointer()
|
||||
pv := &v
|
||||
vcAddr := fmt.Sprintf("%p", v)
|
||||
vAddr := fmt.Sprintf("%p", pv)
|
||||
pvAddr := fmt.Sprintf("%p", &pv)
|
||||
vt := "*testdata._Ctype_char"
|
||||
vs := "116"
|
||||
addDumpTest(v, "("+vt+")("+vcAddr+")("+vs+")\n")
|
||||
addDumpTest(pv, "(*"+vt+")("+vAddr+"->"+vcAddr+")("+vs+")\n")
|
||||
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+"->"+vcAddr+")("+vs+")\n")
|
||||
addDumpTest(nv, "("+vt+")(<nil>)\n")
|
||||
|
||||
// C char array.
|
||||
v2, v2l, v2c := testdata.GetCgoCharArray()
|
||||
v2Len := fmt.Sprintf("%d", v2l)
|
||||
v2Cap := fmt.Sprintf("%d", v2c)
|
||||
v2t := "[6]testdata._Ctype_char"
|
||||
v2s := "(len=" + v2Len + " cap=" + v2Cap + ") " +
|
||||
"{\n 00000000 74 65 73 74 32 00 " +
|
||||
" |test2.|\n}"
|
||||
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
|
||||
|
||||
// C unsigned char array.
|
||||
v3, v3l, v3c := testdata.GetCgoUnsignedCharArray()
|
||||
v3Len := fmt.Sprintf("%d", v3l)
|
||||
v3Cap := fmt.Sprintf("%d", v3c)
|
||||
v3t := "[6]testdata._Ctype_unsignedchar"
|
||||
v3t2 := "[6]testdata._Ctype_uchar"
|
||||
v3s := "(len=" + v3Len + " cap=" + v3Cap + ") " +
|
||||
"{\n 00000000 74 65 73 74 33 00 " +
|
||||
" |test3.|\n}"
|
||||
addDumpTest(v3, "("+v3t+") "+v3s+"\n", "("+v3t2+") "+v3s+"\n")
|
||||
|
||||
// C signed char array.
|
||||
v4, v4l, v4c := testdata.GetCgoSignedCharArray()
|
||||
v4Len := fmt.Sprintf("%d", v4l)
|
||||
v4Cap := fmt.Sprintf("%d", v4c)
|
||||
v4t := "[6]testdata._Ctype_schar"
|
||||
v4t2 := "testdata._Ctype_schar"
|
||||
v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " +
|
||||
"{\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 101,\n (" + v4t2 +
|
||||
") 115,\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 52,\n (" + v4t2 +
|
||||
") 0\n}"
|
||||
addDumpTest(v4, "("+v4t+") "+v4s+"\n")
|
||||
|
||||
// C uint8_t array.
|
||||
v5, v5l, v5c := testdata.GetCgoUint8tArray()
|
||||
v5Len := fmt.Sprintf("%d", v5l)
|
||||
v5Cap := fmt.Sprintf("%d", v5c)
|
||||
v5t := "[6]testdata._Ctype_uint8_t"
|
||||
v5s := "(len=" + v5Len + " cap=" + v5Cap + ") " +
|
||||
"{\n 00000000 74 65 73 74 35 00 " +
|
||||
" |test5.|\n}"
|
||||
addDumpTest(v5, "("+v5t+") "+v5s+"\n")
|
||||
|
||||
// C typedefed unsigned char array.
|
||||
v6, v6l, v6c := testdata.GetCgoTypdefedUnsignedCharArray()
|
||||
v6Len := fmt.Sprintf("%d", v6l)
|
||||
v6Cap := fmt.Sprintf("%d", v6c)
|
||||
v6t := "[6]testdata._Ctype_custom_uchar_t"
|
||||
v6s := "(len=" + v6Len + " cap=" + v6Cap + ") " +
|
||||
"{\n 00000000 74 65 73 74 36 00 " +
|
||||
" |test6.|\n}"
|
||||
addDumpTest(v6, "("+v6t+") "+v6s+"\n")
|
||||
}
|
26
vendor/github.com/davecgh/go-spew/spew/dumpnocgo_test.go
generated
vendored
26
vendor/github.com/davecgh/go-spew/spew/dumpnocgo_test.go
generated
vendored
@@ -1,26 +0,0 @@
|
||||
// Copyright (c) 2013 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when either cgo is not supported or "-tags testcgo" is not added to the go
|
||||
// test command line. This file intentionally does not setup any cgo tests in
|
||||
// this scenario.
|
||||
// +build !cgo !testcgo
|
||||
|
||||
package spew_test
|
||||
|
||||
func addCgoDumpTests() {
|
||||
// Don't add any tests for cgo since this file is only compiled when
|
||||
// there should not be any cgo tests.
|
||||
}
|
226
vendor/github.com/davecgh/go-spew/spew/example_test.go
generated
vendored
226
vendor/github.com/davecgh/go-spew/spew/example_test.go
generated
vendored
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
type Flag int
|
||||
|
||||
const (
|
||||
flagOne Flag = iota
|
||||
flagTwo
|
||||
)
|
||||
|
||||
var flagStrings = map[Flag]string{
|
||||
flagOne: "flagOne",
|
||||
flagTwo: "flagTwo",
|
||||
}
|
||||
|
||||
func (f Flag) String() string {
|
||||
if s, ok := flagStrings[f]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Unknown flag (%d)", int(f))
|
||||
}
|
||||
|
||||
type Bar struct {
|
||||
data uintptr
|
||||
}
|
||||
|
||||
type Foo struct {
|
||||
unexportedField Bar
|
||||
ExportedField map[interface{}]interface{}
|
||||
}
|
||||
|
||||
// This example demonstrates how to use Dump to dump variables to stdout.
|
||||
func ExampleDump() {
|
||||
// The following package level declarations are assumed for this example:
|
||||
/*
|
||||
type Flag int
|
||||
|
||||
const (
|
||||
flagOne Flag = iota
|
||||
flagTwo
|
||||
)
|
||||
|
||||
var flagStrings = map[Flag]string{
|
||||
flagOne: "flagOne",
|
||||
flagTwo: "flagTwo",
|
||||
}
|
||||
|
||||
func (f Flag) String() string {
|
||||
if s, ok := flagStrings[f]; ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("Unknown flag (%d)", int(f))
|
||||
}
|
||||
|
||||
type Bar struct {
|
||||
data uintptr
|
||||
}
|
||||
|
||||
type Foo struct {
|
||||
unexportedField Bar
|
||||
ExportedField map[interface{}]interface{}
|
||||
}
|
||||
*/
|
||||
|
||||
// Setup some sample data structures for the example.
|
||||
bar := Bar{uintptr(0)}
|
||||
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
|
||||
f := Flag(5)
|
||||
b := []byte{
|
||||
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
||||
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
|
||||
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
|
||||
0x31, 0x32,
|
||||
}
|
||||
|
||||
// Dump!
|
||||
spew.Dump(s1, f, b)
|
||||
|
||||
// Output:
|
||||
// (spew_test.Foo) {
|
||||
// unexportedField: (spew_test.Bar) {
|
||||
// data: (uintptr) <nil>
|
||||
// },
|
||||
// ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||
// (string) (len=3) "one": (bool) true
|
||||
// }
|
||||
// }
|
||||
// (spew_test.Flag) Unknown flag (5)
|
||||
// ([]uint8) (len=34 cap=34) {
|
||||
// 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
||||
// 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
||||
// 00000020 31 32 |12|
|
||||
// }
|
||||
//
|
||||
}
|
||||
|
||||
// This example demonstrates how to use Printf to display a variable with a
|
||||
// format string and inline formatting.
|
||||
func ExamplePrintf() {
|
||||
// Create a double pointer to a uint 8.
|
||||
ui8 := uint8(5)
|
||||
pui8 := &ui8
|
||||
ppui8 := &pui8
|
||||
|
||||
// Create a circular data type.
|
||||
type circular struct {
|
||||
ui8 uint8
|
||||
c *circular
|
||||
}
|
||||
c := circular{ui8: 1}
|
||||
c.c = &c
|
||||
|
||||
// Print!
|
||||
spew.Printf("ppui8: %v\n", ppui8)
|
||||
spew.Printf("circular: %v\n", c)
|
||||
|
||||
// Output:
|
||||
// ppui8: <**>5
|
||||
// circular: {1 <*>{1 <*><shown>}}
|
||||
}
|
||||
|
||||
// This example demonstrates how to use a ConfigState.
|
||||
func ExampleConfigState() {
|
||||
// Modify the indent level of the ConfigState only. The global
|
||||
// configuration is not modified.
|
||||
scs := spew.ConfigState{Indent: "\t"}
|
||||
|
||||
// Output using the ConfigState instance.
|
||||
v := map[string]int{"one": 1}
|
||||
scs.Printf("v: %v\n", v)
|
||||
scs.Dump(v)
|
||||
|
||||
// Output:
|
||||
// v: map[one:1]
|
||||
// (map[string]int) (len=1) {
|
||||
// (string) (len=3) "one": (int) 1
|
||||
// }
|
||||
}
|
||||
|
||||
// This example demonstrates how to use ConfigState.Dump to dump variables to
|
||||
// stdout
|
||||
func ExampleConfigState_Dump() {
|
||||
// See the top-level Dump example for details on the types used in this
|
||||
// example.
|
||||
|
||||
// Create two ConfigState instances with different indentation.
|
||||
scs := spew.ConfigState{Indent: "\t"}
|
||||
scs2 := spew.ConfigState{Indent: " "}
|
||||
|
||||
// Setup some sample data structures for the example.
|
||||
bar := Bar{uintptr(0)}
|
||||
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
|
||||
|
||||
// Dump using the ConfigState instances.
|
||||
scs.Dump(s1)
|
||||
scs2.Dump(s1)
|
||||
|
||||
// Output:
|
||||
// (spew_test.Foo) {
|
||||
// unexportedField: (spew_test.Bar) {
|
||||
// data: (uintptr) <nil>
|
||||
// },
|
||||
// ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||
// (string) (len=3) "one": (bool) true
|
||||
// }
|
||||
// }
|
||||
// (spew_test.Foo) {
|
||||
// unexportedField: (spew_test.Bar) {
|
||||
// data: (uintptr) <nil>
|
||||
// },
|
||||
// ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||
// (string) (len=3) "one": (bool) true
|
||||
// }
|
||||
// }
|
||||
//
|
||||
}
|
||||
|
||||
// This example demonstrates how to use ConfigState.Printf to display a variable
|
||||
// with a format string and inline formatting.
|
||||
func ExampleConfigState_Printf() {
|
||||
// See the top-level Dump example for details on the types used in this
|
||||
// example.
|
||||
|
||||
// Create two ConfigState instances and modify the method handling of the
|
||||
// first ConfigState only.
|
||||
scs := spew.NewDefaultConfig()
|
||||
scs2 := spew.NewDefaultConfig()
|
||||
scs.DisableMethods = true
|
||||
|
||||
// Alternatively
|
||||
// scs := spew.ConfigState{Indent: " ", DisableMethods: true}
|
||||
// scs2 := spew.ConfigState{Indent: " "}
|
||||
|
||||
// This is of type Flag which implements a Stringer and has raw value 1.
|
||||
f := flagTwo
|
||||
|
||||
// Dump using the ConfigState instances.
|
||||
scs.Printf("f: %v\n", f)
|
||||
scs2.Printf("f: %v\n", f)
|
||||
|
||||
// Output:
|
||||
// f: 1
|
||||
// f: flagTwo
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user