Compare commits

..

979 Commits

Author SHA1 Message Date
fatedier
eb1e19a821 Merge pull request #2906 from fatedier/dev
bump version
2022-04-22 11:32:27 +08:00
fatedier
6c658586f6 bump version to v0.42.0 2022-04-22 11:15:23 +08:00
fatedier
888ed25314 dependency: update github.com/pires/go-proxyproto to v0.6.2 (#2894) 2022-04-15 11:36:00 +08:00
fatedier
21240ed962 some improvements 2022-04-14 11:24:36 +08:00
Colin Adler
6481870d03 fix: data races when accessing github.com/fatedier/frp/client.(*Service).ctl (#2891)
* fix: data race in client/service.go

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

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

* Move to actual closing routine

* Fix e2e tests for CloseProxy

* Add warning on resource exhaustion

* Add CloseProxy to manual close

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

* fix: config name

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

* fix: typo

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

* fix: x

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

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

* remove statik totally

* split go and static files in assets
2021-08-17 20:20:04 +08:00
fatedier
a62a9431b1 support go1.17 and remove go1.15 (#2532) 2021-08-17 15:26:43 +08:00
fatedier
42745a3da2 frpc: add disable_custom_tls_first_byte to not send first custom tls to frps (#2520) 2021-08-11 23:10:35 +08:00
fatedier
82f80a22be add healthz api (#2511) 2021-08-04 14:33:53 +08:00
fatedier
f570dcb307 update stale action 2021-08-04 10:51:18 +08:00
fatedier
997d406ec2 Merge pull request #2508 from fatedier/dev
bump version
2021-08-03 23:13:31 +08:00
fatedier
87e60683ed add release note for v0.37.1 (#2507) 2021-08-03 23:10:27 +08:00
fatedier
86b2e686a5 vhost: use new readClientHello function (#2504) 2021-08-03 22:58:03 +08:00
fatedier
09f39de74e add more e2e test (#2505) 2021-08-02 13:07:28 +08:00
fatedier
2a68c1152f dep: update github.com/hashicorp/yamux to latest (#2472) 2021-07-08 10:42:28 +08:00
Fishbone
df5859b5f7 Fix server-side proxy inappropriate quit when met accept: too many open files error (#2467) 2021-07-05 10:27:15 +08:00
Blizard
3dd888a9ea fix: stuct name typo (#2458)
Co-authored-by: tanghuafa <tanghuafa@bytedance.com>
2021-06-24 16:33:52 +08:00
fatedier
a51e221db3 add real ip test 2021-06-21 19:35:52 +08:00
fatedier
fe4e9b55f3 update github.com/pires/go-proxyproto to v0.5.0 2021-06-21 19:35:52 +08:00
fatedier
3f11b6a082 update k8s.io/apimachinery to v0.21.2 2021-06-21 19:35:52 +08:00
fatedier
8a333c2ae0 update github.com/stretchr/testify to v1.7.0 2021-06-21 19:35:52 +08:00
fatedier
1fd6ba2738 update github.com/spf13/cobra to v1.1.3 2021-06-21 19:35:52 +08:00
fatedier
a98a9616f6 update dependency github.com/rodaine/table to v1.0.1 2021-06-21 19:35:52 +08:00
fatedier
95cd9ab900 update dependency github.com/prometheus/client_golang to v1.11.0 2021-06-21 19:35:52 +08:00
fatedier
900454e58b more e2e test cases (#2450) 2021-06-18 16:48:36 +08:00
fatedier
c7d4637382 fix web js (#2444) 2021-06-09 11:53:39 +08:00
ztz
56925961df Typo: josn => json (#2429) 2021-06-04 11:21:52 +08:00
fatedier
cfd1a3128a Merge pull request #2426 from fatedier/dev
update workflow file
2021-06-03 00:59:21 +08:00
fatedier
2393923870 update build image go version 2021-06-03 00:56:07 +08:00
fatedier
5f594e9a71 fix goreleaser 2021-06-03 00:46:56 +08:00
fatedier
57577ea044 Merge pull request #2425 from fatedier/dev
bump version
2021-06-03 00:14:32 +08:00
fatedier
8637077d90 update version 2021-06-03 00:10:35 +08:00
fatedier
ccb85a9926 update README 2021-06-03 00:07:51 +08:00
fatedier
02b12df887 frpc: consider include configs for verify and reload command (#2424) 2021-06-02 23:54:22 +08:00
fatedier
c32a2ed140 frpc: support 'includes' in common section to include proxy configs in other files(#2421) 2021-06-02 00:39:05 +08:00
Wu Han
9ae322cccf readme: fix toc hyperlinx (#2401) 2021-05-17 17:29:13 +08:00
fatedier
9cebfccb39 cmd: add verify command to verify if config file syntax is valid (#2389) 2021-05-12 12:15:22 +08:00
fatedier
630dad50ed web: support sudp in dashboard (#2385) 2021-05-11 13:37:14 +08:00
fatedier
0d84da91d4 change default value of dashboard_user and dashboard_pwd to empty string (#2383) 2021-05-10 23:04:26 +08:00
fatedier
2408f1df04 frpc: fix that login_fail_exit invalid if protocol=kcp (#2363) 2021-04-22 18:33:10 +08:00
fatedier
fbaa5f866e add e2e tests (#2334) 2021-03-31 16:57:39 +08:00
fatedier
c5c79e4148 Merge pull request #2324 from fatedier/dev
bump version v0.36.2
2021-03-22 14:56:48 +08:00
fatedier
9a849a29e9 fix config parse logic (#2323) 2021-03-22 14:53:30 +08:00
Splash
6b80861bd6 Fix log_file does not work in config files (#2316) 2021-03-22 11:30:12 +08:00
fatedier
fa0e84382e autogen code 2021-03-19 17:39:26 +08:00
bobo liu
1a11b28f8d config: inline is NOT SUPPORTED in encoding/json (#2304) 2021-03-19 17:36:39 +08:00
YK-Samgo
bed13d7ef1 Support reverseproxy to dashboard with additional parts in path (#2289)
/api/ -> ../api/   in vendor.js
    Support reverseproxy to dashboard with addtional parts in path.
2021-03-19 17:31:16 +08:00
fatedier
e7d76b180d update README.md (#2314) 2021-03-19 16:06:22 +08:00
wuqing
dba8925eaa makefile add arm64 2021-03-19 14:01:26 +08:00
fatedier
55da58eca4 Merge pull request #2310 from fatedier/dev
bump version
2021-03-18 11:14:56 +08:00
wuqing
fdef7448a7 update version 2021-03-18 11:12:10 +08:00
Chotow
0ff27fc9ac fix(server): listen udp port failed (#2308)
fix #2306
2021-03-18 11:05:03 +08:00
fatedier
76a1efccd9 update 2021-03-17 11:43:23 +08:00
fatedier
9f8db314d6 update 2021-03-17 11:43:03 +08:00
fatedier
980f084ad1 Merge pull request #2302 from fatedier/dev
bump version
2021-03-15 21:54:52 +08:00
fatedier
0c35863d97 bump version to v0.36.0 (#2301) 2021-03-15 21:50:47 +08:00
fatedier
184a0ff9ab update Makefile.cross-compiles and support darwin/arm64 (#2295) 2021-03-15 21:02:56 +08:00
yuyulei
8e25f13201 update: support ipv6 (#2288) 2021-03-10 20:19:58 +08:00
yuyulei
b5aee82ca9 update: support custom tls server name (#2278) 2021-03-07 14:57:23 +08:00
iikira
0a2384a283 fix XTCP error handling (#2273) 2021-03-03 20:54:46 +08:00
yuyulei
78b8bb7bc6 update docs: add example for building tls certificates (#2251) 2021-02-22 13:59:21 +08:00
fatedier
8fcd4f4a95 client: reconnect more quickly if it's a dial error (#2240) 2021-02-18 16:15:35 +08:00
Albert Zhao
976fd81d4d modify gitignore;fix a typo (#2237) 2021-02-07 14:07:20 +08:00
Kevin Crawley
52d5c9e25b initial commit for https2https plugin (#2201) 2021-01-27 13:14:16 +08:00
Asher Oto
fa89671452 Update README (#2221)
* Updated README.md

- Fixed spelling of PayPal
- Added information to TLS section, fixed spelling
2021-01-27 13:07:44 +08:00
yuyulei
3621aad1c1 Reconstruct config (#2098)
* refactoring config

* Update by comments
2021-01-26 11:31:08 +08:00
fatedier
3bf1eb8565 Merge pull request #2216 from fatedier/dev
bump version
2021-01-25 16:15:52 +08:00
fatedier
a821db3f45 add release note 2021-01-25 16:06:38 +08:00
fatedier
ecb6ed9258 revert web code (#2215) 2021-01-25 16:04:33 +08:00
fatedier
b2ae433e18 Merge pull request #2206 from fatedier/dev
bump version
2021-01-19 20:56:06 +08:00
fatedier
b26080589b bump version 2021-01-19 20:51:06 +08:00
yuyulei
aff979c2b6 Merge pull request #2199 from fatedier/fix
vhost: set DisableKeepAlives = false and fix websocket not work
2021-01-19 15:40:41 +08:00
fatedier
46f809d711 vhost: set DisableKeepAlives = false and fix websocket not work 2021-01-18 21:49:44 +08:00
yuyulei
72595b2da8 Add user remote address info log (#2184) 2021-01-11 16:52:17 +08:00
XNG
c842558ace Reduced log level of "get hostname error" (#2165)
Reduced log level of "get hostname from http/https request error" to debug so that it won't overwhelms the log file when user is using apache/ngix as their own SSL backend
2020-12-25 13:39:26 +08:00
yuyulei
ed61049041 Bugfix: add ipv6 parsing with address of frps (#2163) 2020-12-24 21:48:26 +08:00
fatedier
abe6f580c0 update README 2020-12-03 20:41:37 +08:00
fatedier
e940066012 auto generate web assets 2020-12-03 20:23:43 +08:00
hubery
1e846df870 some dashboard refactor (#2101) 2020-12-03 20:20:48 +08:00
Mike Cardwell
0ab055e946 Allow server plugin to talk to https services. Option for skipping tls verification (#2103)
* Allow server plugin to talk to https services. Option for skipping tls verification

* Rename TlsVerify to TLSVerify

* Server plugin should use default http transport when scheme is not https
2020-12-03 14:36:14 +08:00
fatedier
fca59c71e2 clear Release.md 2020-11-23 17:34:09 +08:00
wxiaoguang
fae2f8768d tweak logs: (#2100)
* show what config frps uses (config file / command line)
* optimize the "start successfully" log message
2020-11-23 17:01:31 +08:00
fatedier
3d9499f554 autogen web assets 2020-11-23 15:21:07 +08:00
hubery
7adeeedd55 fix dashboard horizontal scrollbar (#2096) 2020-11-23 14:52:55 +08:00
yuyulei
127a31ea6a Fix typo (#2089) 2020-11-23 11:38:21 +08:00
fatedier
a85bd9a4d9 update 2020-11-20 20:10:08 +08:00
fatedier
01d551ec8d update 2020-11-20 18:10:33 +08:00
fatedier
16cabf4127 add .circleci 2020-11-20 18:04:44 +08:00
fatedier
968be4a2c2 use self token 2020-11-20 17:35:06 +08:00
fatedier
aa0a41ee4e Merge pull request #2088 from fatedier/dev
bump version to v0.34.3
2020-11-20 17:04:55 +08:00
fatedier
8a779eb88c add goreleaser action (#2087)
* add goreleaser action

* change version
2020-11-20 17:00:14 +08:00
蓝云Reyes
0138dbd352 update ISSUE_TEMPLATE (#2061) 2020-11-09 19:39:56 +08:00
yuyulei
9b45c93c14 Fix set-env in github actions of frp (#2060) 2020-11-09 16:56:53 +08:00
fatedier
191da54980 .travis.yml add go1.15 and remove go1.13 2020-11-06 15:49:03 +08:00
Dan Ordille
c3b7575453 Add enable_prometheus option as command line flag (#2057) 2020-11-06 15:33:59 +08:00
fatedier
1ea1530b36 Merge pull request #2058 from fatedier/dev
bump version to v0.34.2
2020-11-06 14:50:50 +08:00
fatedier
7f7305fa03 update version 2020-11-06 14:39:04 +08:00
yuyulei
644a0cfdb6 Update ReverseProxy code from official golang repo (#2051)
Fix #1192
2020-11-05 16:34:17 +08:00
Chirag Sukhala
3c2e2bcea5 Update frpc_full.ini (#2023)
Added authenticate_heartbeats and authenticate_new_work_conns
2020-10-08 14:25:03 +08:00
fatedier
e0c45a1aca Merge pull request #2018 from fatedier/dev
bump version to v0.34.1
2020-09-30 15:13:08 +08:00
fatedier
e52dfc4a5c bump version to v0.34.1 2020-09-30 14:40:08 +08:00
lonwern
cc003a2570 reduce docker image size (#2014) 2020-09-30 11:05:34 +08:00
lonwern
0f8040b875 fix create tls work connection (#2013) 2020-09-29 15:44:52 +08:00
harmy
ef5ae3e598 fix: a reconnected proxy will disappear from dashboard after 7 days (#2008) 2020-09-25 16:10:20 +08:00
fatedier
3acf1bb6e9 update stale workflow 2020-09-25 15:15:34 +08:00
fatedier
1089eb9d22 update stale workflow 2020-09-25 15:00:41 +08:00
vesta
edf9596ca8 ci: add close stale (#2001) 2020-09-25 14:40:01 +08:00
fatedier
26e54b901f dockerfile: remove ca-certificates 2020-09-23 16:38:44 +08:00
fatedier
008933f304 typo fix 2020-09-23 15:53:08 +08:00
fatedier
317f901c1c typo 2020-09-23 14:40:22 +08:00
fatedier
cd5314466c update workflows build-and-push-image 2020-09-23 14:20:46 +08:00
fatedier
3fbdea0f6b rename models to pkg (#2005) 2020-09-23 13:49:14 +08:00
vesta
710ecf44f5 feat: support ntlm proxy set in http_proxy env (#2002) 2020-09-23 12:05:05 +08:00
yuyulei
04dafd7ff0 Remove comments (#2004) 2020-09-23 11:57:04 +08:00
yuyulei
c6aa74a2bb Add action to build and push image to dockerhub&github packages (#1998) 2020-09-23 11:05:50 +08:00
fatedier
813c45f5c2 Merge pull request #1993 from fatedier/dev
bump version to v0.34.0
2020-09-20 00:30:51 +08:00
fatedier
c0e05bb41e bump version to v0.34.0 2020-09-20 00:29:27 +08:00
fatedier
aa74dc4646 Merge pull request #1990 from fatedier/dev
bump version to v0.34.0
2020-09-20 00:10:32 +08:00
fatedier
1e420cc766 update tls 2020-09-18 20:06:33 +08:00
yuyulei
4fff3c7472 Add tls configuration to both client and server (#1974) 2020-09-18 19:58:58 +08:00
fatedier
48fa618c34 update e2e tests (#1973) 2020-09-07 15:45:44 +08:00
fatedier
c9fe23eb10 more e2e tests (#1845) 2020-09-07 14:57:23 +08:00
Luka Čehovin Zajc
268afb3438 Sorting plugins so that execution order is deterministic (#1961) 2020-08-31 19:49:46 +08:00
fzhyzamt
b1181fd17a support non-preemptive authentication (#1888) 2020-07-01 11:18:23 +08:00
Albert Zhao
b23548eeff fix grammar issue (#1850) (#1852) 2020-06-11 15:39:59 +08:00
fatedier
262317192c new e2e framework (#1835) 2020-06-02 22:48:55 +08:00
fatedier
8b75b8b837 fix by golint (#1822) 2020-05-24 17:48:37 +08:00
fatedier
2170c481ce update doc (#1821) 2020-05-24 14:05:31 +08:00
Tank
dfbf9c4542 style: adjust frps files (#1820) 2020-05-24 11:28:57 +08:00
Tank
964a1bbf39 refine: frpc flags (#1811) 2020-05-19 10:49:29 +08:00
Tank
228e225f84 fix: sync/atomic bug, fix #1804 (#1805)
Co-authored-by: tanghuafa <tanghuafa@bytedance.com>
2020-05-12 22:09:16 +08:00
Tank
bd6435c982 fix: frps plugin manager (#1803) 2020-05-12 15:55:35 +08:00
Tank
591023a1f0 fix: add frpc tls_enable flag and frps tls_only flag (#1798) 2020-05-12 14:33:34 +08:00
Tank
1ab23b5e0e fix: typo (#1799) 2020-05-10 17:58:35 +08:00
Tank
d193519329 feat: Support user specify udp packet size in config (#1794) 2020-05-07 17:47:36 +08:00
fatedier
2406ecdfea Merge pull request #1780 from fatedier/dev
bump version
2020-04-27 16:50:34 +08:00
fatedier
7266154d54 bump version to v0.33.0 2020-04-27 16:24:17 +08:00
Tank
4797136965 feat: support sudp proxy (#1730) 2020-04-22 21:37:45 +08:00
Guy Lewin
6d78af6144 feat: group TCP mux proxies (#1765) 2020-04-20 13:35:47 +08:00
Tank
7728e35c52 fix: frps handle multi conn may happen data race (#1768) 2020-04-19 16:16:24 +08:00
Tank
5a61fd84ad fix: auth token bug (#1762) 2020-04-16 20:20:36 +08:00
zhang-wei
ad0c449a75 Server manager support the NewUserConn operation (#1740)
support NewUserConn operation
2020-04-16 13:06:46 +08:00
fatedier
1c330185c4 typo 2020-04-03 01:24:37 +08:00
fatedier
8668fef136 Merge pull request #1728 from fatedier/dev
bump version to v0.32.1
2020-04-03 01:14:58 +08:00
fatedier
7491b327f8 update ISSUE_TEMPLATE 2020-04-03 01:03:13 +08:00
fatedier
abb5b05d49 update package.sh 2020-04-03 00:59:47 +08:00
fatedier
b6ec9dad28 bump version to v0.32.1 2020-04-02 11:49:16 +08:00
Tank
caa6e8cf01 fix: frpc reconnect frps frequently lead to memory leak (#1722) 2020-04-02 10:58:37 +08:00
fatedier
ffb932390f remove qq info 2020-03-28 22:29:02 +08:00
xcffl
a8efaee1f3 Improve basic examples for newbies (#1711) 2020-03-26 17:41:18 +08:00
fatedier
4c2afb5c28 doc: add plugin repo link (#1710) 2020-03-20 20:54:22 +08:00
fatedier
809f517db8 server plugin: set version and op in http request query (#1707) 2020-03-20 20:53:14 +08:00
Guy Lewin
a4b105dedb [Feature] Server Plugin - Ping and NewWorkConn RPC (#1702) 2020-03-18 01:52:44 +08:00
Guy Lewin
10acf638f8 [Feature] Include RunId in FRP Server Plugin NewProxy message (#1700)
* feat: include RunId in FRP Server Plugin NewProxy message

* doc: rewrite server plugin documentation
2020-03-14 23:26:35 +08:00
fatedier
ea62bc5a34 remove vendor (#1697) 2020-03-11 14:39:43 +08:00
fatedier
f65ffe2812 remove vendor 2020-03-11 14:34:17 +08:00
fatedier
23bb76397a Merge pull request #1696 from fatedier/dev
bump version to v0.32.0
2020-03-11 14:30:47 +08:00
fatedier
859a330e6c Merge pull request #1695 from fatedier/doc
update doc
2020-03-11 14:18:12 +08:00
fatedier
86ac511763 bump version to v0.32.0 2020-03-11 14:13:49 +08:00
fatedier
f2e98ef8a4 update doc 2020-03-11 14:13:16 +08:00
fatedier
495d999b6c refactoring monitor code, support prometheus (#1668)
* refactoring monitor code, support prometheus
* remove vendor
2020-03-11 13:20:26 +08:00
Guy Lewin
6d1af85e80 fix: send server plugin request as json (#1690) 2020-03-10 15:23:37 +08:00
fatedier
1db091b381 tcp multiplexing over http connect tunnel 2020-03-05 21:47:49 +08:00
glzjin
0b9124d4fd Fix bandwidth compare (#1679)
It may cause all proxy restart when api reload.
2020-03-02 11:20:08 +08:00
Guy Lewin
6c6607ae68 feat: add multiple authentication methods, token and oidc.
token is the current token comparison, and oidc generates oidc token using client-credentials flow. in addition - add ping verification using the same method
2020-03-01 10:57:01 +08:00
fatedier
83d80857fd Merge pull request #1644 from GuyLewin/feature/detailed-errors-to-client
DetailedErrorsToClient - only send detailed error info if this is on
2020-02-12 10:39:03 +08:00
Guy Lewin
98fa3855bd CR: export error string generation to a function 2020-02-11 16:57:38 +02:00
Guy Lewin
9440bc5d72 Merge branch 'dev' into feature/detailed-errors-to-client
# Conflicts:
#	models/config/server_common.go
2020-02-11 11:25:04 +02:00
fatedier
95753ebf1c Merge pull request #1643 from GuyLewin/feature/tls-only
Feature/tls only
2020-02-11 13:57:03 +08:00
Guy Lewin
f8c6795119 DetailedErrorsToClient - only send detailed error info if this is on 2020-02-10 19:29:57 +02:00
Guy Lewin
7033f3e72b Test TlsOnly 2020-02-10 19:14:07 +02:00
Guy Lewin
e3101b7aa8 Update README.md 2020-02-10 19:01:23 +02:00
Guy Lewin
c747f160aa TlsOnly - only accept TLS connections if enabled 2020-02-10 18:56:41 +02:00
fatedier
c8748a2948 update .travis.yml 2020-02-04 22:24:07 +08:00
fatedier
487c8d7c29 Merge pull request #1637 from fatedier/dev
bump version to v0.31.2
2020-02-04 21:54:28 +08:00
fatedier
69fa7ed16e bump version to v0.31.2 2020-02-04 21:43:37 +08:00
fatedier
5336155365 Merge pull request #1636 from fatedier/new
send closeProxy msg to server then client start proxy error
2020-02-04 21:39:26 +08:00
fatedier
4feb74cb89 doc typo 2020-02-04 21:34:46 +08:00
fatedier
4a4cf552af send closeProxy msg to server then client start proxy error, fix #1606 2020-02-04 19:41:39 +08:00
Joe Cloud
0f59b8f329 English grammar fix. (#1619) 2020-01-15 12:11:12 +08:00
fatedier
f480160e2d Merge pull request #1596 from fatedier/dev
v0.31.1, fix bugs
2020-01-06 15:55:44 +08:00
fatedier
4832a2a1e9 bump version 2020-01-06 15:44:18 +08:00
fatedier
52ecd84d8a fix panic if set meta in proxy config, fix #1595 2020-01-06 15:43:25 +08:00
fatedier
30c246c488 Merge pull request #1588 from fatedier/dev
bump version to v0.31.0
2020-01-03 11:45:22 +08:00
fatedier
42014eea23 improve xtcp, fix #1585 2020-01-03 11:39:44 +08:00
fatedier
c2da396230 Merge pull request #1587 from fatedier/doc
add server manage plugin doc
2020-01-03 11:37:52 +08:00
fatedier
e91c9473be add server manage plugin doc 2020-01-03 11:35:12 +08:00
fatedier
13e48c6ca0 Merge pull request #1575 from fatedier/new
support server plugin feature
2019-12-31 14:12:30 +08:00
fatedier
31e2cb76bb bump version 2019-12-23 20:00:59 +08:00
fatedier
91e46a2c53 support server plugin feature 2019-12-23 20:00:04 +08:00
fatedier
a57679f837 support meta info for client and proxy 2019-12-08 21:01:58 +08:00
fatedier
75f3bce04d Merge pull request #1542 from fatedier/dev
bump version to v0.30.0
2019-11-28 14:21:27 +08:00
fatedier
df18375308 Merge pull request #1537 from fatedier/new
bump version to v0.30.0
2019-11-26 10:42:31 +08:00
fatedier
c63737ab3e update doc for bandwith limit 2019-11-26 10:23:37 +08:00
fatedier
1cdceee347 bump version to v0.30.0 2019-11-26 09:15:24 +08:00
fatedier
694c434b9e Merge pull request #1529 from kingjcy/20191118
plugin http2https
2019-11-22 15:25:01 +08:00
kingjcy
62af5c8844 handle close 2019-11-22 15:18:20 +08:00
kingjcy
56c53909aa plugin http2https
plugin http2https
2019-11-22 11:12:48 +08:00
fatedier
21a126e4e4 Merge pull request #1510 from CallanTaylor/close-file
Close file
2019-11-12 14:02:49 +08:00
CallanTaylor
8affab1a2b Close file 2019-11-12 11:38:55 +13:00
fatedier
12cc53d699 update bandwidth_limit 2019-11-09 01:13:30 +08:00
fatedier
2ab832bb89 Merge pull request #1495 from fatedier/new
support bandwith limit for one proxy
2019-11-08 21:05:13 +08:00
fatedier
42425d8218 update vendor files 2019-11-03 01:21:47 +08:00
fatedier
6da093a402 support bandwith limit for one proxy 2019-11-03 01:20:49 +08:00
fatedier
adc3adc13b Merge pull request #1494 from fatedier/dev
bump version to v0.29.1
2019-11-02 21:14:50 +08:00
fatedier
22a79710d8 bump version to v0.29.1 2019-11-02 21:05:37 +08:00
fatedier
0927553fe4 Merge pull request #1465 from lzhfromustc/dev_dup_mis_unlock
dev:udp: Add an Unlock before a continue, to fix a double lock bug
2019-10-16 00:51:26 +08:00
fatedier
858d8f0ba7 Merge pull request #1460 from weisi/dev
Fix typos in English Readme.
2019-10-15 13:52:13 +08:00
lzhfromustc
8eb945ee9b dev:udp: Add an Unlock before a continue, to fix a double lock bug 2019-10-14 23:35:08 -04:00
Weisi Dai
dc0fd60d30 frp: Fix typos in English Readme. 2019-10-13 14:46:08 -07:00
fatedier
cd44c9f55c Merge pull request #1459 from fatedier/new
change log method
2019-10-12 20:19:01 +08:00
fatedier
649f47c345 change log method 2019-10-12 20:13:12 +08:00
fatedier
6ca3160b33 Merge pull request #1451 from GuyLewin/dev
Grammer fixes
2019-10-10 10:54:09 +08:00
Guy Lewin
5f8ed4fc60 Fix CR 2019-10-09 13:41:05 -07:00
Guy Lewin
bf0993d2a6 Grammer fixes 2019-10-01 15:58:35 -04:00
fatedier
5dc8175fc8 Merge pull request #1420 from Hurricanezwf/fix-bad-xtcp-encryption
fix #1347:  bad encryption and compression when use xtcp
2019-09-01 20:49:13 +08:00
zhouwenfeng
dc6a5a29c1 fix bad encryption and compression when use xtcp 2019-08-31 21:24:20 +08:00
fatedier
e62d9a5242 Merge pull request #1415 from fatedier/dev
bump version to v0.29.0
2019-08-29 21:22:30 +08:00
fatedier
94212ac8b8 Merge pull request #1414 from fatedier/new
let max_pool_count valid
2019-08-29 21:17:30 +08:00
fatedier
e9e86fccf0 let max_pool_count valid 2019-08-29 21:13:21 +08:00
fatedier
58745992ef typo 2019-08-27 14:10:53 +08:00
fatedier
234d634bfe Merge pull request #1411 from fatedier/new
proxy protocol: fix detect method for IPV4 and IPV6, fix #1410
2019-08-26 15:30:26 +08:00
fatedier
fdc6902a90 proxy protocol: fix detect method for IPV4 and IPV6 2019-08-26 11:13:33 +08:00
fatedier
d8d587fd93 Merge pull request #1408 from velovix/issue-1387_api-documentation
Add documentation for common configuration fields, common proxy configuration, and sessions
2019-08-25 21:15:32 +08:00
Tyler Compton
92791260a7 Add documentation for base proxy config 2019-08-24 15:25:52 -07:00
Tyler Compton
4dfd851c46 Add docs for common config fields & sessions
Now that the common configuration objects and session objects are part
of a public API, they need to be documented in a way that can be read
with godoc. This commit should lead to easier development with FRP as a
library.
2019-08-24 15:20:34 -07:00
fatedier
bc4df74b5e Merge pull request #1401 from velovix/issue-1387_client-conf-as-argument
Pass client configuration as an argument
2019-08-22 00:14:56 +08:00
Tyler Compton
666f122a72 Pass client configuration as an argument
The ClientCommonConf, configuration file path, and server UDP port are
now passed around as arguments instead of being shared between
components as global variables. This allows for multiple clients to
exist in the same process, and allows client.Session to be used as a
library more easily.
2019-08-20 14:08:01 -07:00
fatedier
f999c8a87e Merge pull request #1396 from velovix/issue-1387_server-conf-as-argument
Pass server configuration as an argument
2019-08-21 00:30:01 +08:00
fatedier
90a32ab75d Merge pull request #1399 from renmu123/dev
fix one punctuation in Chinese docs
2019-08-20 17:24:05 +08:00
renmu123
0713fd28da fix one punctuation in Chinese docs 2019-08-20 17:17:17 +08:00
fatedier
f5b33e6de8 Merge pull request #1397 from velovix/issue-1387_client-assets-dir
Add an "assets_dir" option for frpc
2019-08-20 11:06:12 +08:00
fatedier
fc6043bb4d Merge pull request #1395 from velovix/issue-1387_load-assets-on-demand
Load assets for dashboard/admin panel on demand
2019-08-20 11:03:05 +08:00
Tyler Compton
bc46e3330a Add an "assets_dir" option for frpc
This option allows users to specify where they want assets to be loaded
from, like the "assets_dir" option that already exists for frps. This
allows library users to use the admin panel without having to bundle
assets with statik.
2019-08-19 16:51:03 -07:00
Tyler Compton
5fc7b3ceb5 Remove global ServerService variable
This variable didn't seem to be used anyway, so no further changes were
required.
2019-08-19 16:23:33 -07:00
Tyler Compton
6277af4790 Pass server configuration as an argument
The ServerCommonConf is now passed around as an argument instead of
being shared between components as a global variable. This allows for
more natural interaction with server.Session as a library and allows for
multiple servers to co-exist within the same process.

Related: #1387
2019-08-19 15:52:08 -07:00
Tyler Compton
00bd0a8af4 Load assets for dashboard/admin panel on demand
The client and server services now only attempt to load assets if the
dashboard or admin panel are enabled. This change makes it possible to
use FRP as a library without having to manage assets. If a library user
wants to start a server with the dashboard enabled, they will need to
set the DashboardPort and AssetsDir fields of ServerCommonConf.
2019-08-19 10:10:50 -07:00
fatedier
a415573e45 Merge pull request #1390 from fatedier/new
support disable_log_color for console mode
2019-08-15 23:50:17 +08:00
fatedier
e68012858e ci: add http load balancing test cases 2019-08-15 23:36:05 +08:00
fatedier
ca8a5b753c Merge pull request #1382 from Hurricanezwf/feature/add-httpstohttp-header
add support for customized http header when using https2http plugin
2019-08-14 21:00:16 +08:00
zhouwenfeng
d1f4ac0f2d add README for this feature 2019-08-14 20:44:44 +08:00
zhouwenfeng
ff357882ac use Set() instead of Add() when attaching additional headers 2019-08-14 20:36:10 +08:00
zhouwenfeng
934ac2b836 add support for add http header when using https2http plugin 2019-08-13 21:14:06 +08:00
fatedier
1ad50d5982 bump version to v0.29.0 2019-08-12 00:48:50 +08:00
fatedier
388b016842 support disable_log_color for console mode 2019-08-12 00:47:35 +08:00
fatedier
134a46c00b Merge pull request #1369 from fatedier/dev
bump version to v0.28.2
2019-08-09 12:59:13 +08:00
fatedier
50796643fb Merge pull request #1368 from fatedier/new
fix health check bug, fix #1367
2019-08-09 12:52:46 +08:00
fatedier
b1838b1d5e bump version to v0.28.2 2019-08-09 12:50:33 +08:00
fatedier
757b3613fe fix health check bug, fix #1367 2019-08-09 12:47:27 +08:00
fatedier
ae08811636 Merge pull request #1364 from fatedier/dev
bump version to v0.28.1 and remove support for go1.11
2019-08-08 17:32:57 +08:00
fatedier
b657c0fe09 Merge pull request #1358 from fatedier/new
update vendor packages
2019-08-06 18:59:03 +08:00
fatedier
84df71047c no support for go1.11 2019-08-06 18:53:32 +08:00
fatedier
abc6d720d0 vendor update github.com/gorilla/websocket 2019-08-06 18:53:15 +08:00
fatedier
80154639e3 fix 2019-08-06 17:29:35 +08:00
fatedier
f2117d8331 bump version to v0.28.1 2019-08-06 16:51:55 +08:00
fatedier
261be6a7b7 add vendor files 2019-08-06 16:50:54 +08:00
fatedier
b53a2c1ed9 update reverseproxy from std libraries 2019-08-06 16:49:22 +08:00
fatedier
ee0df07a3c vendor update 2019-08-03 23:23:00 +08:00
fatedier
4e363eca2b update version of github.com/gorilla/mux 2019-08-03 23:22:22 +08:00
fatedier
4277405c0e update vendors 2019-08-03 18:49:55 +08:00
fatedier
6a99f0caf7 update testify and kcp-go 2019-08-03 18:44:11 +08:00
fatedier
394af08561 close session in login() 2019-08-03 16:43:21 +08:00
fatedier
6451583e60 Merge pull request #1349 from fatedier/dev
bump version to v0.28.0
2019-08-01 14:04:55 +08:00
fatedier
30cb0a3ab0 Merge pull request #1344 from fatedier/new
support http load balancing
2019-08-01 13:59:41 +08:00
fatedier
5680a88267 fix connection leak when login_fail_exit is false, fix #1335 2019-07-31 00:50:38 +08:00
fatedier
6b089858db bump version to v0.28.0 2019-07-31 00:47:50 +08:00
fatedier
b3ed863021 support http load balancing 2019-07-31 00:41:58 +08:00
fatedier
5796c27ed5 doc: update 2019-07-31 00:41:43 +08:00
fatedier
310e8dd768 Merge pull request #1331 from muesli/typo-fixes
Fixed typos in comments
2019-07-19 18:50:29 +08:00
Christian Muehlhaeuser
0b40ac2dbc Fixed typos in comments
Just nitpicky typo fixes.
2019-07-19 12:40:14 +02:00
fatedier
f22c8e0882 Merge pull request #1323 from skyrocknroll/skyrocknroll-patch-1
Typo
2019-07-15 14:03:57 +08:00
Yuvaraj L
a388bb2c95 Typo
English Grammar Typo
2019-07-15 01:05:43 +05:30
fatedier
e611c44dea Merge pull request #1322 from fatedier/dev
bump version to v0.27.1
2019-07-14 20:00:31 +08:00
fatedier
8e36e2bb67 Merge pull request #1320 from fatedier/new
add read timeout for TLS check operation
2019-07-14 10:57:22 +08:00
fatedier
541ad8d899 update ISSUE_TEMPLATE 2019-07-12 17:59:45 +08:00
fatedier
17cc0735d1 add read timeout for TLS check operation 2019-07-12 17:11:03 +08:00
fatedier
fd336a5503 Merge pull request #1275 from Arugal/dev
replace the _
2019-06-02 21:22:29 +08:00
zhangwei
802d1c1861 replace the _ 2019-06-01 10:09:13 +08:00
fatedier
65fe0a1179 Merge pull request #1271 from jiajunhuang/resp_body_should_be_closed
resp.Body must be closed after function return
2019-06-01 00:44:03 +08:00
Jiajun Huang
2d24879fa3 fix 2019-05-31 15:56:05 +08:00
Jiajun Huang
75383a95b3 resp.Body must be closed after function return
whether it's success or fail, otherwise it will cause memory leak
ref: https://golang.org/pkg/net/http/
2019-05-30 22:32:36 +08:00
fatedier
95444ea46b Merge pull request #1216 from fatedier/dev
bump version to v0.27.0
2019-04-25 14:41:05 +08:00
fatedier
9f9c01b520 Merge pull request #1215 from fatedier/new
merge new features
2019-04-25 14:38:05 +08:00
fatedier
285d1eba0d bump version to v0.27.0 2019-04-25 12:31:20 +08:00
fatedier
0dfd3a421c frps: support custom_404_page 2019-04-25 12:29:34 +08:00
fatedier
6a1f15b25e support proxy protocol in unix_domain_socket 2019-04-25 12:01:57 +08:00
Gihan
9f47c324b7 api error fix due to status code 2019-04-25 09:54:56 +08:00
fatedier
f0df6084af Merge pull request #1206 from bgkavinga/master
api error fix due to status code
2019-04-24 12:06:13 +08:00
Gihan
879ca47590 api error fix due to status code 2019-04-21 13:29:35 +05:30
fatedier
6a7efc81c9 Merge pull request #1191 from fatedier/dev
Bump version to v0.26.0
2019-04-10 14:02:03 +08:00
fatedier
12c5c553c3 Merge pull request #1190 from fatedier/new
new features
2019-04-10 13:57:59 +08:00
fatedier
988e9b1de3 update doc 2019-04-10 13:51:05 +08:00
fatedier
db6bbc5187 frpc: new plugin https2http 2019-04-10 12:02:22 +08:00
fatedier
c67b4e7b94 vendor: add packages 2019-04-10 10:53:45 +08:00
fatedier
b7a73d3469 support proxy protocol for type http 2019-04-10 10:51:01 +08:00
fatedier
7f9d88c10a fix 2019-04-08 15:39:14 +08:00
fatedier
79237d2b94 bump version to v0.26.0 2019-03-29 19:40:25 +08:00
fatedier
9c4ec56491 support proxy protocol 2019-03-29 19:01:18 +08:00
fatedier
74a8752570 fix route conflict 2019-03-29 17:12:44 +08:00
fatedier
a8ab4c5003 Merge pull request #1160 from fatedier/dev
bump version to v0.25.3, fix #1159
2019-03-26 19:33:39 +08:00
fatedier
9cee263c91 fix panic error when reconnecting using tls 2019-03-26 19:28:24 +08:00
fatedier
c6bf6f59e6 update package.sh 2019-03-25 18:38:02 +08:00
fatedier
4b7aef2196 Merge pull request #1157 from fatedier/dev
bump version to v0.25.2
2019-03-25 18:26:33 +08:00
fatedier
f6d0046b5a bump version to v0.25.2 2019-03-25 18:22:35 +08:00
fatedier
84363266d2 Merge pull request #1156 from fatedier/new
fix health check unclosed resp body
2019-03-25 18:22:15 +08:00
fatedier
9ac8f2a047 fix health check unclosed resp body, fix #1155 2019-03-25 18:17:33 +08:00
fatedier
b2b55533b8 Merge pull request #1147 from a-wing/dev
Add systemd unit
2019-03-21 17:46:36 +08:00
a-wing
a4cfab689a Add systemd unit
Ref https://github.com/fatedier/frp/issues/1058
Ref https://aur.archlinux.org/packages/frp/

Co-authored-by: vimsucks <dev@vimsucks.com>
2019-03-21 11:38:34 +08:00
fatedier
c7df39074c Merge pull request #1140 from fatedier/kcp
update kcp-go package
2019-03-17 17:15:44 +08:00
fatedier
fdcdccb0c2 update kcp-go package 2019-03-17 17:09:54 +08:00
fatedier
e945c1667a Merge pull request #1138 from fatedier/dev
bump version to v0.25.1
2019-03-15 17:05:09 +08:00
fatedier
87a4de4370 Merge pull request #1137 from fatedier/fix
some fixes
2019-03-15 17:00:59 +08:00
fatedier
e1e2913b77 bump version to v0.25.1 2019-03-15 16:46:22 +08:00
fatedier
9be24db410 support multilevel subdomain, fix #1132 2019-03-15 16:22:41 +08:00
fatedier
6b61cb3742 fix frps --log_file useless, fix #1125 2019-03-15 15:37:17 +08:00
fatedier
90b7f2080f Merge pull request #1122 from fatedier/dev
bump version to v0.25.0
2019-03-11 17:40:39 +08:00
fatedier
d1f1c72a55 update ci 2019-03-11 17:11:26 +08:00
fatedier
1925847ef8 update doc 2019-03-11 16:24:54 +08:00
fatedier
8b216b0ca9 Merge pull request #1121 from fatedier/new
new feature
2019-03-11 16:05:18 +08:00
fatedier
dbfeea99f3 update .travis.yml, support go1.12 2019-03-11 16:02:45 +08:00
fatedier
5e64bbfa7c vendor: update package 2019-03-11 15:54:55 +08:00
fatedier
e691a40260 improve the stability of xtcp 2019-03-11 15:53:58 +08:00
fatedier
d812488767 support tls connection 2019-03-11 14:14:31 +08:00
fatedier
3c03690ab7 Merge pull request #1112 from fatedier/p2p
xtcp: wrap yamux on kcp connections, fix #1103
2019-03-05 11:27:15 +08:00
fatedier
3df27b9c04 xtcp: wrap yamux on kcp connections 2019-03-05 11:18:17 +08:00
fatedier
ba45d29b7c fix xtcp cmd 2019-03-03 23:44:44 +08:00
fatedier
3cf83f57a8 update yamux version 2019-03-03 22:29:08 +08:00
fatedier
03e4318d79 Merge pull request #1107 from likev/patch-1
Update instruction of 'Rewriting the Host Header'
2019-03-03 21:57:24 +08:00
xufanglu
178d134f46 Update instruction of 'Rewriting the Host Header'
Update instruction of 'Rewriting the Host Header' in README.md
2019-03-02 21:33:23 +08:00
fatedier
cbf9c731a0 Merge pull request #1088 from fatedier/dev
bump version to v0.24.1
2019-02-12 15:10:43 +08:00
fatedier
de4bfcc43c bump version to v0.24.1 2019-02-12 15:03:40 +08:00
fatedier
9737978f28 Merge pull request #1087 from fatedier/fix
fix PUT /api/config without token
2019-02-12 15:03:00 +08:00
fatedier
5bc7fe2cea fix PUT /api/config without token 2019-02-12 14:59:30 +08:00
fatedier
65d8fe37c5 Merge pull request #1081 from fatedier/dev
bump version to v0.24.0
2019-02-11 14:46:23 +08:00
fatedier
1723d7b651 Merge pull request #1080 from fatedier/client
frpc: support admin UI
2019-02-11 14:42:48 +08:00
fatedier
2481dfab64 fix api 2019-02-11 14:37:52 +08:00
fatedier
95a881a7d3 frps: update server dashboard_api 2019-02-11 11:42:07 +08:00
fatedier
fe403ab328 frpc: update admin_api 2019-02-11 11:26:06 +08:00
fatedier
66555dbb00 frpc admin: not allow empty PUT /api/config body 2019-02-02 11:46:53 +08:00
fatedier
7f9ea48405 bump version to v0.24.0 2019-02-01 19:28:38 +08:00
fatedier
96d7e2da6f add admin UI for frpc 2019-02-01 19:28:05 +08:00
fatedier
d879b8208b frpc: add api PUT api/config 2019-01-31 18:35:44 +08:00
fatedier
3585e456d4 frpc: add api GET api/config 2019-01-31 17:17:34 +08:00
fatedier
1de8c3fc87 Merge pull request #1069 from fatedier/vet
go vet & golint
2019-01-31 16:59:03 +08:00
fatedier
bbab3fe9ca go lint 2019-01-31 16:54:46 +08:00
fatedier
48990da22e go vet 2019-01-31 16:49:23 +08:00
fatedier
5543fc2a9a Merge pull request #1068 from fatedier/dev
bump version to v0.23.3
2019-01-30 11:38:39 +08:00
fatedier
c41de6fd28 bump version 2019-01-30 11:22:25 +08:00
fatedier
8c8fd9790e Merge pull request #1067 from fatedier/fix
frpc: reload proxy not saved after reconnecting
2019-01-30 11:22:41 +08:00
fatedier
5a7ef3be74 frpc: reload proxy not saved after reconnecting 2019-01-30 11:12:28 +08:00
fatedier
d9b5e0bde0 Merge pull request #1061 from fatedier/dev
bump version to v0.23.2
2019-01-26 22:11:57 +08:00
fatedier
05ca72dbf0 bump version 2019-01-26 22:05:58 +08:00
fatedier
ef6f8bbf6c Merge pull request #1060 from fatedier/new
fix control delete error
2019-01-26 22:05:38 +08:00
fatedier
70ac7d3d11 fix control delete error 2019-01-26 21:36:24 +08:00
fatedier
385c4d3dd5 frpc/cmd: update protocol description 2019-01-26 12:52:12 +08:00
fatedier
5e1983f7ed change from dep to go mod 2019-01-26 12:39:03 +08:00
fatedier
516cdbddb0 support go mod 2019-01-16 20:48:47 +08:00
fatedier
3954ceb93b Merge pull request #1049 from fatedier/dev
bump version to v0.23.1
2019-01-16 14:40:29 +08:00
fatedier
2061ef11c8 bump version to v0.23.1 2019-01-16 14:35:22 +08:00
fatedier
71cbe5decc Merge pull request #1048 from 442hz/frpc-fixup-sub-command-status-and-reload-ini-parse-problem
frpc: fixup ini config parse problem in sub command `status` and `rel…
2019-01-16 14:34:20 +08:00
荒野無燈
a2ccb6c190 frpc: fixup ini config parse problem in sub command status and reload. 2019-01-16 13:12:25 +08:00
fatedier
5bdf530b7e Merge pull request #1045 from fatedier/dev
merge dev -> master
2019-01-15 19:45:00 +08:00
fatedier
5177570da4 Merge pull request #1043 from 442hz/fixup-dashboard-api
frps dashboard api: fixup getProxyStatsByType no data return
2019-01-15 19:37:47 +08:00
荒野無燈
0bd8f9cd9b frps dashboard api: fixup getProxyStatsByType no data return 2019-01-15 19:22:38 +08:00
fatedier
649a2f2457 Merge pull request #1042 from fatedier/dev
release v0.23.0
2019-01-15 16:01:24 +08:00
fatedier
54916793f9 Merge pull request #1041 from fatedier/update
optimize code
2019-01-15 15:58:06 +08:00
fatedier
0b06c1c821 doc: add zsxq pic 2019-01-15 15:27:27 +08:00
fatedier
bbc6f1687d doc: add health check 2019-01-15 11:29:41 +08:00
fatedier
b250342e27 web: remove auth timeout 2019-01-15 00:32:11 +08:00
fatedier
f76deb8898 frps: remove auth timeout 2019-01-15 00:22:13 +08:00
fatedier
611d063e1f server: adjust code structure 2019-01-15 00:11:08 +08:00
fatedier
0c7d778896 frps: optimize code 2019-01-11 14:34:50 +08:00
fatedier
7c21906884 improve kcp shutdown 2018-12-11 15:17:36 +08:00
fatedier
a4106ec4b7 Merge pull request #1006 from fatedier/doc
doc: add conf template
2018-12-11 12:05:23 +08:00
fatedier
655c52f9ce doc: add conf template 2018-12-11 12:01:36 +08:00
fatedier
25cfda5768 conf: support render configure file using environment variables 2018-12-11 11:46:12 +08:00
fatedier
b61cb14c8f remove docker support 2018-12-10 14:21:33 +08:00
fatedier
d5ce4d4916 Merge pull request #1000 from fatedier/dev
bump version to v0.22.0
2018-12-09 22:35:26 +08:00
fatedier
4f0ee5980d Merge pull request #998 from fatedier/health
frpc: support health check
2018-12-09 22:10:13 +08:00
fatedier
146956ac6e bump version to v0.22.0 2018-12-09 22:06:56 +08:00
fatedier
35278ad17f mv folders 2018-12-09 22:06:22 +08:00
fatedier
aea9f9fbcc health: add more ci cases and fix bugs 2018-12-09 21:56:46 +08:00
fatedier
08c17c3247 frpc: support health check 2018-12-07 18:40:17 +08:00
fatedier
6934a18f95 Merge pull request #981 from bighamx/patch-1
Update README_zh.md
2018-11-21 14:56:44 +08:00
SpanishBigHam
5165b0821f Update README_zh.md
fix typo
2018-11-21 14:51:33 +08:00
fatedier
0aec869513 Merge pull request #974 from acrogenesis/patch-1
Fix setting basic auth password doc
2018-11-19 10:55:21 +08:00
Adrian Rangel
826b9db5f2 fix setting basic auth password doc 2018-11-15 22:53:35 -06:00
fatedier
89d1a1fb2b Merge pull request #968 from fatedier/health
support health check and code refactor
2018-11-09 10:06:47 +08:00
fatedier
450e0b7148 Merge pull request #967 from fatedier/fix
update golib, fix #959
2018-11-08 09:50:35 +08:00
fatedier
a1ac002694 update golib, fix #959 2018-11-07 21:10:47 +08:00
fatedier
951d33d47c Merge pull request #966 from fatedier/new_health
refactor code
2018-11-06 23:16:56 +08:00
fatedier
b33ea9274c client/control: refactor code 2018-11-06 18:35:05 +08:00
fatedier
3b3f3dc2b5 Merge pull request #947 from HaraldNordgren/master
Bump Travis versions
2018-10-29 10:16:58 +08:00
fatedier
ec0b59732c Merge pull request #955 from muesli/readme-fixes
Grammar fixes and improved README
2018-10-25 10:43:02 +08:00
Christian Muehlhaeuser
bae1ecdc69 Grammar fixes and improved README
I think this reads nicer and more accurately describes the HTTP reverse proxying.
2018-10-25 00:57:50 +02:00
Harald Nordgren
5c2ab5a749 Bump Travis versions 2018-10-19 21:01:21 +02:00
fatedier
1a8ac148ca fix xtcp visitor panic 2018-10-18 13:55:51 +08:00
fatedier
698219b621 frpc: support health check 2018-09-11 18:33:02 +08:00
fatedier
229740524e Merge pull request #891 from mark86092/bump-docker-to-1-10
Upgrade dockerfile golang to 1.10
2018-08-12 22:04:49 +08:00
Yen-chi Chen
cbeeac06a5 Upgrade golang to 1.10 2018-08-12 15:03:48 +08:00
fatedier
66a69f873f Merge pull request #890 from fatedier/dev
bump version to v0.21.0
2018-08-12 12:10:16 +08:00
fatedier
fb13774457 version to v0.21.0 2018-08-12 12:04:12 +08:00
fatedier
f14ed87b29 Merge pull request #886 from fatedier/websocket
Connect protocol support websocket
2018-08-10 16:36:02 +08:00
fatedier
07623027bc websocket: fix 2018-08-10 16:31:49 +08:00
fatedier
941ac25648 fix ci 2018-08-10 12:02:38 +08:00
fatedier
f645082d72 vendor: add package golang.org/x/net/websocket 2018-08-10 11:49:33 +08:00
fatedier
7793f55545 websocket: update muxer for websocket 2018-08-10 11:45:48 +08:00
fatedier
ca88b07ecf optimize 2018-08-08 11:18:38 +08:00
fatedier
6e305db4be Merge pull request #882 from 235832289/master
Fix the problem of long connection for more than 30 seconds and disconnection of the server
2018-08-08 11:03:08 +08:00
itcode
9bb08396c7 Fix the problem of long connection for more than 30 seconds and disconnection of the server 2018-08-08 10:52:08 +08:00
fatedier
64136a3b3e Merge pull request #875 from jettyu/jettyu-websocket
websocket protocol
2018-08-06 15:20:09 +08:00
FishFish
b8037475ed websocket protocol 2018-08-05 12:55:31 +08:00
fatedier
082447f517 frpc: support health check 2018-07-16 01:21:29 +08:00
fatedier
cc6486addb add more cmd test 2018-07-12 16:49:16 +08:00
fatedier
57417c83ae add ci case of reload and reconnect 2018-07-12 15:23:34 +08:00
fatedier
d74b45be5d more ci 2018-07-12 00:31:21 +08:00
fatedier
0d02f291e3 refactor ci test 2018-07-11 23:27:47 +08:00
fatedier
42ee536dae add module comments for vgo 2018-06-27 11:46:53 +08:00
fatedier
c33b5152e7 split visitors from proxies and add health check config 2018-06-25 18:22:35 +08:00
fatedier
b6c219aa97 Merge pull request #814 from neohe/dev
update xtcp log info
2018-06-08 22:51:12 +08:00
Neo He
bbc36be052 update xtcp log info 2018-06-08 21:27:58 +08:00
fatedier
f5778349d5 config: fix server_name not using user as prefix, fix #804 2018-06-08 14:44:19 +08:00
fatedier
71603c6d0b Merge pull request #807 from kac-/allowPorts
cmd: frps: allow_ports option
2018-06-08 10:52:05 +08:00
kac-
e64fcce417 cmd: frps: allow_ports option 2018-06-06 18:25:55 +02:00
fatedier
629f2856b1 Merge pull request #801 from fatedier/dev
bump version v0.20.0
2018-06-01 00:15:22 +08:00
fatedier
aeb9f2b64d update golib 2018-05-29 02:08:41 +08:00
fatedier
85dd41c17b Merge pull request #795 from fatedier/lb
support lb
2018-05-26 22:42:03 +08:00
fatedier
102408d37f doc: load balancing 2018-05-26 02:28:13 +08:00
fatedier
495b577819 update group ci 2018-05-23 14:39:12 +08:00
fatedier
f56b49ad3b new feature: load balancing for tcp proxy 2018-05-22 23:59:35 +08:00
fatedier
cb1bf71bef Merge pull request #787 from lonwern/arm64
build linux_arm64 package
2018-05-21 21:43:16 +08:00
fatedier
b9f062bef2 support lb 2018-05-21 21:22:10 +08:00
fatedier
490019fb51 update vendor, fix #788 2018-05-21 21:09:18 +08:00
fatedier
2e497274ba add ci for setting headers 2018-05-20 23:55:22 +08:00
fatedier
cf4136fe99 Merge pull request #786 from fatedier/header
support setting headers in http request
2018-05-20 23:44:19 +08:00
fatedier
b1e9cff622 doc: about headers 2018-05-20 23:41:15 +08:00
fatedier
db2d1fce76 http: support setting headers 2018-05-20 23:22:07 +08:00
lonwern
8579de9d3f build linux_arm64 package 2018-05-20 23:05:29 +08:00
fatedier
0c35273759 Merge pull request #783 from fatedier/stcp
frps dashboard add stcp
2018-05-20 19:09:43 +08:00
fatedier
6eb8146334 frps dashboard add stcp 2018-05-20 19:06:05 +08:00
fatedier
da78e3f52e Merge pull request #781 from fatedier/dev
bump version to 0.19.1
2018-05-19 23:10:17 +08:00
fatedier
e1918f6396 frps add '-t' to set token 2018-05-18 10:58:08 +08:00
fatedier
ad1e32fd2d fix panic error when vhost_http_port is not set but there is a http
proxy, fix #776
2018-05-18 00:21:11 +08:00
fatedier
3726f99b04 fix ci 2018-05-17 00:44:53 +08:00
fatedier
c8a7405992 version to 0.19.1 2018-05-17 00:17:22 +08:00
fatedier
040d198e36 Merge pull request #773 from fatedier/nat
return error quickly if nathole make error
2018-05-17 00:10:53 +08:00
fatedier
1a6cbbb2d2 return error quickly if nathole make error 2018-05-17 00:07:56 +08:00
fatedier
ea79e03bd0 Merge pull request #772 from fatedier/web
update dashboard ui
2018-05-16 23:56:12 +08:00
fatedier
3e349455a0 commands for xtcp, stcp add 'bind_port', fix #767 2018-05-16 23:45:44 +08:00
fatedier
c7a457a045 update web assets 2018-05-16 03:34:43 +08:00
fatedier
0b0d5c982e update web 2018-05-16 02:51:02 +08:00
fatedier
c4f873c07a update web package 2018-05-14 16:17:13 +08:00
fatedier
01b1df2b91 update cmd help info 2018-05-14 15:01:16 +08:00
fatedier
f1bea49314 Merge pull request #766 from fatedier/dev
bump version to 0.19.0
2018-05-14 11:08:46 +08:00
fatedier
2ffae3489b dashboard: more params 2018-05-11 17:25:01 +08:00
fatedier
96b94d9164 dashbaord_api: more info 2018-05-11 17:14:16 +08:00
fatedier
76b04f52d1 udpate doc 2018-05-11 16:54:36 +08:00
fatedier
97db0d187a Merge pull request #760 from fatedier/vendor
move some packages to golib (https://github.com/fatedier/golib)
2018-05-11 16:47:03 +08:00
fatedier
d9aadab4cb vendor: add golib/msg 2018-05-11 16:42:08 +08:00
fatedier
a0fe2fc2c2 vendor: models/msg 2018-05-11 16:37:44 +08:00
fatedier
1464836f05 logs panic debug strace info 2018-05-11 12:05:37 +08:00
fatedier
b2a2037032 change accept connection error loglevel to debug 2018-05-11 10:35:16 +08:00
fatedier
071cbf4b15 vendor: update 2018-05-09 01:05:14 +08:00
fatedier
20fcb58437 vendor: add package golib/net 2018-05-09 00:23:42 +08:00
fatedier
a27e3dda88 vendor: update shutdown 2018-05-08 23:51:13 +08:00
fatedier
1dd7317c06 vendor: add package io 2018-05-08 23:42:04 +08:00
fatedier
58a54bd0fb Merge pull request #755 from king6cong/master
typo fix
2018-05-08 17:12:02 +08:00
king6cong
caec4982cc typo fix 2018-05-08 17:02:49 +08:00
fatedier
dd8f788ca4 use dep instead of glide 2018-05-08 02:40:11 +08:00
fatedier
8a6d6c534a vendor: udpate 2018-05-08 02:13:30 +08:00
fatedier
39089cf262 Merge pull request #750 from fatedier/doc
doc: update
2018-05-07 16:48:52 +08:00
fatedier
55800dc29f doc: update 2018-05-07 16:46:04 +08:00
fatedier
04560c1896 web: translate web interface completely to English, fix #680 2018-05-06 23:39:33 +08:00
fatedier
178efd67f1 Merge pull request #746 from fatedier/mux
http port and https port can be same with frps bind_port
2018-05-06 22:54:30 +08:00
fatedier
6ef5fb6391 Merge pull request #744 from wujingquan/patch-1
Update frps_full.ini
2018-05-06 22:53:59 +08:00
fatedier
1ae43e4d41 plugin: update http_proxy 2018-05-06 22:47:26 +08:00
fatedier
5db605ca02 frps: vhost_http_port and vhost_https_port can be same with frps bind
port
2018-05-06 20:25:52 +08:00
Touch
e087301425 Update frps_full.ini 2018-05-06 04:08:36 +08:00
fatedier
f45283dbdb disable yamux default log 2018-05-05 00:09:39 +08:00
fatedier
b0959b3caa bump version to v0.19.0 2018-05-04 23:14:38 +08:00
fatedier
c5c89a2519 doc: update 2018-05-04 23:12:23 +08:00
fatedier
bebd1db22a Merge pull request #740 from fatedier/socks5
frpc: support connectiong frps by socks5 proxy
2018-05-04 19:15:11 +08:00
fatedier
cd37d22f3b vendor: udpate golang.org/x/net 2018-05-04 18:37:43 +08:00
fatedier
30af32728a frpc: support connectiong frps by socks5 proxy 2018-05-04 18:36:38 +08:00
fatedier
60ecd1d58c cmd: change http_user and http_pwd default value to empty 2018-05-03 10:20:09 +08:00
fatedier
a60be8f562 update Makefile 2018-05-03 00:32:05 +08:00
fatedier
c008b14d0f Merge pull request #735 from fatedier/dev
bump version to v0.18.0
2018-05-02 22:37:09 +08:00
fatedier
853892f3cd change version to v0.18.0 2018-05-02 22:09:15 +08:00
fatedier
e43f9f5850 Merge pull request #734 from fatedier/mux
use yamux instead of smux
2018-05-02 21:57:18 +08:00
fatedier
d5f30ccd6b Merge pull request #726 from shuaihanhungry/develop
do not ignore config parsing error
2018-05-02 21:51:02 +08:00
hanshuai
b87df569e7 do not ignore config parsing error 2018-05-02 20:40:33 +08:00
fatedier
976cf3e9f8 use yamux instead of smux 2018-04-25 02:42:00 +08:00
fatedier
371c401f5b Merge pull request #720 from fatedier/dev
bump version to v0.17.0
2018-04-24 01:58:57 +08:00
fatedier
69919e8ef9 Merge pull request #719 from fatedier/doc
update
2018-04-24 01:55:19 +08:00
fatedier
9abbe33790 typo 2018-04-24 01:51:52 +08:00
fatedier
4a5c00286e doc: update 2018-04-24 01:28:25 +08:00
fatedier
dfb892c8f6 Merge pull request #718 from fatedier/cmd
more cmds
2018-04-23 03:07:14 +08:00
fatedier
461c4c18fd update doc 2018-04-23 03:04:33 +08:00
fatedier
00b9ba95ae frpc: support specify default dns server, close #700 2018-04-23 02:59:40 +08:00
fatedier
c47aad348d fix 2018-04-23 02:40:25 +08:00
fatedier
4cb4da3afc add package github.com/spf13/cobra 2018-04-23 02:35:50 +08:00
fatedier
c1f57da00d update packages 2018-04-23 02:31:00 +08:00
fatedier
fe187eb8ec remove package github.com/docopt/docopt-go 2018-04-23 02:15:01 +08:00
fatedier
0f6f674a64 cmd: support more cli command 2018-04-23 02:00:25 +08:00
fatedier
814afbe1f6 Merge pull request #688 from miwee/dashboard_api_client_status
dashboard_api for getting a client status by name
2018-04-10 17:57:26 +08:00
miwee
3fde9176c9 dashboard_api for getting a client status by name 2018-04-04 12:07:20 +05:30
fatedier
af7cca1a93 Merge pull request #685 from toby1991/dev
fix https://github.com/fatedier/frp/issues/684
2018-04-04 11:32:48 +08:00
toby1991
7dd28a14aa fix https://github.com/fatedier/frp/issues/684
#684 Cannot build from Dockerfile
2018-04-04 11:06:47 +08:00
fatedier
1325c59a4c Merge pull request #672 from fatedier/dev
bump version to v0.16.1
2018-03-21 18:09:39 +08:00
fatedier
82dc1e924f vhost: typo fix 2018-03-21 18:06:43 +08:00
fatedier
3166bdf3f0 bump version to v0.16.1 2018-03-21 18:00:31 +08:00
fatedier
8af70c8822 update go version to go1.10 2018-03-21 11:52:11 +08:00
fatedier
87763e8251 Merge pull request #670 from fatedier/new
some fix
2018-03-21 11:45:48 +08:00
fatedier
e9241aeb94 udp proxy: fix #652 2018-03-19 20:22:15 +08:00
fatedier
2eaf134042 Merge pull request #646 from travisghansen/dev
build freebsd packages
2018-02-27 23:14:15 +08:00
Travis Glenn Hansen
1739e012d6 build freebsd packages 2018-02-26 21:00:55 -07:00
fatedier
9e8980429f typo 2018-02-07 11:39:30 +08:00
fatedier
1d0865ca49 statsConn: avoid repetition of close function 2018-02-01 11:15:35 +08:00
fatedier
5c9909aeef typo 2018-01-30 22:07:16 +08:00
fatedier
456ce09061 Merge pull request #630 from fatedier/dev
bump version to v0.16.0
2018-01-30 00:04:02 +08:00
fatedier
ffc13b704a update version 2018-01-30 00:00:05 +08:00
fatedier
5d239127bb Merge pull request #629 from fatedier/new
new feature
2018-01-29 23:58:55 +08:00
fatedier
9b990adf96 frpc: add proxy status 'wait start' 2018-01-29 23:51:46 +08:00
fatedier
44e8108910 ci: add test case for range ports mapping 2018-01-29 23:13:10 +08:00
fatedier
1c35e9a0c6 doc: update 2018-01-29 23:05:17 +08:00
fatedier
8e719ff0ff frps: new params max_ports_per_client 2018-01-26 14:56:55 +08:00
fatedier
637ddbce1f frpc: udpate proxies check and start logic 2018-01-26 00:23:48 +08:00
fatedier
ce8fde793c new feature: range section for mapping range ports 2018-01-25 23:05:07 +08:00
fatedier
eede31c064 doc: about static_file plugin 2018-01-24 23:27:03 +08:00
fatedier
41c41789b6 plugin: socks5 support user password auth, close #484 2018-01-24 23:06:38 +08:00
fatedier
68dfc89bce plugin: new plugin static_file for getting files by http protocol 2018-01-24 17:49:13 +08:00
fatedier
8690075c0c Merge pull request #616 from fatedier/dev
bump version to v0.15.1
2018-01-23 17:33:48 +08:00
fatedier
33d8816ced version: to v0.15.1 2018-01-23 17:28:00 +08:00
fatedier
90cd25ac21 Merge pull request #615 from fatedier/ws
fix websocket and plugin http_proxy
2018-01-23 17:25:32 +08:00
fatedier
ff28668cf2 ci: add plugin http_proxy test case 2018-01-23 17:11:59 +08:00
fatedier
a6f2736b80 fix plugin http_proxy error 2018-01-23 16:31:59 +08:00
fatedier
902f6f84a5 ci: add test for websocket 2018-01-23 14:49:04 +08:00
fatedier
cf9193a429 newhttp: support websocket 2018-01-23 01:29:52 +08:00
fatedier
3f64d73ea9 ci: add subdomain test case 2018-01-22 14:16:46 +08:00
fatedier
a77c7e8625 server: change MIN_PORT from 1024 to 1 2018-01-22 11:48:31 +08:00
fatedier
14733dd109 Merge pull request #608 from fatedier/dev
bump version to 0.15.0
2018-01-18 16:49:55 +08:00
fatedier
74b75e8c57 Merge pull request #607 from fatedier/doc
doc: update
2018-01-18 16:46:33 +08:00
fatedier
63e6e0dc92 doc: update 2018-01-18 16:43:03 +08:00
fatedier
4d4a738aa9 web: update assets 2018-01-18 15:26:30 +08:00
fatedier
1ed130e704 version: to v0.15.0 2018-01-18 15:08:16 +08:00
fatedier
2e773d550b Merge pull request #606 from fatedier/test
tests: more ci case
2018-01-18 15:04:03 +08:00
fatedier
e155ff056e tests: more ci case 2018-01-18 14:53:44 +08:00
fatedier
37210d9983 Merge branch 'dev' of github.com:fatedier/frp into dev 2018-01-18 00:46:21 +08:00
fatedier
338d5bae37 fix panic when using socks5 plugin with encryption and compression, fix #446 2018-01-18 00:45:11 +08:00
fatedier
3e62198612 Merge pull request #604 from fatedier/http
fix new http no traffic stats, fix #590
2018-01-17 23:28:48 +08:00
fatedier
4f7dfcdb31 fix new http no traffic stats, fix #590 2018-01-17 23:17:15 +08:00
fatedier
5b08201e5d Merge pull request #603 from fatedier/test
add test cases and new feature assgin a random port if remote_port is 0
2018-01-17 22:45:02 +08:00
fatedier
b2c846664d new feature: assign a random port if remote_port is 0 in type tcp and
udp
2018-01-17 22:18:34 +08:00
fatedier
3f6799c06a add remoteAddr in NewProxyResp message 2018-01-17 15:01:26 +08:00
fatedier
9a5f0c23c4 fix ci 2018-01-17 01:18:40 +08:00
fatedier
afde0c515c packages: add package github.com/rodaine/table 2018-01-17 01:15:34 +08:00
fatedier
584e098e8e frpc: add status command 2018-01-17 01:09:33 +08:00
fatedier
37395b3ef5 Merge pull request #596 from NemoAlex/patch-2
Use sans-serif font in web
2018-01-10 10:36:28 +08:00
NemoAlex
43fb3f3ff7 Use sans-serif font in web 2018-01-08 13:56:51 +08:00
fatedier
82b127494c Merge pull request #576 from gtt116/master
Close connection if frpc can't connection to local server
2017-12-26 14:51:02 +08:00
gtt116
4d79648657 Close connection if frpc can't connection to local server
Now, when frpc can't connect to local server it leaves the connection alone, the patch fix it.

Fixed #575
2017-12-26 14:39:07 +08:00
fatedier
3bb404dfb5 more test case 2017-12-18 19:35:09 +08:00
fatedier
ff4bdec3f7 add test case 2017-12-16 23:59:46 +08:00
fatedier
69f8b08ac0 update role error log info 2017-12-16 21:56:13 +08:00
fatedier
d873df5ca8 let role default value to 'server' 2017-12-15 11:40:08 +08:00
fatedier
a384bf5580 Merge pull request #564 from fatedier/dev
bump version to v0.14.1
2017-12-14 22:28:25 +08:00
fatedier
92046a7ca2 Merge pull request #561 from fatedier/http
improve http vhost package
2017-12-13 23:48:18 +08:00
fatedier
4cc5ddc012 newhttp support BasicAuth 2017-12-13 23:44:27 +08:00
fatedier
46358d466d support encryption and compression in new http reverser proxy 2017-12-13 04:28:58 +08:00
fatedier
7da61f004b improve http vhost package 2017-12-13 03:27:43 +08:00
fatedier
63037f1c65 typo fix 2017-12-11 22:46:45 +08:00
fatedier
cc160995da improve error role log info 2017-12-11 16:21:17 +08:00
fatedier
de48d97cb2 fix kcp port print error 2017-12-11 01:36:47 +08:00
fatedier
1a6a179b68 visitor: fix panic 2017-12-05 22:26:53 +08:00
fatedier
3a2946a2ff Merge pull request #549 from fatedier/dev
bump version to v0.14.0
2017-12-05 01:42:00 +08:00
fatedier
ae9a4623d9 Merge pull request #548 from fatedier/doc
update doc and fix vistor -> visitor
2017-12-05 01:38:03 +08:00
fatedier
bd1e9a3010 update doc and fix vistor -> visitor 2017-12-05 01:34:33 +08:00
fatedier
92fff5c191 Merge pull request #539 from timerever/dev
add custom dashboard bind address
2017-11-29 10:34:36 +08:00
timerever
8c65b337ca add custom dashboard bind address 2017-11-28 15:56:34 +08:00
fatedier
0f1005ff61 using glide 2017-11-01 16:21:57 +08:00
fatedier
ad858a0d32 prevent sending on a closed channel in vhost package, fix #502 2017-11-01 10:51:30 +08:00
fatedier
1e905839f0 update kcp connection options 2017-10-25 03:02:25 +08:00
fatedier
bf50f932d9 update version to v0.14.0 2017-10-25 02:55:36 +08:00
fatedier
673047be2c Merge pull request #496 from fatedier/0.14
xtcp for p2p communication
2017-10-24 13:54:32 -05:00
fatedier
fa2b9a836c fix xtcp encryption 2017-10-25 02:49:56 +08:00
fatedier
9e0fd0c4ef add packages 2017-10-25 02:29:04 +08:00
fatedier
0559865fe5 support xtcp for making nat hole 2017-10-25 01:27:04 +08:00
fatedier
4fc85a36c2 Merge pull request #486 from xiaox0321/patch-1
Update version.go
2017-10-20 04:12:47 -05:00
xiaox0321
3f1174a519 Update version.go
Optimize duplicate code
2017-10-20 15:58:03 +08:00
fatedier
bcbdfcb99b Merge pull request #473 from Hyduan/master
doc: fix spelling error
2017-09-27 11:55:17 -05:00
Hyduan
df046bdeeb doc: fix spelling error 2017-09-26 21:06:28 +08:00
fatedier
f83447c652 Merge pull request #461 from dvrkps/patch-1
travis: add 1.x to go versions
2017-09-11 06:04:14 -05:00
Davor Kapsa
9ae69b4aac travis: add 1.x to go versions 2017-09-11 12:41:33 +02:00
fatedier
c48a89731a Merge pull request #454 from GeorgeYuen/diamondyuan
add Dockerfile_multiple_build
2017-09-06 01:07:36 -05:00
袁凡迪
36b58ab60c add Dockerfile_multiple_build 2017-09-06 12:51:29 +08:00
fatedier
6320f15a7c typo for default config file name used for frpc 2017-07-19 22:56:12 +08:00
fatedier
066172e9c1 Merge pull request #403 from fatedier/dev
bump version to v0.13.0
2017-07-16 13:20:42 -05:00
fatedier
d5931758b6 fix user in reload command 2017-07-17 02:14:30 +08:00
fatedier
c75c3acd21 Merge pull request #402 from fatedier/doc
update doc for v0.13.0
2017-07-16 13:12:06 -05:00
fatedier
0208ecd1d9 update doc for v0.13.0 2017-07-17 02:09:51 +08:00
fatedier
23e9845e65 Merge pull request #401 from fatedier/0.13
merge 0.13
2017-07-14 23:13:42 +08:00
fatedier
2b1ba3a946 update conf 2017-07-13 12:01:46 +08:00
fatedier
ee9ddf52cd frpc: support --reload command 2017-07-13 02:30:25 +08:00
fatedier
d246400a71 frpc: add admin server for reload configure file 2017-07-13 02:20:49 +08:00
fatedier
f63a4f0cdd frps: new parameter 'proxy_bind_addr' 2017-07-05 01:40:01 +08:00
fatedier
b743b5aaed Merge pull request #390 from lukazh/patch-1
Update README.md
2017-07-05 01:27:41 +08:00
Lukaz
9d9416ab94 Update README.md
fix a typo
2017-07-04 23:05:24 +08:00
fatedier
c081df40e1 vendor: add github.com/armon/go-socks5 2017-07-01 16:09:09 +08:00
fatedier
fe32a7c4bb doc: update 2017-07-01 16:03:13 +08:00
fatedier
7bb8c10647 plugin: add socks5 plugin 2017-07-01 15:56:48 +08:00
fatedier
0752508469 vhost: a bug fix of reading request 2017-07-01 12:13:44 +08:00
fatedier
4cc1663a5f vhost: add real ip in first request of one connection
1. fix #248 host_header_rewrite bug
2. close #270, #127
2017-07-01 01:54:37 +08:00
fatedier
b55a24a27e update mutex used in frpc control 2017-06-27 23:31:02 +08:00
fatedier
aede4e54f8 close all proxies if protocol = kcp 2017-06-27 01:59:30 +08:00
fatedier
b811a620c3 vhost: fix 404 page 2017-06-26 22:24:47 +08:00
fatedier
07fe05a9d5 update version to v0.13.0 2017-06-26 20:57:10 +08:00
fatedier
171bc8dd22 new proxy type: stcp(secret tcp) 2017-06-26 03:02:33 +08:00
fatedier
9c175d4eb5 Merge pull request #380 from IanSmith123/fixbug
fix backquote
2017-06-24 13:19:43 +08:00
Iansmith's win10
9f736558e2 fix backquote 2017-06-24 12:17:09 +08:00
fatedier
8f071dd2c2 Merge pull request #375 from fangqiuming/fangqiuming-patch-1
Fix dockerfile
2017-06-21 18:50:47 +08:00
方秋鸣
bcaf51a6ad Fix dockerfile
Fix incorrect filenames
2017-06-21 14:46:24 +08:00
fatedier
ad3cf9a64a Merge pull request #372 from fatedier/dev
bump verson to v0.12.0
2017-06-19 21:36:51 +08:00
fatedier
e3fc73dbc5 update doc 2017-06-17 18:01:08 +08:00
fatedier
f884e894f2 Merge pull request #363 from fatedier/doc
update doc
2017-06-13 12:44:47 -05:00
fatedier
d57ed7d3d8 update doc 2017-06-14 01:40:20 +08:00
fatedier
a2c318d24c update kcp mode 2017-06-13 23:36:10 +08:00
fatedier
32f8745d61 Merge pull request #360 from fatedier/doc
update doc
2017-06-11 13:46:33 -05:00
fatedier
66120fe49d update doc 2017-06-12 02:41:25 +08:00
fatedier
fca7f42b37 msg: new message CloseProxy 2017-06-11 17:22:05 +08:00
fatedier
5b303f5148 vhost: return 404 not found page if domain doesn't exist 2017-06-11 16:23:00 +08:00
fatedier
2a044c9d6d http_proxy: fix error using encryption or compression 2017-06-09 02:13:24 +08:00
fatedier
70e2aee46d format 2017-06-09 01:33:57 +08:00
fatedier
6742fa2ea8 io: WithCompression resuse snappy.Reader and snappy.Writer 2017-06-08 00:57:33 +08:00
fatedier
511503d34c io.Copy use pool buffer 2017-06-06 18:48:40 +08:00
fatedier
1eaf17fd05 fix ci 2017-06-06 01:39:06 +08:00
fatedier
04f4fd0a81 proto/tcp: fix unexpected close function, fix #332 2017-06-05 23:52:24 +08:00
fatedier
3a4d769bb3 update packages 2017-06-04 20:52:42 +08:00
fatedier
84341b7fcc vendor: add kcp-go package 2017-06-04 20:07:03 +08:00
fatedier
80ba931326 support protocol kcp 2017-06-04 19:56:21 +08:00
fatedier
7ebcc7503a Merge pull request #351 from fatedier/dev
update ISSUE_TEMPLATE
2017-06-03 10:50:40 -05:00
fatedier
74cf57feb3 update ISSUE_TEMPLATE 2017-06-03 23:48:38 +08:00
fatedier
712afed0ab Merge pull request #344 from fatedier/dev
bump version to 0.11.0
2017-06-01 10:46:16 -05:00
fatedier
e29a1330ed dashboard: add proxy start and close time 2017-05-31 02:21:15 +08:00
fatedier
44971c7918 dashboard: use gzip for static files, resolve #333 2017-05-31 01:44:18 +08:00
fatedier
7bc6c72844 dashboard: fix dashboard auth error, fix #339 2017-05-31 01:07:51 +08:00
fatedier
93461e0094 Merge pull request #340 from fatedier/http_proxy
plugin: add http_proxy
2017-05-30 03:12:16 -05:00
fatedier
03d55201b2 plugin: add http_proxy 2017-05-30 16:10:21 +08:00
fatedier
e6d82f3162 Merge pull request #336 from bingtianbaihua/refactoring
add http proxy
2017-05-27 06:27:53 -05:00
ambitioner
1af6276be9 modify 2017-05-26 21:20:54 +08:00
ambitioner
d1f5ec083a add http proxy
add http proxy

add proxy code

add proxy
2017-05-26 20:57:03 +08:00
fatedier
716ec281f6 net: add WrapReadWriteCloserConn 2017-05-26 14:17:46 +08:00
fatedier
67bfae5d23 dashboard: add frps version in Overview page 2017-05-26 12:05:39 +08:00
fatedier
f0dc3ed47b metric: clear useless proxy statistics data 2017-05-26 02:00:00 +08:00
fatedier
08b0885564 Merge pull request #335 from fatedier/start
client: add start params
2017-05-24 12:50:23 -05:00
fatedier
49b503c17b client: add start params
Proxy names specified in 'start' params divided by ',' will be started.
If it is empty or not defined, all proxies will be started.
2017-05-25 01:45:38 +08:00
fatedier
150682ec63 Merge pull request #334 from fatedier/start
client: add login_fail_exit params, default is true
2017-05-24 12:16:02 -05:00
fatedier
4dc96f41c9 client: add login_fail_exit params, default is true
if login_fail_exit is false, when frpc first login to server failed, it
    will continues relogin to server every 30 seconds.
2017-05-25 01:10:58 +08:00
fatedier
6c13b6d37a net: fix HTTP_PROXY include escape characters error, fix #275 2017-05-23 02:10:36 +08:00
fatedier
1c04de380d Merge pull request #328 from fatedier/plugin
new feature plugin and unix domian socket plugin
2017-05-22 01:12:14 -05:00
fatedier
738e5dad22 new feature plugin and unix domian socket plugin 2017-05-22 00:15:18 +08:00
fatedier
6d81e4c8c6 Merge pull request #325 from fatedier/dev
merge dev to master
2017-05-20 00:39:45 -05:00
fatedier
faf584e1dd metric: fix statistics error 2017-05-19 18:38:31 +08:00
fatedier
ba6afd5789 doc: update 2017-05-19 02:59:51 +08:00
fatedier
11260389a1 Merge pull request #322 from fatedier/dev
bump version to 0.10.0
2017-05-18 13:47:43 -05:00
fatedier
b8082e6e08 Merge pull request #321 from fatedier/doc
doc: update
2017-05-18 13:31:46 -05:00
fatedier
7957572ced update 2017-05-19 02:20:10 +08:00
fatedier
c2ff37d0d8 Merge pull request #320 from yuyulei/dev
change allow ports from map to array
2017-05-18 13:05:33 -05:00
fatedier
c67f9d5e76 doc: update 2017-05-19 01:54:57 +08:00
yuyulei
1cc61b60f9 fixs import 2017-05-19 00:00:42 +08:00
yuyulei
9c38baeb9e add ut 2017-05-18 23:58:58 +08:00
yuyulei
84465a7463 change allow ports from map to array 2017-05-18 23:58:46 +08:00
fatedier
3fe50df200 doc: update 2017-05-18 01:59:46 +08:00
fatedier
93d86ca635 vendor: add github.com/xtaci/smux 2017-05-17 22:48:41 +08:00
fatedier
b600a07ec0 support tcp stream multiplexing by smux 2017-05-17 17:47:20 +08:00
fatedier
a5f06489cb update 2017-05-17 16:02:31 +08:00
fatedier
2883d70ea9 dashboard: don't check authentication if user and password is empty 2017-05-15 21:30:13 +08:00
fatedier
3f17837a2c web: support http basic auth in dashboard 2017-05-15 21:18:06 +08:00
fatedier
fd268b5082 Merge pull request #316 from fatedier/refactoring
Refactoring
2017-05-14 11:15:52 -05:00
fatedier
69b09eb8a2 udp: add heartbeat in udp work connection 2017-05-15 00:08:21 +08:00
fatedier
a84dd05351 add connection read timeout 2017-05-10 00:46:42 +08:00
fatedier
71f7caa1ee add more log info 2017-04-25 00:34:14 +08:00
fatedier
5360febd72 update log module 2017-04-21 16:44:51 +08:00
fatedier
1b70f0c4fd update log module 2017-04-21 00:02:21 +08:00
fatedier
5c75efa222 update dashboard fetch api 2017-03-28 01:08:04 +08:00
fatedier
ab4a53965b test: more case 2017-03-28 00:27:30 +08:00
fatedier
a0c83bdb78 test: add test case 2017-03-27 23:46:38 +08:00
fatedier
30aeaf968e fix heartbeat error 2017-03-27 17:25:25 +08:00
fatedier
58d0d41501 fix 2017-03-27 03:53:21 +08:00
fatedier
6a95a63fd4 metric: update date_counter 2017-03-27 03:39:32 +08:00
fatedier
aa185eb9f3 update static files for dashboard 2017-03-27 02:21:37 +08:00
fatedier
d8683a0079 new frps dashboard 2017-03-27 02:15:31 +08:00
fatedier
8b2cde3a30 update for metric 2017-03-27 01:39:05 +08:00
fatedier
634e048d0c dep: add httprouter 2017-03-23 02:06:04 +08:00
fatedier
a4fece3f51 api: add server web api for statistics 2017-03-23 02:01:25 +08:00
fatedier
9e683fe446 update basic auth method 2017-03-21 21:36:48 +08:00
fatedier
54bbfe26b0 support udp 2017-03-13 02:44:47 +08:00
fatedier
a1023fdfc2 add more log 2017-03-12 03:22:35 +08:00
fatedier
b02e1007fb support more proxy type 2017-03-12 01:08:33 +08:00
fatedier
f90028cf96 Use encryption in frp protocol. 2017-03-10 01:44:50 +08:00
fatedier
f83a2a73ab test: update 2017-03-10 00:52:32 +08:00
fatedier
307b74cc13 build: add go1.8 and remove go1.5 2017-03-09 22:55:30 +08:00
fatedier
f00a28598f github: add issue template 2017-03-09 22:53:32 +08:00
fatedier
6ee0b25782 update 2017-03-09 22:45:44 +08:00
fatedier
88083d21e8 start refactoring 2017-03-09 22:44:42 +08:00
fatedier
a22440aade start refactoring 2017-03-09 22:28:31 +08:00
fatedier
b006540141 Merge pull request #266 from fsyxhua/master
Disable the "CGO" mode ,fix the "./frpc not found" problem,fix #262
2017-02-24 02:59:49 -06:00
fatedier
e655f07674 Merge branch 'dev' into master 2017-02-24 02:57:47 -06:00
fsyx_hua
aafa96db58 Disable the "CGO" mode ,fix the "./frpc not found" problem,fix #262 2017-02-24 16:42:36 +08:00
fatedier
1325148cd3 build: support 32 bit of mips, remove darwin 32 2017-01-17 23:53:17 +08:00
fatedier
3f9749488a Merge pull request #238 from fatedier/dev
bump version to 0.9.3
2017-01-17 09:18:45 -06:00
fatedier
f9a0d891a1 pool: fix panic caused by sending to closed channel, fix #237 2017-01-17 23:12:52 +08:00
fatedier
92daa45b68 change version to 0.9.3 2017-01-14 00:48:56 +08:00
fatedier
5f20a22b0d Merge pull request #231 from LitleCarl/dev
Fix Bug for issure, fix #227
2017-01-12 23:38:05 -06:00
Tsao
63be94c611 Fix Bug for closing an exist ProxyServer when another ProxyServer with same name comes. 2017-01-13 13:18:22 +08:00
fatedier
694ee44af6 subdomain: subdomain can be configured in frps.ini, fix #220 2017-01-13 02:23:04 +08:00
fatedier
edb97abf50 Merge pull request #217 from fatedier/dev
bump version to 0.9.2
2017-01-08 09:22:28 -06:00
fatedier
0c10279deb doc: add new contributor 2017-01-08 23:18:25 +08:00
fatedier
1f49510e3e udp proxy: fix reconnect error, fix #209 2017-01-05 23:09:17 +08:00
fatedier
1868b3bafb Merge pull request #215 from bingtianbaihua/dev
modified readme.md
2017-01-05 07:39:15 -06:00
bingtianbaihua
a23521885c modified readme.md 2017-01-05 20:04:26 +08:00
fatedier
c80dcd050d update default value of heartbeat_interval and heartbeat_timeout 2017-01-04 23:09:28 +08:00
fatedier
043ab62587 build: update Makefile.cross-compiles 2017-01-04 22:56:08 +08:00
fatedier
a8969b1901 Merge pull request #207 from bingtianbaihua/master
added heartbeat conf
2016-12-30 04:01:02 -06:00
bingtianbaihua
e26285eefc added heartbeat conf 2016-12-30 17:47:34 +08:00
fatedier
299bd7b5cb frps: fix panic caused by frps closing the nil channel, fix #205 2016-12-29 23:49:39 +08:00
fatedier
90d1384bf7 frps: update 2016-12-28 00:56:55 +08:00
fatedier
a5434e31b7 doc: update 2016-12-28 00:40:45 +08:00
fatedier
044bb692dc Merge pull request #201 from fatedier/dev
bump version to 0.9.1
2016-12-27 10:35:14 -06:00
fatedier
34b98dde52 Merge pull request #200 from fatedier/doc
doc: support url routing
2016-12-27 10:32:06 -06:00
fatedier
020f786bf5 doc: support url routing 2016-12-28 00:26:50 +08:00
fatedier
cdcc1240ec vhost: fix a wrong usage of sort.Reverse 2016-12-27 02:52:32 +08:00
fatedier
c2c9f68a00 frps: improve login response message 2016-12-27 01:45:22 +08:00
fatedier
37470c26f0 frps: fix sometimes no response when frpc login to frps, see #142 2016-12-27 01:19:19 +08:00
fatedier
04a4591caa vhost: check host and location for url router 2016-12-27 01:01:39 +08:00
fatedier
8bf61d5e39 doc: add qq group 2016-12-26 12:18:46 +08:00
fatedier
659f84bab2 conf: update 2016-12-26 09:58:58 +08:00
fatedier
9faf4acd62 connection pool: ssh can't work when pool_count is set, fix #193 2016-12-26 01:55:54 +08:00
fatedier
4c3fb22295 modify version to 0.9.1 2016-12-25 14:26:16 +08:00
fatedier
d243f70125 doc: update contributors 2016-12-25 14:25:12 +08:00
fatedier
a56f068f8c subdomain: fix a bug that subdomain is not correct for https, close #194 2016-12-25 14:16:54 +08:00
fatedier
6a6ccc5302 Merge pull request #192 from fatedier/pr_182
http proxy support router by url
2016-12-24 12:41:20 -06:00
fatedier
6f90c3400c update conf 2016-12-25 02:09:01 +08:00
fatedier
eb4f779384 update 2016-12-25 01:53:23 +08:00
fatedier
59a34b81e0 Merge pull request #182 from xuebing1110/dev
[dev] http router by host and url
2016-12-24 09:08:20 -06:00
fatedier
b1d1a7a20a Merge branch 'pr_182' into dev 2016-12-24 08:51:26 -06:00
fatedier
6b34ed4644 update doc 2016-12-21 21:22:10 +08:00
fatedier
dde734c953 Merge pull request #188 from fatedier/dev
bump version to 0.9.0
2016-12-21 00:11:10 -06:00
fatedier
5532881b09 Merge pull request #187 from fatedier/doc
update doc for v0.9.0
2016-12-21 00:04:16 -06:00
fatedier
94ddeebc21 update doc 2016-12-21 12:10:21 +08:00
fatedier
ddbb56ee8f update 2016-12-21 01:07:30 +08:00
fatedier
10fc6c67e0 some fix 2016-12-21 01:01:44 +08:00
fatedier
0573ddcd84 Merge pull request #186 from vashstorm/bugfix
add auth for reload api
2016-12-20 05:20:54 -06:00
vashstorm
5eb5fec761 add auth for reload api 2016-12-20 18:32:17 +08:00
fatedier
52fe721202 frps: fix a panic bug caused by reading map 2016-12-20 01:30:10 +08:00
fatedier
d7d2b72431 Merge pull request #184 from fatedier/fatedier/udp
udp: fix privilege_mode not success for udp type
2016-12-19 09:57:08 -06:00
fatedier
d04d31b39a udp: fix privilege_mode not success for udp type 2016-12-19 23:30:48 +08:00
Pan Hao
d9304d8166 Merge pull request #183 from fatedier/fatedier/udp
support udp type
2016-12-19 07:30:44 -06:00
XueBing
a44be1e2ed set default location empty string array
modified:   src/models/server/config.go
	modified:   src/utils/vhost/router.go

	modified:   src/models/server/config.go
	modified:   src/utils/vhost/router.go
2016-12-19 10:38:41 +08:00
fatedier
2bf1d3e922 conf: update 2016-12-19 01:48:57 +08:00
fatedier
19f349a65e vendor: fix 2016-12-19 01:44:17 +08:00
fatedier
b0e56945cd vendor: update godep conf 2016-12-19 01:33:26 +08:00
fatedier
f2999e3317 support udp type 2016-12-19 01:22:21 +08:00
XueBing
a4c05e6ff9 show "Port" incorrectly in dashboard 2016-12-18 23:43:08 +08:00
XueBing
d93dd82ed9 modify the getListener function && add TPS in dashboard 2016-12-18 23:35:14 +08:00
XueBing
edf4bc431d support the configure of url in "http" section 2016-12-18 22:40:58 +08:00
fatedier
47db75e921 Merge pull request #152 from fatedier/maodanp/subdomain
Maodanp/subdomain
2016-11-09 00:14:33 +08:00
Maodanping
c702355669 frps/control rename authTimeout; add judgement for subdomain 2016-11-08 13:40:40 +08:00
Maodanping
7cc5d03f35 conf/frps.ini modify domain field 2016-11-07 09:51:27 +08:00
Maodanping
54beb19435 frps/control.go remove log info 2016-11-05 14:26:04 +08:00
Maodanping
396e148f80 add subdomain configuration; add conn auth timeout 2016-11-05 14:15:16 +08:00
fatedier
4c69a4810e Merge pull request #112 from moul/patch-1
Grammar fix
2016-09-13 09:43:50 +08:00
Manfred Touron
40e023f5f4 Grammar fix 2016-09-12 23:15:34 +02:00
fatedier
adcb2c1ea5 typo 2016-09-06 17:56:09 +08:00
fatedier
8c497793c5 Merge pull request #101 from fatedier/maodanp/http_auth
Maodanp/http auth
2016-09-05 22:25:01 -05:00
Maodanping
78c6845781 utils/vhost: remove fmt.Printf method 2016-08-29 11:02:59 +08:00
Maodanping
dc5e130d33 utils/vhost: supprot http authentication 2016-08-29 10:53:02 +08:00
fatedier
fbc504dfa3 cmd/frps: fix wrong usage of package docopt
Fixes #98.
2016-08-26 23:00:56 +08:00
fatedier
77f207d69a Merge branch 'master' into dev 2016-08-24 13:49:46 +08:00
fatedier
b65e037b5e Fix conflicts
Conflicts:
	README_zh.md
	src/utils/version/version.go
2016-08-24 13:49:13 +08:00
fatedier
b8a28e945c Merge pull request #94 from fatedier/doc
doc: add donation info
2016-08-24 13:40:55 +08:00
fatedier
0476a85a7d doc: add donation info 2016-08-24 13:36:38 +08:00
fatedier
5661537f7c cmd/frpc: fix a merge typo cause pool_count do not work 2016-08-23 15:59:50 +08:00
fatedier
19f7950485 bump version to 0.8.1
bump version to 0.8.1
2016-08-23 10:37:22 +08:00
fatedier
c21f8ad291 utils/version: bump version to 0.8.1 2016-08-23 10:34:14 +08:00
fatedier
3d6578b15f Merge pull request #90 from fatedier/release0.8
merge some fix
2016-08-23 10:23:44 +08:00
fatedier
899d6837df release v0.8.1
release v0.8.1
2016-08-23 10:22:31 +08:00
fatedier
0e1752b5ce Merge pull request #88 from fatedier/fatedier/wildcard_domains
utils/vhost: support wildcard domains
2016-08-23 01:03:41 +08:00
fatedier
da182ecd81 utils/vhost: support wildcard domains 2016-08-23 00:58:51 +08:00
fatedier
94c7f57949 dep: fix a log file deleted bug caused by beego.logs 2016-08-22 01:08:19 +08:00
fatedier
c8e5096f48 utils/pcrypto: fix the bug of using aes
1. The aes IV needs to be unique, but not secure.
2. The key should be 16, 24 or 32 bytes.

Fixes #85.
2016-08-21 23:28:01 +08:00
fatedier
5079bf01fd doc: update 2016-08-21 00:52:15 +08:00
fatedier
603d7df49a build: add mips64 and mips64le 2016-08-21 00:07:25 +08:00
fatedier
2b1c39e03d cmd/frps: a bug of status display in dashboard, fix #80 2016-08-18 14:52:11 +08:00
fatedier
46ee2f2bc8 utils/conn: typo 2016-08-17 10:07:16 +08:00
fatedier
3d5c3acee0 dockerfile: add dockerfile for alpine 2016-08-16 16:10:04 +08:00
fatedier
41fd4bb673 typo 2016-08-16 15:09:32 +08:00
fatedier
e1e18ba9d6 dockerfile: update 2016-08-16 15:06:48 +08:00
fatedier
ab9eff97a8 doc: update contributing 2016-08-16 14:09:29 +08:00
fatedier
6f40b1a70a build: use golang 1.7 2016-08-16 14:03:19 +08:00
fatedier
87c9b8f548 Merge pull request #78 from moul/add-docker
Initial Docker support
2016-08-15 23:37:53 +08:00
Manfred Touron
3fcf7efc5a Initial Docker support 2016-08-15 17:28:37 +02:00
fatedier
a655f5699b utils/version: change version to 0.9.0 2016-08-15 01:11:37 +08:00
fatedier
09624b56ca Merge pull request #76 from fatedier/fatedier/support_http_proxy
frpc: add support for connecting server through http proxies, see #67
2016-08-15 01:07:50 +08:00
fatedier
e262ac6abd frpc: add support for connecting server through http proxies, see #67 2016-08-15 00:55:22 +08:00
fatedier
47c1a3e52c Merge pull request #74 from se77en/master
Add basic auth to dashboard
2016-08-14 13:55:13 +08:00
Eric Larssen
4dadaac905 Add basic auth to dashboard 2016-08-14 12:45:35 +08:00
fatedier
e1ed6660b0 Merge pull request #71 from ericlarssen/updateDocs
Sentence Spacing in Readme, killed some whitespace.
2016-08-13 23:17:38 +08:00
Eric Larssen
b71b2cf46d Add sentence spacing in readme 2016-08-13 09:32:11 -05:00
fatedier
a0903d4121 release v0.8.0
release v0.8.0
2016-08-12 14:00:54 +08:00
fatedier
b403e4142b Merge pull request #65 from fatedier/0.8doc
0.8doc
2016-08-12 13:58:32 +08:00
fatedier
46716acd8e doc: update 2016-08-12 13:44:25 +08:00
fatedier
c7f85bcdd3 doc: new features 2016-08-12 12:50:12 +08:00
fatedier
ddd2acfe9f models/msg: fix current connections statistics problem 2016-08-12 10:07:02 +08:00
fatedier
e3bf7e2b2b cmd/frps: typo 2016-08-12 00:57:46 +08:00
fatedier
4914472215 assets: update assets 2016-08-12 00:39:39 +08:00
fatedier
5d9300c1e9 utils/conn: support ipv6, fix #62 2016-08-12 00:32:33 +08:00
fatedier
b4a577b0d7 Merge pull request #64 from fatedier/fatedier/assets_to_binary
assets: use statik to compile static files into binary files
2016-08-11 21:58:58 +08:00
fatedier
32d0ce9ea0 assets: use statik to compile static files into binary files 2016-08-11 21:47:26 +08:00
fatedier
2d30a6e8a7 build: remove support for go1.4, add go1.7 2016-08-11 17:03:19 +08:00
fatedier
740691b080 update package beego/logs 2016-08-11 16:32:05 +08:00
fatedier
11fe4b1d8b Godep workspace -> vendor/ 2016-08-11 16:26:21 +08:00
fatedier
c64931fce9 package: use net.http instead of gin 2016-08-11 16:20:59 +08:00
fatedier
d4ecc2218d all: modify import path, change version to v0.8.0 2016-08-11 16:10:44 +08:00
fatedier
9c0ca8675d Merge pull request #63 from fatedier/vashstorm/dashboard_view
provide a simple dashboard webpage
2016-08-11 15:37:15 +08:00
fatedier
5cdb84c666 index.html table show today flow_out and flow_in 2016-08-11 15:31:35 +08:00
Gogs
060277308b index.html: fix duplicate today info bug 2016-08-11 15:23:50 +08:00
vashstorm
31dfd5101f index.html use bar instead of line, fix col merging bug 2016-08-10 21:44:33 +08:00
fatedier
4300169041 assets: optimize static files archetucture 2016-08-10 20:18:36 +08:00
vashstorm
3ab9850871 fix index.html use local css files 2016-08-09 21:28:43 +08:00
panhao
d813b953dd assets:dashboard static resources 2016-08-09 21:17:02 +08:00
fatedier
1da81ad7d3 models/msg: limit single package length 2016-08-07 13:18:54 +08:00
fatedier
3b06d771ac utils/conn: wsarecv error in windows means connection closed? 2016-08-01 15:24:04 +08:00
fatedier
7f386fc042 utils/pool: use sync.pool to reduce the pressure of garbage collection 2016-07-31 03:26:41 +08:00
fatedier
df8edefa56 Merge pull request #55 from fatedier/fatedier/privilege_mode_port
all: privilege_allow_ports can be set in frps for ending abuse of ports
2016-07-31 02:27:06 +08:00
fatedier
ecb6ad4885 all: privilege_allow_ports can be set in frps for ending abuse of ports 2016-07-31 02:17:10 +08:00
fatedier
785dcaad44 Merge branch 'fatedier/connection_pool' into dev 2016-07-29 23:39:48 +08:00
fatedier
fd3c97a0e9 Fix conflicts in fatedier/connection_pool with dev
Conflicts:
	src/frp/cmd/frpc/control.go
	src/frp/cmd/frps/control.go
	src/frp/models/config/config.go
	src/frp/models/server/server.go
2016-07-29 23:38:34 +08:00
fatedier
8f5f0b0a9a all: new feature connection pool 2016-07-29 23:08:00 +08:00
fatedier
452e02adab add host_header_rewrite in frpc.ini to rewrite your requests with a modified Host header 2016-07-26 00:18:19 +08:00
fatedier
d2e1cfa5bc Merge pull request #52 from maodanp/http_hostname
support host rewrite
2016-07-25 20:52:45 +08:00
Maodanping
6dd51e0951 remove the extra filed 2016-07-24 15:08:01 +08:00
Maodanping
e0f2993b70 change the Host value in http request header 2016-07-24 15:00:18 +08:00
fatedier
4067591a4d Merge pull request #50 from fatedier/fatedier/fix_package_loss
frp/models/msg: fix a bug if local service write to socket immediatel…
2016-07-20 16:37:45 +08:00
fatedier
926d0b74a9 utils/vhost: update TcpConn with bufio.Reader 2016-07-20 16:33:42 +08:00
fatedier
4f49458af0 frp/models/msg: fix a bug if local service write to socket immediately every time accept one user connection, fix #20 2016-07-20 16:04:06 +08:00
fatedier
fd6b94908b models/server: add statistics for proxies enabled privilege mode. 2016-07-18 00:30:46 +08:00
fatedier
dee4cbd48c models/metric: sort for metric result 2016-07-18 00:18:40 +08:00
fatedier
9a3564f29c Merge pull request #49 from fatedier/fatedier/statistics
all: add /api/proxies api for statistics info
2016-07-17 21:52:07 +08:00
fatedier
ac09ba3982 all: add /api/proxies api for statistics info 2016-07-17 21:42:21 +08:00
fatedier
a9bf25f255 build: add go 1.6.2 2016-07-12 01:08:08 +08:00
fatedier
6bc05de58e Merge pull request #48 from fatedier/dev
update README.md
2016-07-12 00:18:20 +08:00
fatedier
5265b79957 Merge branch 'master' into dev 2016-07-11 23:45:36 +08:00
fatedier
fefc0a38c3 Merge pull request #47 from fatedier/fatedier/update_doc
update README.md
2016-07-11 23:44:24 +08:00
fatedier
c387138006 doc: update README.md 2016-07-11 23:35:34 +08:00
fatedier
36f8beee3d doc: update README.md 2016-07-11 23:31:35 +08:00
fatedier
366a0c898d Merge pull request #46 from fatedier/dev
merge for new README_zh.md
2016-07-09 13:13:45 +08:00
fatedier
d747f9207e Merge pull request #45 from fatedier/fatedier/update_doc
doc: update README_zh.md
2016-07-09 13:10:51 +08:00
fatedier
5400366036 doc: update README_zh.md 2016-07-09 13:06:20 +08:00
fatedier
9dae7ad6fe cmd/frps: update 2016-07-08 14:19:26 +08:00
fatedier
a4e051d494 cmd/frps: improve the description of login failed 2016-07-08 14:15:26 +08:00
fatedier
28251a8104 Merge pull request #41 from fatedier/dev
release v0.7.0
2016-07-04 15:11:10 +08:00
fatedier
e99357da4e doc: add contributor 2016-07-04 15:08:16 +08:00
fatedier
e580c7b6e6 all: release resources when proxy closed using privilege mode 2016-06-28 00:55:30 +08:00
fatedier
ba74934a1f all: privilege mode update 2016-06-28 00:21:13 +08:00
fatedier
1bad5c6561 Merge pull request #40 from fatedier/fatedier/privilege_mode
support privilege mode
2016-06-26 22:43:02 +08:00
fatedier
f968f3eace all: add new feature privilege mode 2016-06-26 22:36:07 +08:00
fatedier
b14441d5cd utils/vhost: update for vhost_https 2016-06-24 15:43:58 +08:00
fatedier
0a50c3bd82 Merge pull request #39 from fatedier/release0.7
add support for https
2016-06-24 14:09:50 +08:00
fatedier
ef5702213f Merge pull request #38 from fatedier/fatedier/fix_vhost_bug
cmd/frps: fix a bug when vhost_http_port is not set, fix #30
2016-06-24 14:00:50 +08:00
fatedier
c5e4b24f8f all: add use_gzip configure and some improvements, see #28 2016-06-24 13:12:34 +08:00
Pan Hao
1987a399c1 Merge pull request #33 from fatedier/mydev
User can set use aes or gzip
2016-06-14 17:33:32 +08:00
Gogs
ab6c5c813e User can set use aes or gzip 2016-06-14 16:58:53 +08:00
fatedier
51eaec14ab Merge pull request #32 from maodanp/release0.7
add https proto for reverse proxy
2016-06-13 22:43:32 +08:00
Maodanping
f3876d69bb add https proto for reverse proxy 2016-06-13 22:19:24 +08:00
fatedier
817f4463f4 cmd/frps: fix a bug when vhost_http_port is not set, fix #30 2016-06-12 21:57:55 +08:00
fatedier
654981019d release v0.6.0
release v0.6.0
2016-06-07 22:35:56 +08:00
fatedier
740fb05b21 build: remove godep command(can not download packages in golang.org used in latest godep version) 2016-06-07 22:17:37 +08:00
fatedier
e8c830e5c8 all: format 2016-06-07 11:15:59 +08:00
fatedier
2640c0b570 utils/conn: fix a socket error in windows 2016-06-06 11:43:41 +08:00
fatedier
04014bb78f doc: update 2016-06-03 18:15:01 +08:00
fatedier
150c4beef8 cmd/frps: update help info 2016-06-03 17:55:53 +08:00
fatedier
5febee6201 all: add "--reload" command for frps, reload ini file without kill old program 2016-06-03 17:51:45 +08:00
fatedier
ee8786a6b3 models/server: fix one possible error when frps accept too many user connections in a short time 2016-05-19 20:50:19 +08:00
fatedier
d569a60eff cmd/frpc: let cli.StartTunnel async 2016-05-19 15:13:35 +08:00
fatedier
14607b352d utils/version: update version to v0.6.0 2016-05-19 00:36:08 +08:00
fatedier
bc7ad2bb20 update 2016-05-19 00:28:39 +08:00
fatedier
cd59bbdad6 Godeps: add package gin for dashboard 2016-05-19 00:26:16 +08:00
fatedier
f404a0a5ee models/server: add dashboard for frps by gin 2016-05-19 00:04:19 +08:00
fatedier
da7c473288 fetch commits from master
fetch commits from master
2016-05-17 19:21:27 +08:00
fatedier
ea323084ad test: fix 2016-05-17 19:17:04 +08:00
fatedier
c680d87edc test: add function testing case 2016-05-17 19:13:37 +08:00
fatedier
d3c4401473 build: support linux/arm and darwin 2016-05-15 13:30:14 +08:00
fatedier
7a9a675d58 merge dev for fixing bugs when frpc login failed 2016-05-09 00:37:01 +08:00
fatedier
040841db48 cmd/frps/control: fix bug when frpc login failed 2016-05-09 00:15:31 +08:00
fatedier
f804330dbf release v0.5.0 (#24)
[new] Optimize for http services.Support virtual host and custom domain binding.
[new] Support max days of keeping log files.
[fix] Fix a bug when reconnecting.
2016-05-03 22:05:55 +08:00
fatedier
d39d745e43 docker: rm Dockerfile 2016-05-03 21:51:57 +08:00
fatedier
c10321ead6 utils/log: support max days of keeping log files 2016-05-03 21:41:57 +08:00
fatedier
d7797cbd18 doc: update 2016-04-19 19:02:13 +08:00
fatedier
0b9d823168 doc: update 2016-04-19 18:58:55 +08:00
fatedier
deb750652f doc: update 2016-04-19 18:53:48 +08:00
fatedier
14ba38a1b4 doc: update 2016-04-18 15:47:14 +08:00
fatedier
f650d3f330 all: support for virtual host 2016-04-18 15:16:40 +08:00
fatedier
2c39719cc0 Merge branch 'dev' 2016-04-11 17:38:12 +08:00
fatedier
6874688e07 build: for cross-compiles 2016-04-11 17:27:14 +08:00
fatedier
fdd7436736 build: fix 2016-04-11 12:05:24 +08:00
fatedier
0f326449e8 build: fix 2016-04-11 11:58:07 +08:00
fatedier
7c3e00ed28 build: update Makefile to avoid godep warning 2016-04-11 11:21:35 +08:00
fatedier
d5913fc77b all: fix bug when reconnecting 2016-04-07 19:27:39 +08:00
fatedier
2ba84d375a Release v0.3.0
Release v0.3.0
2016-04-06 12:19:31 +08:00
fatedier
6a0d6035cb doc: typo 2016-04-06 11:51:12 +08:00
fatedier
d091e0eac9 doc: update 2016-04-06 11:43:50 +08:00
fatedier
bc176b90f1 all: change passwd to auth_token and improve authentication 2016-04-05 17:18:21 +08:00
fatedier
a729a4fafe pcrypto: update 2016-04-05 16:17:31 +08:00
fatedier
78c770d37d Merge pull request #15 from fatedier/test 2016-04-05 15:43:28 +08:00
fatedier
718e707b77 pcrypto: fix 2016-04-05 15:40:32 +08:00
fatedier
b3ee746be8 optimize compress 2016-04-05 15:25:55 +08:00
fatedier
80fc76da52 all: now messages between frps and frpc can be encryped when use_encryption set true 2016-03-31 18:48:18 +08:00
fatedier
52f99bbc00 all: some optimization 2016-03-31 18:03:44 +08:00
fatedier
45c21b2705 utils/conn: add func joinMore 2016-03-28 11:22:36 +08:00
fatedier
b6212afb03 Merge pull request #11 from vashstorm/new
fix crypto
2016-03-25 14:09:01 +08:00
Gogs
49975c4c1b fix crypto 2016-03-24 15:42:34 +08:00
fatedier
580e75f633 Merge pull request #8 from vashstorm/master
add crypto
2016-03-23 09:59:42 +08:00
Gogs
20afe25ef1 modified: src/frp/models/client/client.go
modified:   src/frp/models/server/server.go
	modified:   src/frp/utils/conn/conn.go
	modified:   src/frp/utils/pcrypto/pcrypto.go
	modified:   src/frp/utils/pcrypto/pcrypto_test.go
2016-03-22 17:18:05 +08:00
fatedier
6e57135533 typo 2016-03-17 17:52:59 +08:00
fatedier
931c102668 fix bug in Dockerfile 2016-03-17 17:47:41 +08:00
fatedier
5700101c0e doc: typo 2016-03-17 11:04:40 +08:00
fatedier
90349a48b0 doc: update version in doc to v0.2.0 2016-03-17 10:49:38 +08:00
296 changed files with 42135 additions and 5624 deletions

23
.circleci/config.yml Normal file
View File

@@ -0,0 +1,23 @@
version: 2
jobs:
go-version-latest:
docker:
- image: cimg/go:1.18-node
steps:
- checkout
- run: make
- run: make alltest
go-version-last:
docker:
- image: cimg/go:1.17-node
steps:
- checkout
- run: make
- run: make alltest
workflows:
version: 2
build_and_test:
jobs:
- go-version-latest
- go-version-last

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

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

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

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

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1 @@
blank_issues_enabled: false

View File

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

View File

@@ -0,0 +1,117 @@
name: Build Image and Publish to Dockerhub & GPR
on:
release:
types: [ created ]
workflow_dispatch:
inputs:
tag:
description: 'Image tag'
required: true
default: 'test'
jobs:
binary:
name: Build Golang project
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: 1.18
- run: |
# https://github.com/actions/setup-go/issues/107
cp -f `which go` /usr/bin/go
- run: go version
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Build
run: make build
- name: Archive artifacts for frpc
uses: actions/upload-artifact@v1
with:
name: frpc
path: bin/frpc
- name: Archive artifacts for frps
uses: actions/upload-artifact@v1
with:
name: frps
path: bin/frps
image:
name: Build Image from Dockerfile and binaries
runs-on: ubuntu-latest
needs: binary
steps:
# environment
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: '0'
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
# download binaries of frpc and frps
- name: Download binary of frpc
uses: actions/download-artifact@v2
with:
name: frpc
path: bin/frpc
- name: Download binary of frps
uses: actions/download-artifact@v2
with:
name: frps
path: bin/frps
# get image tag name
- name: Get Image Tag Name
run: |
if [ x${{ github.event.inputs.tag }} == x"" ]; then
echo "TAG_NAME=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
else
echo "TAG_NAME=${{ github.event.inputs.tag }}" >> $GITHUB_ENV
fi
# prepare image tags
- name: Prepare Image Tags
run: |
echo "DOCKERFILE_FRPC_PATH=dockerfiles/Dockerfile-for-frpc" >> $GITHUB_ENV
echo "DOCKERFILE_FRPS_PATH=dockerfiles/Dockerfile-for-frps" >> $GITHUB_ENV
echo "TAG_FRPC=fatedier/frpc:${{ env.TAG_NAME }}" >> $GITHUB_ENV
echo "TAG_FRPS=fatedier/frps:${{ env.TAG_NAME }}" >> $GITHUB_ENV
echo "TAG_FRPC_GPR=ghcr.io/fatedier/frpc:${{ env.TAG_NAME }}" >> $GITHUB_ENV
echo "TAG_FRPS_GPR=ghcr.io/fatedier/frps:${{ env.TAG_NAME }}" >> $GITHUB_ENV
# build images
- name: Build Images
run: |
# for Docker hub
docker build --file ${{ env.DOCKERFILE_FRPC_PATH }} --tag ${{ env.TAG_FRPC }} .
docker build --file ${{ env.DOCKERFILE_FRPS_PATH }} --tag ${{ env.TAG_FRPS }} .
# for GPR
docker build --file ${{ env.DOCKERFILE_FRPC_PATH }} --tag ${{ env.TAG_FRPC_GPR }} .
docker build --file ${{ env.DOCKERFILE_FRPS_PATH }} --tag ${{ env.TAG_FRPS_GPR }} .
# push to dockerhub
- name: Publish to Dockerhub
run: |
echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login --username ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
docker push ${{ env.TAG_FRPC }}
docker push ${{ env.TAG_FRPS }}
# push to gpr
- name: Publish to GPR
run: |
echo ${{ secrets.GPR_TOKEN }} | docker login ghcr.io --username ${{ github.repository_owner }} --password-stdin
docker push ${{ env.TAG_FRPC_GPR }}
docker push ${{ env.TAG_FRPS_GPR }}

34
.github/workflows/goreleaser.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: goreleaser
on:
workflow_dispatch:
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.18
- run: |
# https://github.com/actions/setup-go/issues/107
cp -f `which go` /usr/bin/go
- name: Make All
run: |
./package.sh
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist --release-notes=./Release.md
env:
GITHUB_TOKEN: ${{ secrets.GPR_TOKEN }}

28
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
name: "Close stale issues"
on:
schedule:
- cron: "20 0 * * *"
workflow_dispatch:
inputs:
debug-only:
description: 'In debug mod'
required: false
default: 'false'
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v5
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Issues go stale after 30d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.'
stale-pr-message: "PRs go stale after 30d of inactivity. Stale PRs rot after an additional 7d of inactivity and eventually close."
stale-issue-label: 'lifecycle/stale'
exempt-issue-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned'
stale-pr-label: 'lifecycle/stale'
exempt-pr-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned'
days-before-stale: 30
days-before-close: 7
debug-only: ${{ github.event.inputs.debug-only }}
exempt-all-pr-milestones: true
exempt-all-pr-assignees: true

6
.gitignore vendored
View File

@@ -25,6 +25,12 @@ _testmain.go
# Self
bin/
packages/
release/
test/bin/
vendor/
dist/
.idea/
# Cache
*.swp

22
.goreleaser.yml Normal file
View File

@@ -0,0 +1,22 @@
builds:
- skip: true
checksum:
name_template: '{{ .ProjectName }}_sha256_checksums.txt'
algorithm: sha256
extra_files:
- glob: ./release/packages/*
release:
# Same as for github
# Note: it can only be one: either github, gitlab or gitea
github:
owner: fatedier
name: frp
draft: false
# You can add extra pre-existing files to the release.
# The filename on the release will be the last part of the path (base). If
# another file with the same name exists, the latest one found will be used.
# Defaults to empty.
extra_files:
- glob: ./release/packages/*

View File

@@ -1,12 +0,0 @@
sudo: false
language: go
go:
- 1.4.2
- 1.5.1
install:
- make
script:
- make test

View File

@@ -1,14 +0,0 @@
FROM golang:1.5
MAINTAINER fatedier
RUN echo "[common]\nbind_addr = 0.0.0.0\nbind_port = 7000\n[wiki]\npasswd = 123\nbind_addr = 0.0.0.0\nlisten_port = 80" > /usr/share/frps.ini
ADD ./ /usr/share/frp/
RUN cd /usr/share/frp && make
EXPOSE 80
EXPOSE 7000
CMD ["/usr/share/frp/bin/frps -c /usr/share/frps.ini"]

23
Godeps/Godeps.json generated
View File

@@ -1,23 +0,0 @@
{
"ImportPath": "frp",
"GoVersion": "go1.4",
"Packages": [
"./..."
],
"Deps": [
{
"ImportPath": "github.com/astaxie/beego/logs",
"Comment": "v1.5.0-9-gfb7314f",
"Rev": "fb7314f8ac86b83ccd34386518d97cf2363e2ae5"
},
{
"ImportPath": "github.com/docopt/docopt-go",
"Comment": "0.6.2",
"Rev": "784ddc588536785e7299f7272f39101f7faccc3f"
},
{
"ImportPath": "github.com/vaughan0/go-ini",
"Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1"
}
]
}

5
Godeps/Readme generated
View File

@@ -1,5 +0,0 @@
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/tools/godep for more information.

2
Godeps/_workspace/.gitignore generated vendored
View File

@@ -1,2 +0,0 @@
/pkg
/bin

View File

@@ -1,13 +0,0 @@
Copyright 2014 astaxie
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.

View File

@@ -1,63 +0,0 @@
## logs
logs is a Go logs manager. It can use many logs adapters. The repo is inspired by `database/sql` .
## How to install?
go get github.com/astaxie/beego/logs
## What adapters are supported?
As of now this logs support console, file,smtp and conn.
## How to use it?
First you must import it
import (
"github.com/astaxie/beego/logs"
)
Then init a Log (example with console adapter)
log := NewLogger(10000)
log.SetLogger("console", "")
> the first params stand for how many channel
Use it like this:
log.Trace("trace")
log.Info("info")
log.Warn("warning")
log.Debug("debug")
log.Critical("critical")
## File adapter
Configure file adapter like this:
log := NewLogger(10000)
log.SetLogger("file", `{"filename":"test.log"}`)
## Conn adapter
Configure like this:
log := NewLogger(1000)
log.SetLogger("conn", `{"net":"tcp","addr":":7020"}`)
log.Info("info")
## Smtp adapter
Configure like this:
log := NewLogger(10000)
log.SetLogger("smtp", `{"username":"beegotest@gmail.com","password":"xxxxxxxx","host":"smtp.gmail.com:587","sendTos":["xiemengjun@gmail.com"]}`)
log.Critical("sendmail critical")
time.Sleep(time.Second * 30)

View File

@@ -1,116 +0,0 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// 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 logs
import (
"encoding/json"
"io"
"log"
"net"
)
// ConnWriter implements LoggerInterface.
// it writes messages in keep-live tcp connection.
type ConnWriter struct {
lg *log.Logger
innerWriter io.WriteCloser
ReconnectOnMsg bool `json:"reconnectOnMsg"`
Reconnect bool `json:"reconnect"`
Net string `json:"net"`
Addr string `json:"addr"`
Level int `json:"level"`
}
// create new ConnWrite returning as LoggerInterface.
func NewConn() LoggerInterface {
conn := new(ConnWriter)
conn.Level = LevelTrace
return conn
}
// init connection writer with json config.
// json config only need key "level".
func (c *ConnWriter) Init(jsonconfig string) error {
return json.Unmarshal([]byte(jsonconfig), c)
}
// write message in connection.
// if connection is down, try to re-connect.
func (c *ConnWriter) WriteMsg(msg string, level int) error {
if level > c.Level {
return nil
}
if c.neddedConnectOnMsg() {
err := c.connect()
if err != nil {
return err
}
}
if c.ReconnectOnMsg {
defer c.innerWriter.Close()
}
c.lg.Println(msg)
return nil
}
// implementing method. empty.
func (c *ConnWriter) Flush() {
}
// destroy connection writer and close tcp listener.
func (c *ConnWriter) Destroy() {
if c.innerWriter != nil {
c.innerWriter.Close()
}
}
func (c *ConnWriter) connect() error {
if c.innerWriter != nil {
c.innerWriter.Close()
c.innerWriter = nil
}
conn, err := net.Dial(c.Net, c.Addr)
if err != nil {
return err
}
if tcpConn, ok := conn.(*net.TCPConn); ok {
tcpConn.SetKeepAlive(true)
}
c.innerWriter = conn
c.lg = log.New(conn, "", log.Ldate|log.Ltime)
return nil
}
func (c *ConnWriter) neddedConnectOnMsg() bool {
if c.Reconnect {
c.Reconnect = false
return true
}
if c.innerWriter == nil {
return true
}
return c.ReconnectOnMsg
}
func init() {
Register("conn", NewConn)
}

View File

@@ -1,95 +0,0 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// 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 logs
import (
"encoding/json"
"log"
"os"
"runtime"
)
type Brush func(string) string
func NewBrush(color string) Brush {
pre := "\033["
reset := "\033[0m"
return func(text string) string {
return pre + color + "m" + text + reset
}
}
var colors = []Brush{
NewBrush("1;37"), // Emergency white
NewBrush("1;36"), // Alert cyan
NewBrush("1;35"), // Critical magenta
NewBrush("1;31"), // Error red
NewBrush("1;33"), // Warning yellow
NewBrush("1;32"), // Notice green
NewBrush("1;34"), // Informational blue
NewBrush("1;34"), // Debug blue
}
// ConsoleWriter implements LoggerInterface and writes messages to terminal.
type ConsoleWriter struct {
lg *log.Logger
Level int `json:"level"`
}
// create ConsoleWriter returning as LoggerInterface.
func NewConsole() LoggerInterface {
cw := &ConsoleWriter{
lg: log.New(os.Stdout, "", log.Ldate|log.Ltime),
Level: LevelDebug,
}
return cw
}
// init console logger.
// jsonconfig like '{"level":LevelTrace}'.
func (c *ConsoleWriter) Init(jsonconfig string) error {
if len(jsonconfig) == 0 {
return nil
}
return json.Unmarshal([]byte(jsonconfig), c)
}
// write message in console.
func (c *ConsoleWriter) WriteMsg(msg string, level int) error {
if level > c.Level {
return nil
}
if goos := runtime.GOOS; goos == "windows" {
c.lg.Println(msg)
return nil
}
c.lg.Println(colors[level](msg))
return nil
}
// implementing method. empty.
func (c *ConsoleWriter) Destroy() {
}
// implementing method. empty.
func (c *ConsoleWriter) Flush() {
}
func init() {
Register("console", NewConsole)
}

View File

@@ -1,76 +0,0 @@
package es
import (
"encoding/json"
"errors"
"fmt"
"net"
"net/url"
"time"
"github.com/astaxie/beego/logs"
"github.com/belogik/goes"
)
func NewES() logs.LoggerInterface {
cw := &esLogger{
Level: logs.LevelDebug,
}
return cw
}
type esLogger struct {
*goes.Connection
DSN string `json:"dsn"`
Level int `json:"level"`
}
// {"dsn":"http://localhost:9200/","level":1}
func (el *esLogger) Init(jsonconfig string) error {
err := json.Unmarshal([]byte(jsonconfig), el)
if err != nil {
return err
}
if el.DSN == "" {
return errors.New("empty dsn")
} else if u, err := url.Parse(el.DSN); err != nil {
return err
} else if u.Path == "" {
return errors.New("missing prefix")
} else if host, port, err := net.SplitHostPort(u.Host); err != nil {
return err
} else {
conn := goes.NewConnection(host, port)
el.Connection = conn
}
return nil
}
func (el *esLogger) WriteMsg(msg string, level int) error {
if level > el.Level {
return nil
}
t := time.Now()
vals := make(map[string]interface{})
vals["@timestamp"] = t.Format(time.RFC3339)
vals["@msg"] = msg
d := goes.Document{
Index: fmt.Sprintf("%04d.%02d.%02d", t.Year(), t.Month(), t.Day()),
Type: "logs",
Fields: vals,
}
_, err := el.Index(d, nil)
return err
}
func (el *esLogger) Destroy() {
}
func (el *esLogger) Flush() {
}
func init() {
logs.Register("es", NewES)
}

View File

@@ -1,283 +0,0 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// 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 logs
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
"sync"
"time"
)
// FileLogWriter implements LoggerInterface.
// It writes messages by lines limit, file size limit, or time frequency.
type FileLogWriter struct {
*log.Logger
mw *MuxWriter
// The opened file
Filename string `json:"filename"`
Maxlines int `json:"maxlines"`
maxlines_curlines int
// Rotate at size
Maxsize int `json:"maxsize"`
maxsize_cursize int
// Rotate daily
Daily bool `json:"daily"`
Maxdays int64 `json:"maxdays"`
daily_opendate int
Rotate bool `json:"rotate"`
startLock sync.Mutex // Only one log can write to the file
Level int `json:"level"`
}
// an *os.File writer with locker.
type MuxWriter struct {
sync.Mutex
fd *os.File
}
// write to os.File.
func (l *MuxWriter) Write(b []byte) (int, error) {
l.Lock()
defer l.Unlock()
return l.fd.Write(b)
}
// set os.File in writer.
func (l *MuxWriter) SetFd(fd *os.File) {
if l.fd != nil {
l.fd.Close()
}
l.fd = fd
}
// create a FileLogWriter returning as LoggerInterface.
func NewFileWriter() LoggerInterface {
w := &FileLogWriter{
Filename: "",
Maxlines: 1000000,
Maxsize: 1 << 28, //256 MB
Daily: true,
Maxdays: 7,
Rotate: true,
Level: LevelTrace,
}
// use MuxWriter instead direct use os.File for lock write when rotate
w.mw = new(MuxWriter)
// set MuxWriter as Logger's io.Writer
w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime)
return w
}
// Init file logger with json config.
// jsonconfig like:
// {
// "filename":"logs/beego.log",
// "maxlines":10000,
// "maxsize":1<<30,
// "daily":true,
// "maxdays":15,
// "rotate":true
// }
func (w *FileLogWriter) Init(jsonconfig string) error {
err := json.Unmarshal([]byte(jsonconfig), w)
if err != nil {
return err
}
if len(w.Filename) == 0 {
return errors.New("jsonconfig must have filename")
}
err = w.startLogger()
return err
}
// start file logger. create log file and set to locker-inside file writer.
func (w *FileLogWriter) startLogger() error {
fd, err := w.createLogFile()
if err != nil {
return err
}
w.mw.SetFd(fd)
return w.initFd()
}
func (w *FileLogWriter) docheck(size int) {
w.startLock.Lock()
defer w.startLock.Unlock()
if w.Rotate && ((w.Maxlines > 0 && w.maxlines_curlines >= w.Maxlines) ||
(w.Maxsize > 0 && w.maxsize_cursize >= w.Maxsize) ||
(w.Daily && time.Now().Day() != w.daily_opendate)) {
if err := w.DoRotate(); err != nil {
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
return
}
}
w.maxlines_curlines++
w.maxsize_cursize += size
}
// write logger message into file.
func (w *FileLogWriter) WriteMsg(msg string, level int) error {
if level > w.Level {
return nil
}
n := 24 + len(msg) // 24 stand for the length "2013/06/23 21:00:22 [T] "
w.docheck(n)
w.Logger.Println(msg)
return nil
}
func (w *FileLogWriter) createLogFile() (*os.File, error) {
// Open the log file
fd, err := os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
return fd, err
}
func (w *FileLogWriter) initFd() error {
fd := w.mw.fd
finfo, err := fd.Stat()
if err != nil {
return fmt.Errorf("get stat err: %s\n", err)
}
w.maxsize_cursize = int(finfo.Size())
w.daily_opendate = time.Now().Day()
w.maxlines_curlines = 0
if finfo.Size() > 0 {
count, err := w.lines()
if err != nil {
return err
}
w.maxlines_curlines = count
}
return nil
}
func (w *FileLogWriter) lines() (int, error) {
fd, err := os.Open(w.Filename)
if err != nil {
return 0, err
}
defer fd.Close()
buf := make([]byte, 32768) // 32k
count := 0
lineSep := []byte{'\n'}
for {
c, err := fd.Read(buf)
if err != nil && err != io.EOF {
return count, err
}
count += bytes.Count(buf[:c], lineSep)
if err == io.EOF {
break
}
}
return count, nil
}
// DoRotate means it need to write file in new file.
// new file name like xx.log.2013-01-01.2
func (w *FileLogWriter) DoRotate() error {
_, err := os.Lstat(w.Filename)
if err == nil { // file exists
// Find the next available number
num := 1
fname := ""
for ; err == nil && num <= 999; num++ {
fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num)
_, err = os.Lstat(fname)
}
// return error if the last file checked still existed
if err == nil {
return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.Filename)
}
// block Logger's io.Writer
w.mw.Lock()
defer w.mw.Unlock()
fd := w.mw.fd
fd.Close()
// close fd before rename
// Rename the file to its newfound home
err = os.Rename(w.Filename, fname)
if err != nil {
return fmt.Errorf("Rotate: %s\n", err)
}
// re-start logger
err = w.startLogger()
if err != nil {
return fmt.Errorf("Rotate StartLogger: %s\n", err)
}
go w.deleteOldLog()
}
return nil
}
func (w *FileLogWriter) deleteOldLog() {
dir := filepath.Dir(w.Filename)
filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) {
defer func() {
if r := recover(); r != nil {
returnErr = fmt.Errorf("Unable to delete old log '%s', error: %+v", path, r)
fmt.Println(returnErr)
}
}()
if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) {
if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) {
os.Remove(path)
}
}
return
})
}
// destroy file logger, close file writer.
func (w *FileLogWriter) Destroy() {
w.mw.fd.Close()
}
// flush file logger.
// there are no buffering messages in file logger in memory.
// flush file means sync file from disk.
func (w *FileLogWriter) Flush() {
w.mw.fd.Sync()
}
func init() {
Register("file", NewFileWriter)
}

View File

@@ -1,350 +0,0 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// 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.
// Usage:
//
// import "github.com/astaxie/beego/logs"
//
// log := NewLogger(10000)
// log.SetLogger("console", "")
//
// > the first params stand for how many channel
//
// Use it like this:
//
// log.Trace("trace")
// log.Info("info")
// log.Warn("warning")
// log.Debug("debug")
// log.Critical("critical")
//
// more docs http://beego.me/docs/module/logs.md
package logs
import (
"fmt"
"path"
"runtime"
"sync"
)
// RFC5424 log message levels.
const (
LevelEmergency = iota
LevelAlert
LevelCritical
LevelError
LevelWarning
LevelNotice
LevelInformational
LevelDebug
)
// Legacy loglevel constants to ensure backwards compatibility.
//
// Deprecated: will be removed in 1.5.0.
const (
LevelInfo = LevelInformational
LevelTrace = LevelDebug
LevelWarn = LevelWarning
)
type loggerType func() LoggerInterface
// LoggerInterface defines the behavior of a log provider.
type LoggerInterface interface {
Init(config string) error
WriteMsg(msg string, level int) error
Destroy()
Flush()
}
var adapters = make(map[string]loggerType)
// Register makes a log provide available by the provided name.
// If Register is called twice with the same name or if driver is nil,
// it panics.
func Register(name string, log loggerType) {
if log == nil {
panic("logs: Register provide is nil")
}
if _, dup := adapters[name]; dup {
panic("logs: Register called twice for provider " + name)
}
adapters[name] = log
}
// BeeLogger is default logger in beego application.
// it can contain several providers and log message into all providers.
type BeeLogger struct {
lock sync.Mutex
level int
enableFuncCallDepth bool
loggerFuncCallDepth int
asynchronous bool
msg chan *logMsg
outputs map[string]LoggerInterface
}
type logMsg struct {
level int
msg string
}
// NewLogger returns a new BeeLogger.
// channellen means the number of messages in chan.
// if the buffering chan is full, logger adapters write to file or other way.
func NewLogger(channellen int64) *BeeLogger {
bl := new(BeeLogger)
bl.level = LevelDebug
bl.loggerFuncCallDepth = 2
bl.msg = make(chan *logMsg, channellen)
bl.outputs = make(map[string]LoggerInterface)
return bl
}
func (bl *BeeLogger) Async() *BeeLogger {
bl.asynchronous = true
go bl.startLogger()
return bl
}
// SetLogger provides a given logger adapter into BeeLogger with config string.
// config need to be correct JSON as string: {"interval":360}.
func (bl *BeeLogger) SetLogger(adaptername string, config string) error {
bl.lock.Lock()
defer bl.lock.Unlock()
if log, ok := adapters[adaptername]; ok {
lg := log()
err := lg.Init(config)
bl.outputs[adaptername] = lg
if err != nil {
fmt.Println("logs.BeeLogger.SetLogger: " + err.Error())
return err
}
} else {
return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adaptername)
}
return nil
}
// remove a logger adapter in BeeLogger.
func (bl *BeeLogger) DelLogger(adaptername string) error {
bl.lock.Lock()
defer bl.lock.Unlock()
if lg, ok := bl.outputs[adaptername]; ok {
lg.Destroy()
delete(bl.outputs, adaptername)
return nil
} else {
return fmt.Errorf("logs: unknown adaptername %q (forgotten Register?)", adaptername)
}
}
func (bl *BeeLogger) writerMsg(loglevel int, msg string) error {
lm := new(logMsg)
lm.level = loglevel
if bl.enableFuncCallDepth {
_, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
if !ok {
file = "???"
line = 0
}
_, filename := path.Split(file)
lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg)
} else {
lm.msg = msg
}
if bl.asynchronous {
bl.msg <- lm
} else {
for name, l := range bl.outputs {
err := l.WriteMsg(lm.msg, lm.level)
if err != nil {
fmt.Println("unable to WriteMsg to adapter:", name, err)
return err
}
}
}
return nil
}
// Set log message level.
//
// If message level (such as LevelDebug) is higher than logger level (such as LevelWarning),
// log providers will not even be sent the message.
func (bl *BeeLogger) SetLevel(l int) {
bl.level = l
}
// set log funcCallDepth
func (bl *BeeLogger) SetLogFuncCallDepth(d int) {
bl.loggerFuncCallDepth = d
}
// get log funcCallDepth for wrapper
func (bl *BeeLogger) GetLogFuncCallDepth() int {
return bl.loggerFuncCallDepth
}
// enable log funcCallDepth
func (bl *BeeLogger) EnableFuncCallDepth(b bool) {
bl.enableFuncCallDepth = b
}
// start logger chan reading.
// when chan is not empty, write logs.
func (bl *BeeLogger) startLogger() {
for {
select {
case bm := <-bl.msg:
for _, l := range bl.outputs {
err := l.WriteMsg(bm.msg, bm.level)
if err != nil {
fmt.Println("ERROR, unable to WriteMsg:", err)
}
}
}
}
}
// Log EMERGENCY level message.
func (bl *BeeLogger) Emergency(format string, v ...interface{}) {
if LevelEmergency > bl.level {
return
}
msg := fmt.Sprintf("[M] "+format, v...)
bl.writerMsg(LevelEmergency, msg)
}
// Log ALERT level message.
func (bl *BeeLogger) Alert(format string, v ...interface{}) {
if LevelAlert > bl.level {
return
}
msg := fmt.Sprintf("[A] "+format, v...)
bl.writerMsg(LevelAlert, msg)
}
// Log CRITICAL level message.
func (bl *BeeLogger) Critical(format string, v ...interface{}) {
if LevelCritical > bl.level {
return
}
msg := fmt.Sprintf("[C] "+format, v...)
bl.writerMsg(LevelCritical, msg)
}
// Log ERROR level message.
func (bl *BeeLogger) Error(format string, v ...interface{}) {
if LevelError > bl.level {
return
}
msg := fmt.Sprintf("[E] "+format, v...)
bl.writerMsg(LevelError, msg)
}
// Log WARNING level message.
func (bl *BeeLogger) Warning(format string, v ...interface{}) {
if LevelWarning > bl.level {
return
}
msg := fmt.Sprintf("[W] "+format, v...)
bl.writerMsg(LevelWarning, msg)
}
// Log NOTICE level message.
func (bl *BeeLogger) Notice(format string, v ...interface{}) {
if LevelNotice > bl.level {
return
}
msg := fmt.Sprintf("[N] "+format, v...)
bl.writerMsg(LevelNotice, msg)
}
// Log INFORMATIONAL level message.
func (bl *BeeLogger) Informational(format string, v ...interface{}) {
if LevelInformational > bl.level {
return
}
msg := fmt.Sprintf("[I] "+format, v...)
bl.writerMsg(LevelInformational, msg)
}
// Log DEBUG level message.
func (bl *BeeLogger) Debug(format string, v ...interface{}) {
if LevelDebug > bl.level {
return
}
msg := fmt.Sprintf("[D] "+format, v...)
bl.writerMsg(LevelDebug, msg)
}
// Log WARN level message.
// compatibility alias for Warning()
func (bl *BeeLogger) Warn(format string, v ...interface{}) {
if LevelWarning > bl.level {
return
}
msg := fmt.Sprintf("[W] "+format, v...)
bl.writerMsg(LevelWarning, msg)
}
// Log INFO level message.
// compatibility alias for Informational()
func (bl *BeeLogger) Info(format string, v ...interface{}) {
if LevelInformational > bl.level {
return
}
msg := fmt.Sprintf("[I] "+format, v...)
bl.writerMsg(LevelInformational, msg)
}
// Log TRACE level message.
// compatibility alias for Debug()
func (bl *BeeLogger) Trace(format string, v ...interface{}) {
if LevelDebug > bl.level {
return
}
msg := fmt.Sprintf("[D] "+format, v...)
bl.writerMsg(LevelDebug, msg)
}
// flush all chan data.
func (bl *BeeLogger) Flush() {
for _, l := range bl.outputs {
l.Flush()
}
}
// close logger, flush all chan data and destroy all adapters in BeeLogger.
func (bl *BeeLogger) Close() {
for {
if len(bl.msg) > 0 {
bm := <-bl.msg
for _, l := range bl.outputs {
err := l.WriteMsg(bm.msg, bm.level)
if err != nil {
fmt.Println("ERROR, unable to WriteMsg (while closing logger):", err)
}
}
continue
}
break
}
for _, l := range bl.outputs {
l.Flush()
l.Destroy()
}
}

View File

@@ -1,165 +0,0 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// 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 logs
import (
"crypto/tls"
"encoding/json"
"fmt"
"net"
"net/smtp"
"strings"
"time"
)
const (
// no usage
// subjectPhrase = "Diagnostic message from server"
)
// smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server.
type SmtpWriter struct {
Username string `json:"Username"`
Password string `json:"password"`
Host string `json:"Host"`
Subject string `json:"subject"`
FromAddress string `json:"fromAddress"`
RecipientAddresses []string `json:"sendTos"`
Level int `json:"level"`
}
// create smtp writer.
func NewSmtpWriter() LoggerInterface {
return &SmtpWriter{Level: LevelTrace}
}
// init smtp writer with json config.
// config like:
// {
// "Username":"example@gmail.com",
// "password:"password",
// "host":"smtp.gmail.com:465",
// "subject":"email title",
// "fromAddress":"from@example.com",
// "sendTos":["email1","email2"],
// "level":LevelError
// }
func (s *SmtpWriter) Init(jsonconfig string) error {
err := json.Unmarshal([]byte(jsonconfig), s)
if err != nil {
return err
}
return nil
}
func (s *SmtpWriter) GetSmtpAuth(host string) smtp.Auth {
if len(strings.Trim(s.Username, " ")) == 0 && len(strings.Trim(s.Password, " ")) == 0 {
return nil
}
return smtp.PlainAuth(
"",
s.Username,
s.Password,
host,
)
}
func (s *SmtpWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAddress string, recipients []string, msgContent []byte) error {
client, err := smtp.Dial(hostAddressWithPort)
if err != nil {
return err
}
host, _, _ := net.SplitHostPort(hostAddressWithPort)
tlsConn := &tls.Config{
InsecureSkipVerify: true,
ServerName: host,
}
if err = client.StartTLS(tlsConn); err != nil {
return err
}
if auth != nil {
if err = client.Auth(auth); err != nil {
return err
}
}
if err = client.Mail(fromAddress); err != nil {
return err
}
for _, rec := range recipients {
if err = client.Rcpt(rec); err != nil {
return err
}
}
w, err := client.Data()
if err != nil {
return err
}
_, err = w.Write([]byte(msgContent))
if err != nil {
return err
}
err = w.Close()
if err != nil {
return err
}
err = client.Quit()
if err != nil {
return err
}
return nil
}
// write message in smtp writer.
// it will send an email with subject and only this message.
func (s *SmtpWriter) WriteMsg(msg string, level int) error {
if level > s.Level {
return nil
}
hp := strings.Split(s.Host, ":")
// Set up authentication information.
auth := s.GetSmtpAuth(hp[0])
// Connect to the server, authenticate, set the sender and recipient,
// and send the email all in one step.
content_type := "Content-Type: text/plain" + "; charset=UTF-8"
mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress +
">\r\nSubject: " + s.Subject + "\r\n" + content_type + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg)
return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg)
}
// implementing method. empty.
func (s *SmtpWriter) Flush() {
return
}
// implementing method. empty.
func (s *SmtpWriter) Destroy() {
return
}
func init() {
Register("smtp", NewSmtpWriter)
}

View File

@@ -1,19 +0,0 @@
Copyright (c) 2011-2014 Dmitry Chestnykh <dmitry@codingrobots.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -1,25 +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
# coverage droppings
profile.cov

View File

@@ -1,31 +0,0 @@
# Travis CI (http://travis-ci.org/) is a continuous integration
# service for open source projects. This file configures it
# to run unit tests for docopt-go.
language: go
go:
- 1.4
- 1.5
- tip
matrix:
fast_finish: true
before_install:
- go get golang.org/x/tools/cmd/vet
- go get golang.org/x/tools/cmd/cover
- go get github.com/golang/lint/golint
- go get github.com/mattn/goveralls
install:
- go get -d -v ./... && go build -v ./...
script:
- go vet -x ./...
- $HOME/gopath/bin/golint ./...
- go test -v ./...
- go test -covermode=count -coverprofile=profile.cov .
after_script:
- $HOME/gopath/bin/goveralls -coverprofile=profile.cov -service=travis-ci

View File

@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2013 Keith Batten
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,88 +0,0 @@
docopt-go
=========
[![Build Status](https://travis-ci.org/docopt/docopt.go.svg?branch=master)](https://travis-ci.org/docopt/docopt.go)
[![Coverage Status](https://coveralls.io/repos/docopt/docopt.go/badge.png)](https://coveralls.io/r/docopt/docopt.go)
[![GoDoc](https://godoc.org/github.com/docopt/docopt.go?status.png)](https://godoc.org/github.com/docopt/docopt.go)
An implementation of [docopt](http://docopt.org/) in the
[Go](http://golang.org/) programming language.
**docopt** helps you create *beautiful* command-line interfaces easily:
```go
package main
import (
"fmt"
"github.com/docopt/docopt-go"
)
func main() {
usage := `Naval Fate.
Usage:
naval_fate ship new <name>...
naval_fate ship <name> move <x> <y> [--speed=<kn>]
naval_fate ship shoot <x> <y>
naval_fate mine (set|remove) <x> <y> [--moored|--drifting]
naval_fate -h | --help
naval_fate --version
Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--moored Moored (anchored) mine.
--drifting Drifting mine.`
arguments, _ := docopt.Parse(usage, nil, true, "Naval Fate 2.0", false)
fmt.Println(arguments)
}
```
**docopt** parses command-line arguments based on a help message. Don't
write parser code: a good help message already has all the necessary
information in it.
## Installation
⚠ Use the alias “docopt-go”. To use docopt in your Go code:
```go
import "github.com/docopt/docopt-go"
```
To install docopt according to your `$GOPATH`:
```console
$ go get github.com/docopt/docopt-go
```
## API
```go
func Parse(doc string, argv []string, help bool, version string,
optionsFirst bool, exit ...bool) (map[string]interface{}, error)
```
Parse `argv` based on the command-line interface described in `doc`.
Given a conventional command-line help message, docopt creates a parser and
processes the arguments. See
https://github.com/docopt/docopt#help-message-format for a description of the
help message format. If `argv` is `nil`, `os.Args[1:]` is used.
docopt returns a map of option names to the values parsed from `argv`, and an
error or `nil`.
More documentation for docopt is available at
[GoDoc.org](https://godoc.org/github.com/docopt/docopt.go).
## Testing
All tests from the Python version are implemented and passing
at [Travis CI](https://travis-ci.org/docopt/docopt.go). New
language-agnostic tests have been added
to [test_golang.docopt](test_golang.docopt).
To run tests for docopt-go, use `go test`.

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +0,0 @@
r"""usage: prog [NAME_-2]..."""
$ prog 10 20
{"NAME_-2": ["10", "20"]}
$ prog 10
{"NAME_-2": ["10"]}
$ prog
{"NAME_-2": []}

View File

@@ -1,957 +0,0 @@
r"""Usage: prog
"""
$ prog
{}
$ prog --xxx
"user-error"
r"""Usage: prog [options]
Options: -a All.
"""
$ prog
{"-a": false}
$ prog -a
{"-a": true}
$ prog -x
"user-error"
r"""Usage: prog [options]
Options: --all All.
"""
$ prog
{"--all": false}
$ prog --all
{"--all": true}
$ prog --xxx
"user-error"
r"""Usage: prog [options]
Options: -v, --verbose Verbose.
"""
$ prog --verbose
{"--verbose": true}
$ prog --ver
{"--verbose": true}
$ prog -v
{"--verbose": true}
r"""Usage: prog [options]
Options: -p PATH
"""
$ prog -p home/
{"-p": "home/"}
$ prog -phome/
{"-p": "home/"}
$ prog -p
"user-error"
r"""Usage: prog [options]
Options: --path <path>
"""
$ prog --path home/
{"--path": "home/"}
$ prog --path=home/
{"--path": "home/"}
$ prog --pa home/
{"--path": "home/"}
$ prog --pa=home/
{"--path": "home/"}
$ prog --path
"user-error"
r"""Usage: prog [options]
Options: -p PATH, --path=<path> Path to files.
"""
$ prog -proot
{"--path": "root"}
r"""Usage: prog [options]
Options: -p --path PATH Path to files.
"""
$ prog -p root
{"--path": "root"}
$ prog --path root
{"--path": "root"}
r"""Usage: prog [options]
Options:
-p PATH Path to files [default: ./]
"""
$ prog
{"-p": "./"}
$ prog -phome
{"-p": "home"}
r"""UsAgE: prog [options]
OpTiOnS: --path=<files> Path to files
[dEfAuLt: /root]
"""
$ prog
{"--path": "/root"}
$ prog --path=home
{"--path": "home"}
r"""usage: prog [options]
options:
-a Add
-r Remote
-m <msg> Message
"""
$ prog -a -r -m Hello
{"-a": true,
"-r": true,
"-m": "Hello"}
$ prog -armyourass
{"-a": true,
"-r": true,
"-m": "yourass"}
$ prog -a -r
{"-a": true,
"-r": true,
"-m": null}
r"""Usage: prog [options]
Options: --version
--verbose
"""
$ prog --version
{"--version": true,
"--verbose": false}
$ prog --verbose
{"--version": false,
"--verbose": true}
$ prog --ver
"user-error"
$ prog --verb
{"--version": false,
"--verbose": true}
r"""usage: prog [-a -r -m <msg>]
options:
-a Add
-r Remote
-m <msg> Message
"""
$ prog -armyourass
{"-a": true,
"-r": true,
"-m": "yourass"}
r"""usage: prog [-armmsg]
options: -a Add
-r Remote
-m <msg> Message
"""
$ prog -a -r -m Hello
{"-a": true,
"-r": true,
"-m": "Hello"}
r"""usage: prog -a -b
options:
-a
-b
"""
$ prog -a -b
{"-a": true, "-b": true}
$ prog -b -a
{"-a": true, "-b": true}
$ prog -a
"user-error"
$ prog
"user-error"
r"""usage: prog (-a -b)
options: -a
-b
"""
$ prog -a -b
{"-a": true, "-b": true}
$ prog -b -a
{"-a": true, "-b": true}
$ prog -a
"user-error"
$ prog
"user-error"
r"""usage: prog [-a] -b
options: -a
-b
"""
$ prog -a -b
{"-a": true, "-b": true}
$ prog -b -a
{"-a": true, "-b": true}
$ prog -a
"user-error"
$ prog -b
{"-a": false, "-b": true}
$ prog
"user-error"
r"""usage: prog [(-a -b)]
options: -a
-b
"""
$ prog -a -b
{"-a": true, "-b": true}
$ prog -b -a
{"-a": true, "-b": true}
$ prog -a
"user-error"
$ prog -b
"user-error"
$ prog
{"-a": false, "-b": false}
r"""usage: prog (-a|-b)
options: -a
-b
"""
$ prog -a -b
"user-error"
$ prog
"user-error"
$ prog -a
{"-a": true, "-b": false}
$ prog -b
{"-a": false, "-b": true}
r"""usage: prog [ -a | -b ]
options: -a
-b
"""
$ prog -a -b
"user-error"
$ prog
{"-a": false, "-b": false}
$ prog -a
{"-a": true, "-b": false}
$ prog -b
{"-a": false, "-b": true}
r"""usage: prog <arg>"""
$ prog 10
{"<arg>": "10"}
$ prog 10 20
"user-error"
$ prog
"user-error"
r"""usage: prog [<arg>]"""
$ prog 10
{"<arg>": "10"}
$ prog 10 20
"user-error"
$ prog
{"<arg>": null}
r"""usage: prog <kind> <name> <type>"""
$ prog 10 20 40
{"<kind>": "10", "<name>": "20", "<type>": "40"}
$ prog 10 20
"user-error"
$ prog
"user-error"
r"""usage: prog <kind> [<name> <type>]"""
$ prog 10 20 40
{"<kind>": "10", "<name>": "20", "<type>": "40"}
$ prog 10 20
{"<kind>": "10", "<name>": "20", "<type>": null}
$ prog
"user-error"
r"""usage: prog [<kind> | <name> <type>]"""
$ prog 10 20 40
"user-error"
$ prog 20 40
{"<kind>": null, "<name>": "20", "<type>": "40"}
$ prog
{"<kind>": null, "<name>": null, "<type>": null}
r"""usage: prog (<kind> --all | <name>)
options:
--all
"""
$ prog 10 --all
{"<kind>": "10", "--all": true, "<name>": null}
$ prog 10
{"<kind>": null, "--all": false, "<name>": "10"}
$ prog
"user-error"
r"""usage: prog [<name> <name>]"""
$ prog 10 20
{"<name>": ["10", "20"]}
$ prog 10
{"<name>": ["10"]}
$ prog
{"<name>": []}
r"""usage: prog [(<name> <name>)]"""
$ prog 10 20
{"<name>": ["10", "20"]}
$ prog 10
"user-error"
$ prog
{"<name>": []}
r"""usage: prog NAME..."""
$ prog 10 20
{"NAME": ["10", "20"]}
$ prog 10
{"NAME": ["10"]}
$ prog
"user-error"
r"""usage: prog [NAME]..."""
$ prog 10 20
{"NAME": ["10", "20"]}
$ prog 10
{"NAME": ["10"]}
$ prog
{"NAME": []}
r"""usage: prog [NAME...]"""
$ prog 10 20
{"NAME": ["10", "20"]}
$ prog 10
{"NAME": ["10"]}
$ prog
{"NAME": []}
r"""usage: prog [NAME [NAME ...]]"""
$ prog 10 20
{"NAME": ["10", "20"]}
$ prog 10
{"NAME": ["10"]}
$ prog
{"NAME": []}
r"""usage: prog (NAME | --foo NAME)
options: --foo
"""
$ prog 10
{"NAME": "10", "--foo": false}
$ prog --foo 10
{"NAME": "10", "--foo": true}
$ prog --foo=10
"user-error"
r"""usage: prog (NAME | --foo) [--bar | NAME]
options: --foo
options: --bar
"""
$ prog 10
{"NAME": ["10"], "--foo": false, "--bar": false}
$ prog 10 20
{"NAME": ["10", "20"], "--foo": false, "--bar": false}
$ prog --foo --bar
{"NAME": [], "--foo": true, "--bar": true}
r"""Naval Fate.
Usage:
prog ship new <name>...
prog ship [<name>] move <x> <y> [--speed=<kn>]
prog ship shoot <x> <y>
prog mine (set|remove) <x> <y> [--moored|--drifting]
prog -h | --help
prog --version
Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--moored Mored (anchored) mine.
--drifting Drifting mine.
"""
$ prog ship Guardian move 150 300 --speed=20
{"--drifting": false,
"--help": false,
"--moored": false,
"--speed": "20",
"--version": false,
"<name>": ["Guardian"],
"<x>": "150",
"<y>": "300",
"mine": false,
"move": true,
"new": false,
"remove": false,
"set": false,
"ship": true,
"shoot": false}
r"""usage: prog --hello"""
$ prog --hello
{"--hello": true}
r"""usage: prog [--hello=<world>]"""
$ prog
{"--hello": null}
$ prog --hello wrld
{"--hello": "wrld"}
r"""usage: prog [-o]"""
$ prog
{"-o": false}
$ prog -o
{"-o": true}
r"""usage: prog [-opr]"""
$ prog -op
{"-o": true, "-p": true, "-r": false}
r"""usage: prog --aabb | --aa"""
$ prog --aa
{"--aabb": false, "--aa": true}
$ prog --a
"user-error" # not a unique prefix
#
# Counting number of flags
#
r"""Usage: prog -v"""
$ prog -v
{"-v": true}
r"""Usage: prog [-v -v]"""
$ prog
{"-v": 0}
$ prog -v
{"-v": 1}
$ prog -vv
{"-v": 2}
r"""Usage: prog -v ..."""
$ prog
"user-error"
$ prog -v
{"-v": 1}
$ prog -vv
{"-v": 2}
$ prog -vvvvvv
{"-v": 6}
r"""Usage: prog [-v | -vv | -vvv]
This one is probably most readable user-friednly variant.
"""
$ prog
{"-v": 0}
$ prog -v
{"-v": 1}
$ prog -vv
{"-v": 2}
$ prog -vvvv
"user-error"
r"""usage: prog [--ver --ver]"""
$ prog --ver --ver
{"--ver": 2}
#
# Counting commands
#
r"""usage: prog [go]"""
$ prog go
{"go": true}
r"""usage: prog [go go]"""
$ prog
{"go": 0}
$ prog go
{"go": 1}
$ prog go go
{"go": 2}
$ prog go go go
"user-error"
r"""usage: prog go..."""
$ prog go go go go go
{"go": 5}
#
# [options] does not include options from usage-pattern
#
r"""usage: prog [options] [-a]
options: -a
-b
"""
$ prog -a
{"-a": true, "-b": false}
$ prog -aa
"user-error"
#
# Test [options] shourtcut
#
r"""Usage: prog [options] A
Options:
-q Be quiet
-v Be verbose.
"""
$ prog arg
{"A": "arg", "-v": false, "-q": false}
$ prog -v arg
{"A": "arg", "-v": true, "-q": false}
$ prog -q arg
{"A": "arg", "-v": false, "-q": true}
#
# Test single dash
#
r"""usage: prog [-]"""
$ prog -
{"-": true}
$ prog
{"-": false}
#
# If argument is repeated, its value should always be a list
#
r"""usage: prog [NAME [NAME ...]]"""
$ prog a b
{"NAME": ["a", "b"]}
$ prog
{"NAME": []}
#
# Option's argument defaults to null/None
#
r"""usage: prog [options]
options:
-a Add
-m <msg> Message
"""
$ prog -a
{"-m": null, "-a": true}
#
# Test options without description
#
r"""usage: prog --hello"""
$ prog --hello
{"--hello": true}
r"""usage: prog [--hello=<world>]"""
$ prog
{"--hello": null}
$ prog --hello wrld
{"--hello": "wrld"}
r"""usage: prog [-o]"""
$ prog
{"-o": false}
$ prog -o
{"-o": true}
r"""usage: prog [-opr]"""
$ prog -op
{"-o": true, "-p": true, "-r": false}
r"""usage: git [-v | --verbose]"""
$ prog -v
{"-v": true, "--verbose": false}
r"""usage: git remote [-v | --verbose]"""
$ prog remote -v
{"remote": true, "-v": true, "--verbose": false}
#
# Test empty usage pattern
#
r"""usage: prog"""
$ prog
{}
r"""usage: prog
prog <a> <b>
"""
$ prog 1 2
{"<a>": "1", "<b>": "2"}
$ prog
{"<a>": null, "<b>": null}
r"""usage: prog <a> <b>
prog
"""
$ prog
{"<a>": null, "<b>": null}
#
# Option's argument should not capture default value from usage pattern
#
r"""usage: prog [--file=<f>]"""
$ prog
{"--file": null}
r"""usage: prog [--file=<f>]
options: --file <a>
"""
$ prog
{"--file": null}
r"""Usage: prog [-a <host:port>]
Options: -a, --address <host:port> TCP address [default: localhost:6283].
"""
$ prog
{"--address": "localhost:6283"}
#
# If option with argument could be repeated,
# its arguments should be accumulated into a list
#
r"""usage: prog --long=<arg> ..."""
$ prog --long one
{"--long": ["one"]}
$ prog --long one --long two
{"--long": ["one", "two"]}
#
# Test multiple elements repeated at once
#
r"""usage: prog (go <direction> --speed=<km/h>)..."""
$ prog go left --speed=5 go right --speed=9
{"go": 2, "<direction>": ["left", "right"], "--speed": ["5", "9"]}
#
# Required options should work with option shortcut
#
r"""usage: prog [options] -a
options: -a
"""
$ prog -a
{"-a": true}
#
# If option could be repeated its defaults should be split into a list
#
r"""usage: prog [-o <o>]...
options: -o <o> [default: x]
"""
$ prog -o this -o that
{"-o": ["this", "that"]}
$ prog
{"-o": ["x"]}
r"""usage: prog [-o <o>]...
options: -o <o> [default: x y]
"""
$ prog -o this
{"-o": ["this"]}
$ prog
{"-o": ["x", "y"]}
#
# Test stacked option's argument
#
r"""usage: prog -pPATH
options: -p PATH
"""
$ prog -pHOME
{"-p": "HOME"}
#
# Issue 56: Repeated mutually exclusive args give nested lists sometimes
#
r"""Usage: foo (--xx=x|--yy=y)..."""
$ prog --xx=1 --yy=2
{"--xx": ["1"], "--yy": ["2"]}
#
# POSIXly correct tokenization
#
r"""usage: prog [<input file>]"""
$ prog f.txt
{"<input file>": "f.txt"}
r"""usage: prog [--input=<file name>]..."""
$ prog --input a.txt --input=b.txt
{"--input": ["a.txt", "b.txt"]}
#
# Issue 85: `[options]` shourtcut with multiple subcommands
#
r"""usage: prog good [options]
prog fail [options]
options: --loglevel=N
"""
$ prog fail --loglevel 5
{"--loglevel": "5", "fail": true, "good": false}
#
# Usage-section syntax
#
r"""usage:prog --foo"""
$ prog --foo
{"--foo": true}
r"""PROGRAM USAGE: prog --foo"""
$ prog --foo
{"--foo": true}
r"""Usage: prog --foo
prog --bar
NOT PART OF SECTION"""
$ prog --foo
{"--foo": true, "--bar": false}
r"""Usage:
prog --foo
prog --bar
NOT PART OF SECTION"""
$ prog --foo
{"--foo": true, "--bar": false}
r"""Usage:
prog --foo
prog --bar
NOT PART OF SECTION"""
$ prog --foo
{"--foo": true, "--bar": false}
#
# Options-section syntax
#
r"""Usage: prog [options]
global options: --foo
local options: --baz
--bar
other options:
--egg
--spam
-not-an-option-
"""
$ prog --baz --egg
{"--foo": false, "--baz": true, "--bar": false, "--egg": true, "--spam": false}

View File

@@ -1,14 +0,0 @@
Copyright (c) 2013 Vaughan Newton
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,70 +0,0 @@
go-ini
======
INI parsing library for Go (golang).
View the API documentation [here](http://godoc.org/github.com/vaughan0/go-ini).
Usage
-----
Parse an INI file:
```go
import "github.com/vaughan0/go-ini"
file, err := ini.LoadFile("myfile.ini")
```
Get data from the parsed file:
```go
name, ok := file.Get("person", "name")
if !ok {
panic("'name' variable missing from 'person' section")
}
```
Iterate through values in a section:
```go
for key, value := range file["mysection"] {
fmt.Printf("%s => %s\n", key, value)
}
```
Iterate through sections in a file:
```go
for name, section := range file {
fmt.Printf("Section name: %s\n", name)
}
```
File Format
-----------
INI files are parsed by go-ini line-by-line. Each line may be one of the following:
* A section definition: [section-name]
* A property: key = value
* A comment: #blahblah _or_ ;blahblah
* Blank. The line will be ignored.
Properties defined before any section headers are placed in the default section, which has
the empty string as it's key.
Example:
```ini
# I am a comment
; So am I!
[apples]
colour = red or green
shape = applish
[oranges]
shape = square
colour = blue
```

View File

@@ -1,123 +0,0 @@
// Package ini provides functions for parsing INI configuration files.
package ini
import (
"bufio"
"fmt"
"io"
"os"
"regexp"
"strings"
)
var (
sectionRegex = regexp.MustCompile(`^\[(.*)\]$`)
assignRegex = regexp.MustCompile(`^([^=]+)=(.*)$`)
)
// ErrSyntax is returned when there is a syntax error in an INI file.
type ErrSyntax struct {
Line int
Source string // The contents of the erroneous line, without leading or trailing whitespace
}
func (e ErrSyntax) Error() string {
return fmt.Sprintf("invalid INI syntax on line %d: %s", e.Line, e.Source)
}
// A File represents a parsed INI file.
type File map[string]Section
// A Section represents a single section of an INI file.
type Section map[string]string
// Returns a named Section. A Section will be created if one does not already exist for the given name.
func (f File) Section(name string) Section {
section := f[name]
if section == nil {
section = make(Section)
f[name] = section
}
return section
}
// Looks up a value for a key in a section and returns that value, along with a boolean result similar to a map lookup.
func (f File) Get(section, key string) (value string, ok bool) {
if s := f[section]; s != nil {
value, ok = s[key]
}
return
}
// Loads INI data from a reader and stores the data in the File.
func (f File) Load(in io.Reader) (err error) {
bufin, ok := in.(*bufio.Reader)
if !ok {
bufin = bufio.NewReader(in)
}
return parseFile(bufin, f)
}
// Loads INI data from a named file and stores the data in the File.
func (f File) LoadFile(file string) (err error) {
in, err := os.Open(file)
if err != nil {
return
}
defer in.Close()
return f.Load(in)
}
func parseFile(in *bufio.Reader, file File) (err error) {
section := ""
lineNum := 0
for done := false; !done; {
var line string
if line, err = in.ReadString('\n'); err != nil {
if err == io.EOF {
done = true
} else {
return
}
}
lineNum++
line = strings.TrimSpace(line)
if len(line) == 0 {
// Skip blank lines
continue
}
if line[0] == ';' || line[0] == '#' {
// Skip comments
continue
}
if groups := assignRegex.FindStringSubmatch(line); groups != nil {
key, val := groups[1], groups[2]
key, val = strings.TrimSpace(key), strings.TrimSpace(val)
file.Section(section)[key] = val
} else if groups := sectionRegex.FindStringSubmatch(line); groups != nil {
name := strings.TrimSpace(groups[1])
section = name
// Create the section if it does not exist
file.Section(section)
} else {
return ErrSyntax{lineNum, line}
}
}
return nil
}
// Loads and returns a File from a reader.
func Load(in io.Reader) (File, error) {
file := make(File)
err := file.Load(in)
return file, err
}
// Loads and returns an INI File from a file on disk.
func LoadFile(filename string) (File, error) {
file := make(File)
err := file.LoadFile(filename)
return file, err
}

View File

@@ -1,2 +0,0 @@
[default]
stuff = things

View File

@@ -1,21 +1,47 @@
export PATH := $(GOPATH)/bin:$(PATH)
export NEW_GOPATH := $(shell pwd)
export GO111MODULE=on
LDFLAGS := -s -w
all: build
all: fmt build
build: godep fmt frps frpc
build: frps frpc
godep:
@go get github.com/tools/godep
# compile assets into binary file
file:
rm -rf ./assets/frps/static/*
rm -rf ./assets/frpc/static/*
cp -rf ./web/frps/dist/* ./assets/frps/static
cp -rf ./web/frpc/dist/* ./assets/frpc/static
fmt:
GOPATH=$(NEW_GOPATH) godep go fmt ./...
go fmt ./...
vet:
go vet ./...
frps:
GOPATH=$(NEW_GOPATH) godep go build -o bin/frps ./src/frp/cmd/frps
env CGO_ENABLED=0 go build -trimpath -ldflags "$(LDFLAGS)" -o bin/frps ./cmd/frps
frpc:
GOPATH=$(NEW_GOPATH) godep go build -o bin/frpc ./src/frp/cmd/frpc
env CGO_ENABLED=0 go build -trimpath -ldflags "$(LDFLAGS)" -o bin/frpc ./cmd/frpc
test:
@GOPATH=$(NEW_GOPATH) godep go test -v ./...
test: gotest
gotest:
go test -v --cover ./assets/...
go test -v --cover ./cmd/...
go test -v --cover ./client/...
go test -v --cover ./server/...
go test -v --cover ./pkg/...
e2e:
./hack/run-e2e.sh
e2e-trace:
DEBUG=true LOG_LEVEL=trace ./hack/run-e2e.sh
alltest: vet gotest e2e
clean:
rm -f ./bin/frpc
rm -f ./bin/frps

25
Makefile.cross-compiles Normal file
View File

@@ -0,0 +1,25 @@
export PATH := $(GOPATH)/bin:$(PATH)
export GO111MODULE=on
LDFLAGS := -s -w
os-archs=darwin:amd64 darwin:arm64 freebsd:386 freebsd:amd64 linux:386 linux:amd64 linux:arm linux:arm64 windows:386 windows:amd64 linux:mips64 linux:mips64le linux:mips:softfloat linux:mipsle:softfloat
all: build
build: app
app:
@$(foreach n, $(os-archs),\
os=$(shell echo "$(n)" | cut -d : -f 1);\
arch=$(shell echo "$(n)" | cut -d : -f 2);\
gomips=$(shell echo "$(n)" | cut -d : -f 3);\
target_suffix=$${os}_$${arch};\
echo "Build $${os}-$${arch}...";\
env CGO_ENABLED=0 GOOS=$${os} GOARCH=$${arch} GOMIPS=$${gomips} go build -trimpath -ldflags "$(LDFLAGS)" -o ./release/frpc_$${target_suffix} ./cmd/frpc;\
env CGO_ENABLED=0 GOOS=$${os} GOARCH=$${arch} GOMIPS=$${gomips} go build -trimpath -ldflags "$(LDFLAGS)" -o ./release/frps_$${target_suffix} ./cmd/frps;\
echo "Build $${os}-$${arch} done";\
)
@mv ./release/frpc_windows_386 ./release/frpc_windows_386.exe
@mv ./release/frps_windows_386 ./release/frps_windows_386.exe
@mv ./release/frpc_windows_amd64 ./release/frpc_windows_amd64.exe
@mv ./release/frps_windows_amd64 ./release/frps_windows_amd64.exe

1108
README.md

File diff suppressed because it is too large Load Diff

97
README_zh.md Normal file
View File

@@ -0,0 +1,97 @@
# frp
[![Build Status](https://travis-ci.org/fatedier/frp.svg?branch=master)](https://travis-ci.org/fatedier/frp)
[![GitHub release](https://img.shields.io/github/tag/fatedier/frp.svg?label=release)](https://github.com/fatedier/frp/releases)
[README](README.md) | [中文文档](README_zh.md)
frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。
<h3 align="center">Platinum Sponsors</h3>
<!--platinum sponsors start-->
<p align="center">
<a href="https://www.doppler.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
<img width="400px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_doppler.png">
</a>
</p>
<!--platinum sponsors end-->
<h3 align="center">Gold Sponsors</h3>
<!--gold sponsors start-->
<p align="center">
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=frp&utm_source=github" target="_blank">
<img width="300px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_workos.png">
</a>
</p>
<!--gold sponsors end-->
<h3 align="center">Silver Sponsors</h3>
* Sakura Frp - 欢迎点击 "加入我们"
## 为什么使用 frp
通过在具有公网 IP 的节点上部署 frp 服务端,可以轻松地将内网服务穿透到公网,同时提供诸多专业的功能特性,这包括:
* 客户端服务端通信支持 TCP、KCP 以及 Websocket 等多种协议。
* 采用 TCP 连接流式复用,在单个连接间承载更多请求,节省连接建立时间。
* 代理组间的负载均衡。
* 端口复用,多个服务通过同一个服务端端口暴露。
* 多个原生支持的客户端插件静态文件查看HTTP、SOCK5 代理等),便于独立使用 frp 客户端完成某些工作。
* 高度扩展性的服务端插件系统,方便结合自身需求进行功能扩展。
* 服务端和客户端 UI 页面。
## 开发状态
frp 目前已被很多公司广泛用于测试、生产环境。
master 分支用于发布稳定版本dev 分支用于开发,您可以尝试下载最新的 release 版本进行测试。
我们正在进行 v2 大版本的开发,将会尝试在各个方面进行重构和升级,且不会与 v1 版本进行兼容,预计会持续一段时间。
现在的 v0 版本将会在合适的时间切换为 v1 版本并且保证兼容性,后续只做 bug 修复和优化,不再进行大的功能性更新。
## 文档
完整文档已经迁移至 [https://gofrp.org](https://gofrp.org/docs)。
## 为 frp 做贡献
frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进步贡献力量。
* 在使用过程中出现任何问题,可以通过 [issues](https://github.com/fatedier/frp/issues) 来反馈。
* Bug 的修复可以直接提交 Pull Request 到 dev 分支。
* 如果是增加新的功能特性,请先创建一个 issue 并做简单描述以及大致的实现方法,提议被采纳后,就可以创建一个实现新特性的 Pull Request。
* 欢迎对说明文档做出改善,帮助更多的人使用 frp特别是英文文档。
* 贡献代码请提交 PR 至 dev 分支master 分支仅用于发布稳定可用版本。
* 如果你有任何其他方面的问题或合作,欢迎发送邮件至 fatedier@gmail.com 。
**提醒:和项目相关的问题最好在 [issues](https://github.com/fatedier/frp/issues) 中反馈,这样方便其他有类似问题的人可以快速查找解决方法,并且也避免了我们重复回答一些问题。**
## 捐助
如果您觉得 frp 对你有帮助,欢迎给予我们一定的捐助来维持项目的长期发展。
### GitHub Sponsors
您可以通过 [GitHub Sponsors](https://github.com/sponsors/fatedier) 赞助我们。
企业赞助者可以将贵公司的 Logo 以及链接放置在项目 README 文件中。
### 知识星球
如果您想学习 frp 相关的知识和技术,或者寻求任何帮助及咨询,都可以通过微信扫描下方的二维码付费加入知识星球的官方社群:
![zsxq](/doc/pic/zsxq.jpg)
### 支付宝扫码捐赠
![donate-alipay](/doc/pic/donate-alipay.png)
### 微信支付捐赠
![donate-wechatpay](/doc/pic/donate-wechatpay.png)

7
Release.md Normal file
View File

@@ -0,0 +1,7 @@
### New
* Added new parameter `config_dir` in frpc to run multiple client instances in one process.
### Fix
* Equal sign in environment variables causes parsing error.

48
assets/assets.go Normal file
View File

@@ -0,0 +1,48 @@
// 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 assets
import (
"io/fs"
"net/http"
)
var (
// read-only filesystem created by "embed" for embedded files
content fs.FS
FileSystem http.FileSystem
// if prefix is not empty, we get file content from disk
prefixPath string
)
// if path is empty, load assets in memory
// or set FileSystem using disk files
func Load(path string) {
prefixPath = path
if prefixPath != "" {
FileSystem = http.Dir(prefixPath)
} else {
FileSystem = http.FS(content)
}
}
func Register(fileSystem fs.FS) {
subFs, err := fs.Sub(fileSystem, "static")
if err == nil {
content = subFs
}
}

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

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

View File

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

File diff suppressed because one or more lines are too long

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -0,0 +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?5d154ba4c6b342d8c0c3"></script><script type="text/javascript" src="vendor.js?ddbd1f69fb6e67be4b78"></script></body> </html>

View File

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

File diff suppressed because one or more lines are too long

82
client/admin.go Normal file
View File

@@ -0,0 +1,82 @@
// 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 client
import (
"net"
"net/http"
"net/http/pprof"
"time"
"github.com/fatedier/frp/assets"
frpNet "github.com/fatedier/frp/pkg/util/net"
"github.com/gorilla/mux"
)
var (
httpServerReadTimeout = 60 * time.Second
httpServerWriteTimeout = 60 * time.Second
)
func (svr *Service) RunAdminServer(address string) (err error) {
// url router
router := mux.NewRouter()
router.HandleFunc("/healthz", svr.healthz)
// debug
if svr.cfg.PprofEnable {
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
router.HandleFunc("/debug/pprof/trace", pprof.Trace)
router.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
}
subRouter := router.NewRoute().Subrouter()
user, passwd := svr.cfg.AdminUser, svr.cfg.AdminPwd
subRouter.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware)
// api, see admin_api.go
subRouter.HandleFunc("/api/reload", svr.apiReload).Methods("GET")
subRouter.HandleFunc("/api/status", svr.apiStatus).Methods("GET")
subRouter.HandleFunc("/api/config", svr.apiGetConfig).Methods("GET")
subRouter.HandleFunc("/api/config", svr.apiPutConfig).Methods("PUT")
// view
subRouter.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET")
subRouter.PathPrefix("/static/").Handler(frpNet.MakeHTTPGzipHandler(http.StripPrefix("/static/", http.FileServer(assets.FileSystem)))).Methods("GET")
subRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/static/", http.StatusMovedPermanently)
})
server := &http.Server{
Addr: address,
Handler: router,
ReadTimeout: httpServerReadTimeout,
WriteTimeout: httpServerWriteTimeout,
}
if address == "" {
address = ":http"
}
ln, err := net.Listen("tcp", address)
if err != nil {
return err
}
go server.Serve(ln)
return
}

323
client/admin_api.go Normal file
View File

@@ -0,0 +1,323 @@
// 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 client
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"sort"
"strings"
"github.com/fatedier/frp/client/proxy"
"github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/util/log"
)
type GeneralResponse struct {
Code int
Msg string
}
// /healthz
func (svr *Service) healthz(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
}
// GET api/reload
func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
res := GeneralResponse{Code: 200}
log.Info("api request [/api/reload]")
defer func() {
log.Info("api response [/api/reload], code [%d]", res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
w.Write([]byte(res.Msg))
}
}()
_, pxyCfgs, visitorCfgs, err := config.ParseClientConfig(svr.cfgFile)
if err != nil {
res.Code = 400
res.Msg = err.Error()
log.Warn("reload frpc proxy config error: %s", res.Msg)
return
}
if err = svr.ReloadConf(pxyCfgs, visitorCfgs); err != nil {
res.Code = 500
res.Msg = err.Error()
log.Warn("reload frpc proxy config error: %s", res.Msg)
return
}
log.Info("success reload conf")
return
}
type StatusResp struct {
TCP []ProxyStatusResp `json:"tcp"`
UDP []ProxyStatusResp `json:"udp"`
HTTP []ProxyStatusResp `json:"http"`
HTTPS []ProxyStatusResp `json:"https"`
STCP []ProxyStatusResp `json:"stcp"`
XTCP []ProxyStatusResp `json:"xtcp"`
SUDP []ProxyStatusResp `json:"sudp"`
}
type ProxyStatusResp struct {
Name string `json:"name"`
Type string `json:"type"`
Status string `json:"status"`
Err string `json:"err"`
LocalAddr string `json:"local_addr"`
Plugin string `json:"plugin"`
RemoteAddr string `json:"remote_addr"`
}
type ByProxyStatusResp []ProxyStatusResp
func (a ByProxyStatusResp) Len() int { return len(a) }
func (a ByProxyStatusResp) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByProxyStatusResp) Less(i, j int) bool { return strings.Compare(a[i].Name, a[j].Name) < 0 }
func NewProxyStatusResp(status *proxy.WorkingStatus, serverAddr string) ProxyStatusResp {
psr := ProxyStatusResp{
Name: status.Name,
Type: status.Type,
Status: status.Phase,
Err: status.Err,
}
switch cfg := status.Cfg.(type) {
case *config.TCPProxyConf:
if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort)
}
psr.Plugin = cfg.Plugin
if status.Err != "" {
psr.RemoteAddr = fmt.Sprintf("%s:%d", serverAddr, cfg.RemotePort)
} else {
psr.RemoteAddr = 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", serverAddr, cfg.RemotePort)
} else {
psr.RemoteAddr = serverAddr + status.RemoteAddr
}
case *config.HTTPProxyConf:
if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort)
}
psr.Plugin = cfg.Plugin
psr.RemoteAddr = status.RemoteAddr
case *config.HTTPSProxyConf:
if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort)
}
psr.Plugin = cfg.Plugin
psr.RemoteAddr = status.RemoteAddr
case *config.STCPProxyConf:
if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort)
}
psr.Plugin = cfg.Plugin
case *config.XTCPProxyConf:
if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort)
}
psr.Plugin = cfg.Plugin
case *config.SUDPProxyConf:
if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIP, cfg.LocalPort)
}
psr.Plugin = cfg.Plugin
}
return psr
}
// GET api/status
func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) {
var (
buf []byte
res StatusResp
)
res.TCP = make([]ProxyStatusResp, 0)
res.UDP = make([]ProxyStatusResp, 0)
res.HTTP = make([]ProxyStatusResp, 0)
res.HTTPS = make([]ProxyStatusResp, 0)
res.STCP = make([]ProxyStatusResp, 0)
res.XTCP = make([]ProxyStatusResp, 0)
res.SUDP = make([]ProxyStatusResp, 0)
log.Info("Http request [/api/status]")
defer func() {
log.Info("Http response [/api/status]")
buf, _ = json.Marshal(&res)
w.Write(buf)
}()
ps := svr.ctl.pm.GetAllProxyStatus()
for _, status := range ps {
switch status.Type {
case "tcp":
res.TCP = append(res.TCP, NewProxyStatusResp(status, svr.cfg.ServerAddr))
case "udp":
res.UDP = append(res.UDP, NewProxyStatusResp(status, svr.cfg.ServerAddr))
case "http":
res.HTTP = append(res.HTTP, NewProxyStatusResp(status, svr.cfg.ServerAddr))
case "https":
res.HTTPS = append(res.HTTPS, NewProxyStatusResp(status, svr.cfg.ServerAddr))
case "stcp":
res.STCP = append(res.STCP, NewProxyStatusResp(status, svr.cfg.ServerAddr))
case "xtcp":
res.XTCP = append(res.XTCP, NewProxyStatusResp(status, svr.cfg.ServerAddr))
case "sudp":
res.SUDP = append(res.SUDP, NewProxyStatusResp(status, svr.cfg.ServerAddr))
}
}
sort.Sort(ByProxyStatusResp(res.TCP))
sort.Sort(ByProxyStatusResp(res.UDP))
sort.Sort(ByProxyStatusResp(res.HTTP))
sort.Sort(ByProxyStatusResp(res.HTTPS))
sort.Sort(ByProxyStatusResp(res.STCP))
sort.Sort(ByProxyStatusResp(res.XTCP))
sort.Sort(ByProxyStatusResp(res.SUDP))
return
}
// GET api/config
func (svr *Service) apiGetConfig(w http.ResponseWriter, r *http.Request) {
res := GeneralResponse{Code: 200}
log.Info("Http get request [/api/config]")
defer func() {
log.Info("Http get response [/api/config], code [%d]", res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
w.Write([]byte(res.Msg))
}
}()
if svr.cfgFile == "" {
res.Code = 400
res.Msg = "frpc has no config file path"
log.Warn("%s", res.Msg)
return
}
content, err := config.GetRenderedConfFromFile(svr.cfgFile)
if err != nil {
res.Code = 400
res.Msg = err.Error()
log.Warn("load frpc config file error: %s", res.Msg)
return
}
rows := strings.Split(string(content), "\n")
newRows := make([]string, 0, len(rows))
for _, row := range rows {
row = strings.TrimSpace(row)
if strings.HasPrefix(row, "token") {
continue
}
newRows = append(newRows, row)
}
res.Msg = strings.Join(newRows, "\n")
}
// PUT api/config
func (svr *Service) apiPutConfig(w http.ResponseWriter, r *http.Request) {
res := GeneralResponse{Code: 200}
log.Info("Http put request [/api/config]")
defer func() {
log.Info("Http put response [/api/config], code [%d]", res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
w.Write([]byte(res.Msg))
}
}()
// get new config content
body, err := io.ReadAll(r.Body)
if err != nil {
res.Code = 400
res.Msg = fmt.Sprintf("read request body error: %v", err)
log.Warn("%s", res.Msg)
return
}
if len(body) == 0 {
res.Code = 400
res.Msg = "body can't be empty"
log.Warn("%s", res.Msg)
return
}
// get token from origin content
token := ""
b, err := os.ReadFile(svr.cfgFile)
if err != nil {
res.Code = 400
res.Msg = err.Error()
log.Warn("load frpc config file error: %s", res.Msg)
return
}
content := string(b)
for _, row := range strings.Split(content, "\n") {
row = strings.TrimSpace(row)
if strings.HasPrefix(row, "token") {
token = row
break
}
}
tmpRows := make([]string, 0)
for _, row := range strings.Split(string(body), "\n") {
row = strings.TrimSpace(row)
if strings.HasPrefix(row, "token") {
continue
}
tmpRows = append(tmpRows, row)
}
newRows := make([]string, 0)
if token != "" {
for _, row := range tmpRows {
newRows = append(newRows, row)
if strings.HasPrefix(row, "[common]") {
newRows = append(newRows, token)
}
}
} else {
newRows = tmpRows
}
content = strings.Join(newRows, "\n")
err = os.WriteFile(svr.cfgFile, []byte(content), 0644)
if err != nil {
res.Code = 500
res.Msg = fmt.Sprintf("write content to frpc config file error: %v", err)
log.Warn("%s", res.Msg)
return
}
}

428
client/control.go Normal file
View File

@@ -0,0 +1,428 @@
// 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 client
import (
"context"
"crypto/tls"
"io"
"net"
"runtime/debug"
"strconv"
"sync"
"time"
"github.com/fatedier/frp/client/proxy"
"github.com/fatedier/frp/pkg/auth"
"github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/msg"
"github.com/fatedier/frp/pkg/transport"
frpNet "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/xlog"
"github.com/fatedier/golib/control/shutdown"
"github.com/fatedier/golib/crypto"
libdial "github.com/fatedier/golib/net/dial"
fmux "github.com/hashicorp/yamux"
)
type Control struct {
// uniq id got from frps, attach it in loginMsg
runID string
// manage all proxies
pxyCfgs map[string]config.ProxyConf
pm *proxy.Manager
// manage all visitors
vm *VisitorManager
// control connection
conn net.Conn
// tcp stream multiplexing, if enabled
session *fmux.Session
// put a message in this channel to send it over control connection to server
sendCh chan (msg.Message)
// read from this channel to get the next message sent by server
readCh chan (msg.Message)
// goroutines can block by reading from this channel, it will be closed only in reader() when control connection is closed
closedCh chan struct{}
closedDoneCh chan struct{}
// last time got the Pong message
lastPong time.Time
// The client configuration
clientCfg config.ClientCommonConf
readerShutdown *shutdown.Shutdown
writerShutdown *shutdown.Shutdown
msgHandlerShutdown *shutdown.Shutdown
// The UDP port that the server is listening on
serverUDPPort int
mu sync.RWMutex
xl *xlog.Logger
// service context
ctx context.Context
// sets authentication based on selected method
authSetter auth.Setter
}
func NewControl(ctx context.Context, runID string, conn net.Conn, session *fmux.Session,
clientCfg config.ClientCommonConf,
pxyCfgs map[string]config.ProxyConf,
visitorCfgs map[string]config.VisitorConf,
serverUDPPort int,
authSetter auth.Setter) *Control {
// new xlog instance
ctl := &Control{
runID: runID,
conn: conn,
session: session,
pxyCfgs: pxyCfgs,
sendCh: make(chan msg.Message, 100),
readCh: make(chan msg.Message, 100),
closedCh: make(chan struct{}),
closedDoneCh: make(chan struct{}),
clientCfg: clientCfg,
readerShutdown: shutdown.New(),
writerShutdown: shutdown.New(),
msgHandlerShutdown: shutdown.New(),
serverUDPPort: serverUDPPort,
xl: xlog.FromContextSafe(ctx),
ctx: ctx,
authSetter: authSetter,
}
ctl.pm = proxy.NewManager(ctl.ctx, ctl.sendCh, clientCfg, serverUDPPort)
ctl.vm = NewVisitorManager(ctl.ctx, ctl)
ctl.vm.Reload(visitorCfgs)
return ctl
}
func (ctl *Control) Run() {
go ctl.worker()
// start all proxies
ctl.pm.Reload(ctl.pxyCfgs)
// start all visitors
go ctl.vm.Run()
return
}
func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) {
xl := ctl.xl
workConn, err := ctl.connectServer()
if err != nil {
return
}
m := &msg.NewWorkConn{
RunID: ctl.runID,
}
if err = ctl.authSetter.SetNewWorkConn(m); err != nil {
xl.Warn("error during NewWorkConn authentication: %v", err)
return
}
if err = msg.WriteMsg(workConn, m); err != nil {
xl.Warn("work connection write to server error: %v", err)
workConn.Close()
return
}
var startMsg msg.StartWorkConn
if err = msg.ReadMsgInto(workConn, &startMsg); err != nil {
xl.Error("work connection closed before response StartWorkConn message: %v", err)
workConn.Close()
return
}
if startMsg.Error != "" {
xl.Error("StartWorkConn contains error: %s", startMsg.Error)
workConn.Close()
return
}
// dispatch this work connection to related proxy
ctl.pm.HandleWorkConn(startMsg.ProxyName, workConn, &startMsg)
}
func (ctl *Control) HandleNewProxyResp(inMsg *msg.NewProxyResp) {
xl := ctl.xl
// Server will return NewProxyResp message to each NewProxy message.
// Start a new proxy handler if no error got
err := ctl.pm.StartProxy(inMsg.ProxyName, inMsg.RemoteAddr, inMsg.Error)
if err != nil {
xl.Warn("[%s] start error: %v", inMsg.ProxyName, err)
} else {
xl.Info("[%s] start proxy success", inMsg.ProxyName)
}
}
func (ctl *Control) Close() error {
return ctl.GracefulClose(0)
}
func (ctl *Control) GracefulClose(d time.Duration) error {
ctl.pm.Close()
ctl.vm.Close()
time.Sleep(d)
ctl.conn.Close()
if ctl.session != nil {
ctl.session.Close()
}
return nil
}
// ClosedDoneCh returns a channel which will be closed after all resources are released
func (ctl *Control) ClosedDoneCh() <-chan struct{} {
return ctl.closedDoneCh
}
// connectServer return a new connection to frps
func (ctl *Control) connectServer() (conn net.Conn, err error) {
xl := ctl.xl
if ctl.clientCfg.TCPMux {
stream, errRet := ctl.session.OpenStream()
if errRet != nil {
err = errRet
xl.Warn("start new connection to server error: %v", err)
return
}
conn = stream
} else {
var tlsConfig *tls.Config
sn := ctl.clientCfg.TLSServerName
if sn == "" {
sn = ctl.clientCfg.ServerAddr
}
if ctl.clientCfg.TLSEnable {
tlsConfig, err = transport.NewClientTLSConfig(
ctl.clientCfg.TLSCertFile,
ctl.clientCfg.TLSKeyFile,
ctl.clientCfg.TLSTrustedCaFile,
sn)
if err != nil {
xl.Warn("fail to build tls configuration when connecting to server, err: %v", err)
return
}
}
proxyType, addr, auth, err := libdial.ParseProxyURL(ctl.clientCfg.HTTPProxy)
if err != nil {
xl.Error("fail to parse proxy url")
return nil, err
}
dialOptions := []libdial.DialOption{}
protocol := ctl.clientCfg.Protocol
if protocol == "websocket" {
protocol = "tcp"
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: frpNet.DialHookWebsocket()}))
}
if ctl.clientCfg.ConnectServerLocalIP != "" {
dialOptions = append(dialOptions, libdial.WithLocalAddr(ctl.clientCfg.ConnectServerLocalIP))
}
dialOptions = append(dialOptions,
libdial.WithProtocol(protocol),
libdial.WithTimeout(time.Duration(ctl.clientCfg.DialServerTimeout)*time.Second),
libdial.WithKeepAlive(time.Duration(ctl.clientCfg.DialServerKeepAlive)*time.Second),
libdial.WithProxy(proxyType, addr),
libdial.WithProxyAuth(auth),
libdial.WithTLSConfig(tlsConfig),
libdial.WithAfterHook(libdial.AfterHook{
Hook: frpNet.DialHookCustomTLSHeadByte(tlsConfig != nil, ctl.clientCfg.DisableCustomTLSFirstByte),
}),
)
conn, err = libdial.Dial(
net.JoinHostPort(ctl.clientCfg.ServerAddr, strconv.Itoa(ctl.clientCfg.ServerPort)),
dialOptions...,
)
if err != nil {
xl.Warn("start new connection to server error: %v", err)
return nil, err
}
}
return
}
// reader read all messages from frps and send to readCh
func (ctl *Control) reader() {
xl := ctl.xl
defer func() {
if err := recover(); err != nil {
xl.Error("panic error: %v", err)
xl.Error(string(debug.Stack()))
}
}()
defer ctl.readerShutdown.Done()
defer close(ctl.closedCh)
encReader := crypto.NewReader(ctl.conn, []byte(ctl.clientCfg.Token))
for {
m, err := msg.ReadMsg(encReader)
if err != nil {
if err == io.EOF {
xl.Debug("read from control connection EOF")
return
}
xl.Warn("read error: %v", err)
ctl.conn.Close()
return
}
ctl.readCh <- m
}
}
// writer writes messages got from sendCh to frps
func (ctl *Control) writer() {
xl := ctl.xl
defer ctl.writerShutdown.Done()
encWriter, err := crypto.NewWriter(ctl.conn, []byte(ctl.clientCfg.Token))
if err != nil {
xl.Error("crypto new writer error: %v", err)
ctl.conn.Close()
return
}
for {
m, ok := <-ctl.sendCh
if !ok {
xl.Info("control writer is closing")
return
}
if err := msg.WriteMsg(encWriter, m); err != nil {
xl.Warn("write message to control connection error: %v", err)
return
}
}
}
// msgHandler handles all channel events and do corresponding operations.
func (ctl *Control) msgHandler() {
xl := ctl.xl
defer func() {
if err := recover(); err != nil {
xl.Error("panic error: %v", err)
xl.Error(string(debug.Stack()))
}
}()
defer ctl.msgHandlerShutdown.Done()
var hbSendCh <-chan time.Time
// TODO(fatedier): disable heartbeat if TCPMux is enabled.
// Just keep it here to keep compatible with old version frps.
if ctl.clientCfg.HeartbeatInterval > 0 {
hbSend := time.NewTicker(time.Duration(ctl.clientCfg.HeartbeatInterval) * time.Second)
defer hbSend.Stop()
hbSendCh = hbSend.C
}
var hbCheckCh <-chan time.Time
// Check heartbeat timeout only if TCPMux is not enabled and users don't disable heartbeat feature.
if ctl.clientCfg.HeartbeatInterval > 0 && ctl.clientCfg.HeartbeatTimeout > 0 && !ctl.clientCfg.TCPMux {
hbCheck := time.NewTicker(time.Second)
defer hbCheck.Stop()
hbCheckCh = hbCheck.C
}
ctl.lastPong = time.Now()
for {
select {
case <-hbSendCh:
// send heartbeat to server
xl.Debug("send heartbeat to server")
pingMsg := &msg.Ping{}
if err := ctl.authSetter.SetPing(pingMsg); err != nil {
xl.Warn("error during ping authentication: %v", err)
return
}
ctl.sendCh <- pingMsg
case <-hbCheckCh:
if time.Since(ctl.lastPong) > time.Duration(ctl.clientCfg.HeartbeatTimeout)*time.Second {
xl.Warn("heartbeat timeout")
// let reader() stop
ctl.conn.Close()
return
}
case rawMsg, ok := <-ctl.readCh:
if !ok {
return
}
switch m := rawMsg.(type) {
case *msg.ReqWorkConn:
go ctl.HandleReqWorkConn(m)
case *msg.NewProxyResp:
ctl.HandleNewProxyResp(m)
case *msg.Pong:
if m.Error != "" {
xl.Error("Pong contains error: %s", m.Error)
ctl.conn.Close()
return
}
ctl.lastPong = time.Now()
xl.Debug("receive heartbeat from server")
}
}
}
}
// If controler is notified by closedCh, reader and writer and handler will exit
func (ctl *Control) worker() {
go ctl.msgHandler()
go ctl.reader()
go ctl.writer()
select {
case <-ctl.closedCh:
// close related channels and wait until other goroutines done
close(ctl.readCh)
ctl.readerShutdown.WaitDone()
ctl.msgHandlerShutdown.WaitDone()
close(ctl.sendCh)
ctl.writerShutdown.WaitDone()
ctl.pm.Close()
ctl.vm.Close()
close(ctl.closedDoneCh)
if ctl.session != nil {
ctl.session.Close()
}
return
}
}
func (ctl *Control) ReloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) error {
ctl.vm.Reload(visitorCfgs)
ctl.pm.Reload(pxyCfgs)
return nil
}

28
client/event/event.go Normal file
View File

@@ -0,0 +1,28 @@
package event
import (
"errors"
"github.com/fatedier/frp/pkg/msg"
)
type Type int
const (
EvStartProxy Type = iota
EvCloseProxy
)
var (
ErrPayloadType = errors.New("error payload type")
)
type Handler func(evType Type, payload interface{}) error
type StartProxyPayload struct {
NewProxyMsg *msg.NewProxy
}
type CloseProxyPayload struct {
CloseProxyMsg *msg.CloseProxy
}

170
client/health/health.go Normal file
View File

@@ -0,0 +1,170 @@
// 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 health
import (
"context"
"errors"
"fmt"
"io"
"net"
"net/http"
"time"
"github.com/fatedier/frp/pkg/util/xlog"
)
var (
ErrHealthCheckType = errors.New("error health check type")
)
type Monitor struct {
checkType string
interval time.Duration
timeout time.Duration
maxFailedTimes int
// For tcp
addr string
// For http
url string
failedTimes uint64
statusOK bool
statusNormalFn func()
statusFailedFn func()
ctx context.Context
cancel context.CancelFunc
}
func NewMonitor(ctx context.Context, checkType string,
intervalS int, timeoutS int, maxFailedTimes int,
addr string, url string,
statusNormalFn func(), statusFailedFn func()) *Monitor {
if intervalS <= 0 {
intervalS = 10
}
if timeoutS <= 0 {
timeoutS = 3
}
if maxFailedTimes <= 0 {
maxFailedTimes = 1
}
newctx, cancel := context.WithCancel(ctx)
return &Monitor{
checkType: checkType,
interval: time.Duration(intervalS) * time.Second,
timeout: time.Duration(timeoutS) * time.Second,
maxFailedTimes: maxFailedTimes,
addr: addr,
url: url,
statusOK: false,
statusNormalFn: statusNormalFn,
statusFailedFn: statusFailedFn,
ctx: newctx,
cancel: cancel,
}
}
func (monitor *Monitor) Start() {
go monitor.checkWorker()
}
func (monitor *Monitor) Stop() {
monitor.cancel()
}
func (monitor *Monitor) checkWorker() {
xl := xlog.FromContextSafe(monitor.ctx)
for {
doCtx, cancel := context.WithDeadline(monitor.ctx, time.Now().Add(monitor.timeout))
err := monitor.doCheck(doCtx)
// check if this monitor has been closed
select {
case <-monitor.ctx.Done():
cancel()
return
default:
cancel()
}
if err == nil {
xl.Trace("do one health check success")
if !monitor.statusOK && monitor.statusNormalFn != nil {
xl.Info("health check status change to success")
monitor.statusOK = true
monitor.statusNormalFn()
}
} else {
xl.Warn("do one health check failed: %v", err)
monitor.failedTimes++
if monitor.statusOK && int(monitor.failedTimes) >= monitor.maxFailedTimes && monitor.statusFailedFn != nil {
xl.Warn("health check status change to failed")
monitor.statusOK = false
monitor.statusFailedFn()
}
}
time.Sleep(monitor.interval)
}
}
func (monitor *Monitor) doCheck(ctx context.Context) error {
switch monitor.checkType {
case "tcp":
return monitor.doTCPCheck(ctx)
case "http":
return monitor.doHTTPCheck(ctx)
default:
return ErrHealthCheckType
}
}
func (monitor *Monitor) doTCPCheck(ctx context.Context) error {
// if tcp address is not specified, always return nil
if monitor.addr == "" {
return nil
}
var d net.Dialer
conn, err := d.DialContext(ctx, "tcp", monitor.addr)
if err != nil {
return err
}
conn.Close()
return nil
}
func (monitor *Monitor) doHTTPCheck(ctx context.Context) error {
req, err := http.NewRequest("GET", monitor.url, nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
io.Copy(io.Discard, resp.Body)
if resp.StatusCode/100 != 2 {
return fmt.Errorf("do http health check, StatusCode is [%d] not 2xx", resp.StatusCode)
}
return nil
}

809
client/proxy/proxy.go Normal file
View File

@@ -0,0 +1,809 @@
// 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 proxy
import (
"bytes"
"context"
"fmt"
"io"
"net"
"strconv"
"strings"
"sync"
"time"
"github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/msg"
plugin "github.com/fatedier/frp/pkg/plugin/client"
"github.com/fatedier/frp/pkg/proto/udp"
"github.com/fatedier/frp/pkg/util/limit"
frpNet "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/xlog"
"github.com/fatedier/golib/errors"
frpIo "github.com/fatedier/golib/io"
libdial "github.com/fatedier/golib/net/dial"
"github.com/fatedier/golib/pool"
fmux "github.com/hashicorp/yamux"
pp "github.com/pires/go-proxyproto"
"golang.org/x/time/rate"
)
// Proxy defines how to handle work connections for different proxy type.
type Proxy interface {
Run() error
// InWorkConn accept work connections registered to server.
InWorkConn(net.Conn, *msg.StartWorkConn)
Close()
}
func NewProxy(ctx context.Context, pxyConf config.ProxyConf, clientCfg config.ClientCommonConf, serverUDPPort int) (pxy Proxy) {
var limiter *rate.Limiter
limitBytes := pxyConf.GetBaseInfo().BandwidthLimit.Bytes()
if limitBytes > 0 {
limiter = rate.NewLimiter(rate.Limit(float64(limitBytes)), int(limitBytes))
}
baseProxy := BaseProxy{
clientCfg: clientCfg,
serverUDPPort: serverUDPPort,
limiter: limiter,
xl: xlog.FromContextSafe(ctx),
ctx: ctx,
}
switch cfg := pxyConf.(type) {
case *config.TCPProxyConf:
pxy = &TCPProxy{
BaseProxy: &baseProxy,
cfg: cfg,
}
case *config.TCPMuxProxyConf:
pxy = &TCPMuxProxy{
BaseProxy: &baseProxy,
cfg: cfg,
}
case *config.UDPProxyConf:
pxy = &UDPProxy{
BaseProxy: &baseProxy,
cfg: cfg,
}
case *config.HTTPProxyConf:
pxy = &HTTPProxy{
BaseProxy: &baseProxy,
cfg: cfg,
}
case *config.HTTPSProxyConf:
pxy = &HTTPSProxy{
BaseProxy: &baseProxy,
cfg: cfg,
}
case *config.STCPProxyConf:
pxy = &STCPProxy{
BaseProxy: &baseProxy,
cfg: cfg,
}
case *config.XTCPProxyConf:
pxy = &XTCPProxy{
BaseProxy: &baseProxy,
cfg: cfg,
}
case *config.SUDPProxyConf:
pxy = &SUDPProxy{
BaseProxy: &baseProxy,
cfg: cfg,
closeCh: make(chan struct{}),
}
}
return
}
type BaseProxy struct {
closed bool
clientCfg config.ClientCommonConf
serverUDPPort int
limiter *rate.Limiter
mu sync.RWMutex
xl *xlog.Logger
ctx context.Context
}
// TCP
type TCPProxy struct {
*BaseProxy
cfg *config.TCPProxyConf
proxyPlugin plugin.Plugin
}
func (pxy *TCPProxy) Run() (err error) {
if pxy.cfg.Plugin != "" {
pxy.proxyPlugin, err = plugin.Create(pxy.cfg.Plugin, pxy.cfg.PluginParams)
if err != nil {
return
}
}
return
}
func (pxy *TCPProxy) Close() {
if pxy.proxyPlugin != nil {
pxy.proxyPlugin.Close()
}
}
func (pxy *TCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
conn, []byte(pxy.clientCfg.Token), m)
}
// TCP Multiplexer
type TCPMuxProxy struct {
*BaseProxy
cfg *config.TCPMuxProxyConf
proxyPlugin plugin.Plugin
}
func (pxy *TCPMuxProxy) Run() (err error) {
if pxy.cfg.Plugin != "" {
pxy.proxyPlugin, err = plugin.Create(pxy.cfg.Plugin, pxy.cfg.PluginParams)
if err != nil {
return
}
}
return
}
func (pxy *TCPMuxProxy) Close() {
if pxy.proxyPlugin != nil {
pxy.proxyPlugin.Close()
}
}
func (pxy *TCPMuxProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
conn, []byte(pxy.clientCfg.Token), m)
}
// HTTP
type HTTPProxy struct {
*BaseProxy
cfg *config.HTTPProxyConf
proxyPlugin plugin.Plugin
}
func (pxy *HTTPProxy) Run() (err error) {
if pxy.cfg.Plugin != "" {
pxy.proxyPlugin, err = plugin.Create(pxy.cfg.Plugin, pxy.cfg.PluginParams)
if err != nil {
return
}
}
return
}
func (pxy *HTTPProxy) Close() {
if pxy.proxyPlugin != nil {
pxy.proxyPlugin.Close()
}
}
func (pxy *HTTPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
conn, []byte(pxy.clientCfg.Token), m)
}
// HTTPS
type HTTPSProxy struct {
*BaseProxy
cfg *config.HTTPSProxyConf
proxyPlugin plugin.Plugin
}
func (pxy *HTTPSProxy) Run() (err error) {
if pxy.cfg.Plugin != "" {
pxy.proxyPlugin, err = plugin.Create(pxy.cfg.Plugin, pxy.cfg.PluginParams)
if err != nil {
return
}
}
return
}
func (pxy *HTTPSProxy) Close() {
if pxy.proxyPlugin != nil {
pxy.proxyPlugin.Close()
}
}
func (pxy *HTTPSProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
conn, []byte(pxy.clientCfg.Token), m)
}
// STCP
type STCPProxy struct {
*BaseProxy
cfg *config.STCPProxyConf
proxyPlugin plugin.Plugin
}
func (pxy *STCPProxy) Run() (err error) {
if pxy.cfg.Plugin != "" {
pxy.proxyPlugin, err = plugin.Create(pxy.cfg.Plugin, pxy.cfg.PluginParams)
if err != nil {
return
}
}
return
}
func (pxy *STCPProxy) Close() {
if pxy.proxyPlugin != nil {
pxy.proxyPlugin.Close()
}
}
func (pxy *STCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
conn, []byte(pxy.clientCfg.Token), m)
}
// XTCP
type XTCPProxy struct {
*BaseProxy
cfg *config.XTCPProxyConf
proxyPlugin plugin.Plugin
}
func (pxy *XTCPProxy) Run() (err error) {
if pxy.cfg.Plugin != "" {
pxy.proxyPlugin, err = plugin.Create(pxy.cfg.Plugin, pxy.cfg.PluginParams)
if err != nil {
return
}
}
return
}
func (pxy *XTCPProxy) Close() {
if pxy.proxyPlugin != nil {
pxy.proxyPlugin.Close()
}
}
func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
xl := pxy.xl
defer conn.Close()
var natHoleSidMsg msg.NatHoleSid
err := msg.ReadMsgInto(conn, &natHoleSidMsg)
if err != nil {
xl.Error("xtcp read from workConn error: %v", err)
return
}
natHoleClientMsg := &msg.NatHoleClient{
ProxyName: pxy.cfg.ProxyName,
Sid: natHoleSidMsg.Sid,
}
raddr, _ := net.ResolveUDPAddr("udp",
fmt.Sprintf("%s:%d", pxy.clientCfg.ServerAddr, pxy.serverUDPPort))
clientConn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
xl.Error("dial server udp addr error: %v", err)
return
}
defer clientConn.Close()
err = msg.WriteMsg(clientConn, natHoleClientMsg)
if err != nil {
xl.Error("send natHoleClientMsg to server error: %v", err)
return
}
// Wait for client address at most 5 seconds.
var natHoleRespMsg msg.NatHoleResp
clientConn.SetReadDeadline(time.Now().Add(5 * time.Second))
buf := pool.GetBuf(1024)
n, err := clientConn.Read(buf)
if err != nil {
xl.Error("get natHoleRespMsg error: %v", err)
return
}
err = msg.ReadMsgInto(bytes.NewReader(buf[:n]), &natHoleRespMsg)
if err != nil {
xl.Error("get natHoleRespMsg error: %v", err)
return
}
clientConn.SetReadDeadline(time.Time{})
clientConn.Close()
if natHoleRespMsg.Error != "" {
xl.Error("natHoleRespMsg get error info: %s", natHoleRespMsg.Error)
return
}
xl.Trace("get natHoleRespMsg, sid [%s], client address [%s] visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
// Send detect message
host, portStr, err := net.SplitHostPort(natHoleRespMsg.VisitorAddr)
if err != nil {
xl.Error("get NatHoleResp visitor address [%s] error: %v", natHoleRespMsg.VisitorAddr, err)
}
laddr, _ := net.ResolveUDPAddr("udp", clientConn.LocalAddr().String())
port, err := strconv.ParseInt(portStr, 10, 64)
if err != nil {
xl.Error("get natHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
return
}
pxy.sendDetectMsg(host, int(port), laddr, []byte(natHoleRespMsg.Sid))
xl.Trace("send all detect msg done")
msg.WriteMsg(conn, &msg.NatHoleClientDetectOK{})
// Listen for clientConn's address and wait for visitor connection
lConn, err := net.ListenUDP("udp", laddr)
if err != nil {
xl.Error("listen on visitorConn's local address error: %v", err)
return
}
defer lConn.Close()
lConn.SetReadDeadline(time.Now().Add(8 * time.Second))
sidBuf := pool.GetBuf(1024)
var uAddr *net.UDPAddr
n, uAddr, err = lConn.ReadFromUDP(sidBuf)
if err != nil {
xl.Warn("get sid from visitor error: %v", err)
return
}
lConn.SetReadDeadline(time.Time{})
if string(sidBuf[:n]) != natHoleRespMsg.Sid {
xl.Warn("incorrect sid from visitor")
return
}
pool.PutBuf(sidBuf)
xl.Info("nat hole connection make success, sid [%s]", natHoleRespMsg.Sid)
lConn.WriteToUDP(sidBuf[:n], uAddr)
kcpConn, err := frpNet.NewKCPConnFromUDP(lConn, false, uAddr.String())
if err != nil {
xl.Error("create kcp connection from udp connection error: %v", err)
return
}
fmuxCfg := fmux.DefaultConfig()
fmuxCfg.KeepAliveInterval = 5 * time.Second
fmuxCfg.LogOutput = io.Discard
sess, err := fmux.Server(kcpConn, fmuxCfg)
if err != nil {
xl.Error("create yamux server from kcp connection error: %v", err)
return
}
defer sess.Close()
muxConn, err := sess.Accept()
if err != nil {
xl.Error("accept for yamux connection error: %v", err)
return
}
HandleTCPWorkConnection(pxy.ctx, &pxy.cfg.LocalSvrConf, pxy.proxyPlugin, pxy.cfg.GetBaseInfo(), pxy.limiter,
muxConn, []byte(pxy.cfg.Sk), m)
}
func (pxy *XTCPProxy) sendDetectMsg(addr string, port int, laddr *net.UDPAddr, content []byte) (err error) {
daddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", addr, port))
if err != nil {
return err
}
tConn, err := net.DialUDP("udp", laddr, daddr)
if err != nil {
return err
}
//uConn := ipv4.NewConn(tConn)
//uConn.SetTTL(3)
tConn.Write(content)
tConn.Close()
return nil
}
// UDP
type UDPProxy struct {
*BaseProxy
cfg *config.UDPProxyConf
localAddr *net.UDPAddr
readCh chan *msg.UDPPacket
// include msg.UDPPacket and msg.Ping
sendCh chan msg.Message
workConn net.Conn
}
func (pxy *UDPProxy) Run() (err error) {
pxy.localAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pxy.cfg.LocalIP, pxy.cfg.LocalPort))
if err != nil {
return
}
return
}
func (pxy *UDPProxy) Close() {
pxy.mu.Lock()
defer pxy.mu.Unlock()
if !pxy.closed {
pxy.closed = true
if pxy.workConn != nil {
pxy.workConn.Close()
}
if pxy.readCh != nil {
close(pxy.readCh)
}
if pxy.sendCh != nil {
close(pxy.sendCh)
}
}
}
func (pxy *UDPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
xl := pxy.xl
xl.Info("incoming a new work connection for udp proxy, %s", conn.RemoteAddr().String())
// close resources releated with old workConn
pxy.Close()
var rwc io.ReadWriteCloser = conn
var err error
if pxy.limiter != nil {
rwc = frpIo.WrapReadWriteCloser(limit.NewReader(conn, pxy.limiter), limit.NewWriter(conn, pxy.limiter), func() error {
return conn.Close()
})
}
if pxy.cfg.UseEncryption {
rwc, err = frpIo.WithEncryption(rwc, []byte(pxy.clientCfg.Token))
if err != nil {
conn.Close()
xl.Error("create encryption stream error: %v", err)
return
}
}
if pxy.cfg.UseCompression {
rwc = frpIo.WithCompression(rwc)
}
conn = frpNet.WrapReadWriteCloserToConn(rwc, conn)
pxy.mu.Lock()
pxy.workConn = conn
pxy.readCh = make(chan *msg.UDPPacket, 1024)
pxy.sendCh = make(chan msg.Message, 1024)
pxy.closed = false
pxy.mu.Unlock()
workConnReaderFn := func(conn net.Conn, readCh chan *msg.UDPPacket) {
for {
var udpMsg msg.UDPPacket
if errRet := msg.ReadMsgInto(conn, &udpMsg); errRet != nil {
xl.Warn("read from workConn for udp error: %v", errRet)
return
}
if errRet := errors.PanicToError(func() {
xl.Trace("get udp package from workConn: %s", udpMsg.Content)
readCh <- &udpMsg
}); errRet != nil {
xl.Info("reader goroutine for udp work connection closed: %v", errRet)
return
}
}
}
workConnSenderFn := func(conn net.Conn, sendCh chan msg.Message) {
defer func() {
xl.Info("writer goroutine for udp work connection closed")
}()
var errRet error
for rawMsg := range sendCh {
switch m := rawMsg.(type) {
case *msg.UDPPacket:
xl.Trace("send udp package to workConn: %s", m.Content)
case *msg.Ping:
xl.Trace("send ping message to udp workConn")
}
if errRet = msg.WriteMsg(conn, rawMsg); errRet != nil {
xl.Error("udp work write error: %v", errRet)
return
}
}
}
heartbeatFn := func(conn net.Conn, sendCh chan msg.Message) {
var errRet error
for {
time.Sleep(time.Duration(30) * time.Second)
if errRet = errors.PanicToError(func() {
sendCh <- &msg.Ping{}
}); errRet != nil {
xl.Trace("heartbeat goroutine for udp work connection closed")
break
}
}
}
go workConnSenderFn(pxy.workConn, pxy.sendCh)
go workConnReaderFn(pxy.workConn, pxy.readCh)
go heartbeatFn(pxy.workConn, pxy.sendCh)
udp.Forwarder(pxy.localAddr, pxy.readCh, pxy.sendCh, int(pxy.clientCfg.UDPPacketSize))
}
type SUDPProxy struct {
*BaseProxy
cfg *config.SUDPProxyConf
localAddr *net.UDPAddr
closeCh chan struct{}
}
func (pxy *SUDPProxy) Run() (err error) {
pxy.localAddr, err = net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pxy.cfg.LocalIP, pxy.cfg.LocalPort))
if err != nil {
return
}
return
}
func (pxy *SUDPProxy) Close() {
pxy.mu.Lock()
defer pxy.mu.Unlock()
select {
case <-pxy.closeCh:
return
default:
close(pxy.closeCh)
}
}
func (pxy *SUDPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
xl := pxy.xl
xl.Info("incoming a new work connection for sudp proxy, %s", conn.RemoteAddr().String())
var rwc io.ReadWriteCloser = conn
var err error
if pxy.limiter != nil {
rwc = frpIo.WrapReadWriteCloser(limit.NewReader(conn, pxy.limiter), limit.NewWriter(conn, pxy.limiter), func() error {
return conn.Close()
})
}
if pxy.cfg.UseEncryption {
rwc, err = frpIo.WithEncryption(rwc, []byte(pxy.clientCfg.Token))
if err != nil {
conn.Close()
xl.Error("create encryption stream error: %v", err)
return
}
}
if pxy.cfg.UseCompression {
rwc = frpIo.WithCompression(rwc)
}
conn = frpNet.WrapReadWriteCloserToConn(rwc, conn)
workConn := conn
readCh := make(chan *msg.UDPPacket, 1024)
sendCh := make(chan msg.Message, 1024)
isClose := false
mu := &sync.Mutex{}
closeFn := func() {
mu.Lock()
defer mu.Unlock()
if isClose {
return
}
isClose = true
if workConn != nil {
workConn.Close()
}
close(readCh)
close(sendCh)
}
// udp service <- frpc <- frps <- frpc visitor <- user
workConnReaderFn := func(conn net.Conn, readCh chan *msg.UDPPacket) {
defer closeFn()
for {
// first to check sudp proxy is closed or not
select {
case <-pxy.closeCh:
xl.Trace("frpc sudp proxy is closed")
return
default:
}
var udpMsg msg.UDPPacket
if errRet := msg.ReadMsgInto(conn, &udpMsg); errRet != nil {
xl.Warn("read from workConn for sudp error: %v", errRet)
return
}
if errRet := errors.PanicToError(func() {
readCh <- &udpMsg
}); errRet != nil {
xl.Warn("reader goroutine for sudp work connection closed: %v", errRet)
return
}
}
}
// udp service -> frpc -> frps -> frpc visitor -> user
workConnSenderFn := func(conn net.Conn, sendCh chan msg.Message) {
defer func() {
closeFn()
xl.Info("writer goroutine for sudp work connection closed")
}()
var errRet error
for rawMsg := range sendCh {
switch m := rawMsg.(type) {
case *msg.UDPPacket:
xl.Trace("frpc send udp package to frpc visitor, [udp local: %v, remote: %v], [tcp work conn local: %v, remote: %v]",
m.LocalAddr.String(), m.RemoteAddr.String(), conn.LocalAddr().String(), conn.RemoteAddr().String())
case *msg.Ping:
xl.Trace("frpc send ping message to frpc visitor")
}
if errRet = msg.WriteMsg(conn, rawMsg); errRet != nil {
xl.Error("sudp work write error: %v", errRet)
return
}
}
}
heartbeatFn := func(conn net.Conn, sendCh chan msg.Message) {
ticker := time.NewTicker(30 * time.Second)
defer func() {
ticker.Stop()
closeFn()
}()
var errRet error
for {
select {
case <-ticker.C:
if errRet = errors.PanicToError(func() {
sendCh <- &msg.Ping{}
}); errRet != nil {
xl.Warn("heartbeat goroutine for sudp work connection closed")
return
}
case <-pxy.closeCh:
xl.Trace("frpc sudp proxy is closed")
return
}
}
}
go workConnSenderFn(workConn, sendCh)
go workConnReaderFn(workConn, readCh)
go heartbeatFn(workConn, sendCh)
udp.Forwarder(pxy.localAddr, readCh, sendCh, int(pxy.clientCfg.UDPPacketSize))
}
// Common handler for tcp work connections.
func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf, proxyPlugin plugin.Plugin,
baseInfo *config.BaseProxyConf, limiter *rate.Limiter, workConn net.Conn, encKey []byte, m *msg.StartWorkConn) {
xl := xlog.FromContextSafe(ctx)
var (
remote io.ReadWriteCloser
err error
)
remote = workConn
if limiter != nil {
remote = frpIo.WrapReadWriteCloser(limit.NewReader(workConn, limiter), limit.NewWriter(workConn, limiter), func() error {
return workConn.Close()
})
}
xl.Trace("handle tcp work connection, use_encryption: %t, use_compression: %t",
baseInfo.UseEncryption, baseInfo.UseCompression)
if baseInfo.UseEncryption {
remote, err = frpIo.WithEncryption(remote, encKey)
if err != nil {
workConn.Close()
xl.Error("create encryption stream error: %v", err)
return
}
}
if baseInfo.UseCompression {
remote = frpIo.WithCompression(remote)
}
// check if we need to send proxy protocol info
var extraInfo []byte
if baseInfo.ProxyProtocolVersion != "" {
if m.SrcAddr != "" && m.SrcPort != 0 {
if m.DstAddr == "" {
m.DstAddr = "127.0.0.1"
}
srcAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.SrcAddr, strconv.Itoa(int(m.SrcPort))))
dstAddr, _ := net.ResolveTCPAddr("tcp", net.JoinHostPort(m.DstAddr, strconv.Itoa(int(m.DstPort))))
h := &pp.Header{
Command: pp.PROXY,
SourceAddr: srcAddr,
DestinationAddr: dstAddr,
}
if strings.Contains(m.SrcAddr, ".") {
h.TransportProtocol = pp.TCPv4
} else {
h.TransportProtocol = pp.TCPv6
}
if baseInfo.ProxyProtocolVersion == "v1" {
h.Version = 1
} else if baseInfo.ProxyProtocolVersion == "v2" {
h.Version = 2
}
buf := bytes.NewBuffer(nil)
h.WriteTo(buf)
extraInfo = buf.Bytes()
}
}
if proxyPlugin != nil {
// if plugin is set, let plugin handle connections first
xl.Debug("handle by plugin: %s", proxyPlugin.Name())
proxyPlugin.Handle(remote, workConn, extraInfo)
xl.Debug("handle by plugin finished")
return
}
localConn, err := libdial.Dial(
net.JoinHostPort(localInfo.LocalIP, strconv.Itoa(localInfo.LocalPort)),
libdial.WithTimeout(10*time.Second),
)
if err != nil {
workConn.Close()
xl.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIP, localInfo.LocalPort, err)
return
}
xl.Debug("join connections, localConn(l[%s] r[%s]) workConn(l[%s] r[%s])", localConn.LocalAddr().String(),
localConn.RemoteAddr().String(), workConn.LocalAddr().String(), workConn.RemoteAddr().String())
if len(extraInfo) > 0 {
localConn.Write(extraInfo)
}
frpIo.Join(localConn, remote)
xl.Debug("join connections closed")
}

View File

@@ -0,0 +1,146 @@
package proxy
import (
"context"
"fmt"
"net"
"sync"
"github.com/fatedier/frp/client/event"
"github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/msg"
"github.com/fatedier/frp/pkg/util/xlog"
"github.com/fatedier/golib/errors"
)
type Manager struct {
sendCh chan (msg.Message)
proxies map[string]*Wrapper
closed bool
mu sync.RWMutex
clientCfg config.ClientCommonConf
// The UDP port that the server is listening on
serverUDPPort int
ctx context.Context
}
func NewManager(ctx context.Context, msgSendCh chan (msg.Message), clientCfg config.ClientCommonConf, serverUDPPort int) *Manager {
return &Manager{
sendCh: msgSendCh,
proxies: make(map[string]*Wrapper),
closed: false,
clientCfg: clientCfg,
serverUDPPort: serverUDPPort,
ctx: ctx,
}
}
func (pm *Manager) StartProxy(name string, remoteAddr string, serverRespErr string) error {
pm.mu.RLock()
pxy, ok := pm.proxies[name]
pm.mu.RUnlock()
if !ok {
return fmt.Errorf("proxy [%s] not found", name)
}
err := pxy.SetRunningStatus(remoteAddr, serverRespErr)
if err != nil {
return err
}
return nil
}
func (pm *Manager) Close() {
pm.mu.Lock()
defer pm.mu.Unlock()
for _, pxy := range pm.proxies {
pxy.Stop()
}
pm.proxies = make(map[string]*Wrapper)
}
func (pm *Manager) HandleWorkConn(name string, workConn net.Conn, m *msg.StartWorkConn) {
pm.mu.RLock()
pw, ok := pm.proxies[name]
pm.mu.RUnlock()
if ok {
pw.InWorkConn(workConn, m)
} else {
workConn.Close()
}
}
func (pm *Manager) HandleEvent(evType event.Type, payload interface{}) error {
var m msg.Message
switch e := payload.(type) {
case *event.StartProxyPayload:
m = e.NewProxyMsg
case *event.CloseProxyPayload:
m = e.CloseProxyMsg
default:
return event.ErrPayloadType
}
err := errors.PanicToError(func() {
pm.sendCh <- m
})
return err
}
func (pm *Manager) GetAllProxyStatus() []*WorkingStatus {
ps := make([]*WorkingStatus, 0)
pm.mu.RLock()
defer pm.mu.RUnlock()
for _, pxy := range pm.proxies {
ps = append(ps, pxy.GetStatus())
}
return ps
}
func (pm *Manager) Reload(pxyCfgs map[string]config.ProxyConf) {
xl := xlog.FromContextSafe(pm.ctx)
pm.mu.Lock()
defer pm.mu.Unlock()
delPxyNames := make([]string, 0)
for name, pxy := range pm.proxies {
del := false
cfg, ok := pxyCfgs[name]
if !ok {
del = true
} else {
if !pxy.Cfg.Compare(cfg) {
del = true
}
}
if del {
delPxyNames = append(delPxyNames, name)
delete(pm.proxies, name)
pxy.Stop()
}
}
if len(delPxyNames) > 0 {
xl.Info("proxy removed: %v", delPxyNames)
}
addPxyNames := make([]string, 0)
for name, cfg := range pxyCfgs {
if _, ok := pm.proxies[name]; !ok {
pxy := NewWrapper(pm.ctx, cfg, pm.clientCfg, pm.HandleEvent, pm.serverUDPPort)
pm.proxies[name] = pxy
addPxyNames = append(addPxyNames, name)
pxy.Start()
}
}
if len(addPxyNames) > 0 {
xl.Info("proxy added: %v", addPxyNames)
}
}

View File

@@ -0,0 +1,250 @@
package proxy
import (
"context"
"fmt"
"net"
"sync"
"sync/atomic"
"time"
"github.com/fatedier/frp/client/event"
"github.com/fatedier/frp/client/health"
"github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/msg"
"github.com/fatedier/frp/pkg/util/xlog"
"github.com/fatedier/golib/errors"
)
const (
ProxyPhaseNew = "new"
ProxyPhaseWaitStart = "wait start"
ProxyPhaseStartErr = "start error"
ProxyPhaseRunning = "running"
ProxyPhaseCheckFailed = "check failed"
ProxyPhaseClosed = "closed"
)
var (
statusCheckInterval time.Duration = 3 * time.Second
waitResponseTimeout = 20 * time.Second
startErrTimeout = 30 * time.Second
)
type WorkingStatus struct {
Name string `json:"name"`
Type string `json:"type"`
Phase string `json:"status"`
Err string `json:"err"`
Cfg config.ProxyConf `json:"cfg"`
// Got from server.
RemoteAddr string `json:"remote_addr"`
}
type Wrapper struct {
WorkingStatus
// underlying proxy
pxy Proxy
// if ProxyConf has healcheck config
// monitor will watch if it is alive
monitor *health.Monitor
// event handler
handler event.Handler
health uint32
lastSendStartMsg time.Time
lastStartErr time.Time
closeCh chan struct{}
healthNotifyCh chan struct{}
mu sync.RWMutex
xl *xlog.Logger
ctx context.Context
}
func NewWrapper(ctx context.Context, cfg config.ProxyConf, clientCfg config.ClientCommonConf, eventHandler event.Handler, serverUDPPort int) *Wrapper {
baseInfo := cfg.GetBaseInfo()
xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(baseInfo.ProxyName)
pw := &Wrapper{
WorkingStatus: WorkingStatus{
Name: baseInfo.ProxyName,
Type: baseInfo.ProxyType,
Phase: ProxyPhaseNew,
Cfg: cfg,
},
closeCh: make(chan struct{}),
healthNotifyCh: make(chan struct{}),
handler: eventHandler,
xl: xl,
ctx: xlog.NewContext(ctx, xl),
}
if baseInfo.HealthCheckType != "" {
pw.health = 1 // means failed
pw.monitor = health.NewMonitor(pw.ctx, baseInfo.HealthCheckType, baseInfo.HealthCheckIntervalS,
baseInfo.HealthCheckTimeoutS, baseInfo.HealthCheckMaxFailed, baseInfo.HealthCheckAddr,
baseInfo.HealthCheckURL, pw.statusNormalCallback, pw.statusFailedCallback)
xl.Trace("enable health check monitor")
}
pw.pxy = NewProxy(pw.ctx, pw.Cfg, clientCfg, serverUDPPort)
return pw
}
func (pw *Wrapper) SetRunningStatus(remoteAddr string, respErr string) error {
pw.mu.Lock()
defer pw.mu.Unlock()
if pw.Phase != ProxyPhaseWaitStart {
return fmt.Errorf("status not wait start, ignore start message")
}
pw.RemoteAddr = remoteAddr
if respErr != "" {
pw.Phase = ProxyPhaseStartErr
pw.Err = respErr
pw.lastStartErr = time.Now()
return fmt.Errorf(pw.Err)
}
if err := pw.pxy.Run(); err != nil {
pw.close()
pw.Phase = ProxyPhaseStartErr
pw.Err = err.Error()
pw.lastStartErr = time.Now()
return err
}
pw.Phase = ProxyPhaseRunning
pw.Err = ""
return nil
}
func (pw *Wrapper) Start() {
go pw.checkWorker()
if pw.monitor != nil {
go pw.monitor.Start()
}
}
func (pw *Wrapper) Stop() {
pw.mu.Lock()
defer pw.mu.Unlock()
close(pw.closeCh)
close(pw.healthNotifyCh)
pw.pxy.Close()
if pw.monitor != nil {
pw.monitor.Stop()
}
pw.Phase = ProxyPhaseClosed
pw.close()
}
func (pw *Wrapper) close() {
pw.handler(event.EvCloseProxy, &event.CloseProxyPayload{
CloseProxyMsg: &msg.CloseProxy{
ProxyName: pw.Name,
},
})
}
func (pw *Wrapper) checkWorker() {
xl := pw.xl
if pw.monitor != nil {
// let monitor do check request first
time.Sleep(500 * time.Millisecond)
}
for {
// check proxy status
now := time.Now()
if atomic.LoadUint32(&pw.health) == 0 {
pw.mu.Lock()
if pw.Phase == ProxyPhaseNew ||
pw.Phase == ProxyPhaseCheckFailed ||
(pw.Phase == ProxyPhaseWaitStart && now.After(pw.lastSendStartMsg.Add(waitResponseTimeout))) ||
(pw.Phase == ProxyPhaseStartErr && now.After(pw.lastStartErr.Add(startErrTimeout))) {
xl.Trace("change status from [%s] to [%s]", pw.Phase, ProxyPhaseWaitStart)
pw.Phase = ProxyPhaseWaitStart
var newProxyMsg msg.NewProxy
pw.Cfg.MarshalToMsg(&newProxyMsg)
pw.lastSendStartMsg = now
pw.handler(event.EvStartProxy, &event.StartProxyPayload{
NewProxyMsg: &newProxyMsg,
})
}
pw.mu.Unlock()
} else {
pw.mu.Lock()
if pw.Phase == ProxyPhaseRunning || pw.Phase == ProxyPhaseWaitStart {
pw.close()
xl.Trace("change status from [%s] to [%s]", pw.Phase, ProxyPhaseCheckFailed)
pw.Phase = ProxyPhaseCheckFailed
}
pw.mu.Unlock()
}
select {
case <-pw.closeCh:
return
case <-time.After(statusCheckInterval):
case <-pw.healthNotifyCh:
}
}
}
func (pw *Wrapper) statusNormalCallback() {
xl := pw.xl
atomic.StoreUint32(&pw.health, 0)
errors.PanicToError(func() {
select {
case pw.healthNotifyCh <- struct{}{}:
default:
}
})
xl.Info("health check success")
}
func (pw *Wrapper) statusFailedCallback() {
xl := pw.xl
atomic.StoreUint32(&pw.health, 1)
errors.PanicToError(func() {
select {
case pw.healthNotifyCh <- struct{}{}:
default:
}
})
xl.Info("health check failed")
}
func (pw *Wrapper) InWorkConn(workConn net.Conn, m *msg.StartWorkConn) {
xl := pw.xl
pw.mu.RLock()
pxy := pw.pxy
pw.mu.RUnlock()
if pxy != nil && pw.Phase == ProxyPhaseRunning {
xl.Debug("start a new work connection, localAddr: %s remoteAddr: %s", workConn.LocalAddr().String(), workConn.RemoteAddr().String())
go pxy.InWorkConn(workConn, m)
} else {
workConn.Close()
}
}
func (pw *Wrapper) GetStatus() *WorkingStatus {
pw.mu.RLock()
defer pw.mu.RUnlock()
ps := &WorkingStatus{
Name: pw.Name,
Type: pw.Type,
Phase: pw.Phase,
Err: pw.Err,
Cfg: pw.Cfg,
RemoteAddr: pw.RemoteAddr,
}
return ps
}

383
client/service.go Normal file
View File

@@ -0,0 +1,383 @@
// 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 client
import (
"context"
"crypto/tls"
"fmt"
"io"
"math/rand"
"net"
"runtime"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/fatedier/frp/assets"
"github.com/fatedier/frp/pkg/auth"
"github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/msg"
"github.com/fatedier/frp/pkg/transport"
"github.com/fatedier/frp/pkg/util/log"
frpNet "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/util"
"github.com/fatedier/frp/pkg/util/version"
"github.com/fatedier/frp/pkg/util/xlog"
"github.com/fatedier/golib/crypto"
libdial "github.com/fatedier/golib/net/dial"
fmux "github.com/hashicorp/yamux"
)
func init() {
crypto.DefaultSalt = "frp"
rand.Seed(time.Now().UnixNano())
}
// Service is a client service.
type Service struct {
// uniq id got from frps, attach it in loginMsg
runID string
// manager control connection with server
ctl *Control
ctlMu sync.RWMutex
// Sets authentication based on selected method
authSetter auth.Setter
cfg config.ClientCommonConf
pxyCfgs map[string]config.ProxyConf
visitorCfgs map[string]config.VisitorConf
cfgMu sync.RWMutex
// The configuration file used to initialize this client, or an empty
// string if no configuration file was used.
cfgFile string
// This is configured by the login response from frps
serverUDPPort int
exit uint32 // 0 means not exit
// service context
ctx context.Context
// call cancel to stop service
cancel context.CancelFunc
}
func NewService(cfg config.ClientCommonConf, pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf, cfgFile string) (svr *Service, err error) {
ctx, cancel := context.WithCancel(context.Background())
svr = &Service{
authSetter: auth.NewAuthSetter(cfg.ClientConfig),
cfg: cfg,
cfgFile: cfgFile,
pxyCfgs: pxyCfgs,
visitorCfgs: visitorCfgs,
exit: 0,
ctx: xlog.NewContext(ctx, xlog.New()),
cancel: cancel,
}
return
}
func (svr *Service) GetController() *Control {
svr.ctlMu.RLock()
defer svr.ctlMu.RUnlock()
return svr.ctl
}
func (svr *Service) Run() error {
xl := xlog.FromContextSafe(svr.ctx)
// set custom DNSServer
if svr.cfg.DNSServer != "" {
dnsAddr := svr.cfg.DNSServer
if !strings.Contains(dnsAddr, ":") {
dnsAddr += ":53"
}
// Change default dns server for frpc
net.DefaultResolver = &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
return net.Dial("udp", dnsAddr)
},
}
}
// login to frps
for {
conn, session, err := svr.login()
if err != nil {
xl.Warn("login to server failed: %v", err)
// if login_fail_exit is true, just exit this program
// otherwise sleep a while and try again to connect to server
if svr.cfg.LoginFailExit {
return err
}
util.RandomSleep(10*time.Second, 0.9, 1.1)
} else {
// login success
ctl := NewControl(svr.ctx, svr.runID, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort, svr.authSetter)
ctl.Run()
svr.ctlMu.Lock()
svr.ctl = ctl
svr.ctlMu.Unlock()
break
}
}
go svr.keepControllerWorking()
if svr.cfg.AdminPort != 0 {
// Init admin server assets
assets.Load(svr.cfg.AssetsDir)
address := net.JoinHostPort(svr.cfg.AdminAddr, strconv.Itoa(svr.cfg.AdminPort))
err := svr.RunAdminServer(address)
if err != nil {
log.Warn("run admin server error: %v", err)
}
log.Info("admin server listen on %s:%d", svr.cfg.AdminAddr, svr.cfg.AdminPort)
}
<-svr.ctx.Done()
return nil
}
func (svr *Service) keepControllerWorking() {
xl := xlog.FromContextSafe(svr.ctx)
maxDelayTime := 20 * time.Second
delayTime := time.Second
// if frpc reconnect frps, we need to limit retry times in 1min
// current retry logic is sleep 0s, 0s, 0s, 1s, 2s, 4s, 8s, ...
// when exceed 1min, we will reset delay and counts
cutoffTime := time.Now().Add(time.Minute)
reconnectDelay := time.Second
reconnectCounts := 1
for {
<-svr.ctl.ClosedDoneCh()
if atomic.LoadUint32(&svr.exit) != 0 {
return
}
// the first three retry with no delay
if reconnectCounts > 3 {
util.RandomSleep(reconnectDelay, 0.9, 1.1)
xl.Info("wait %v to reconnect", reconnectDelay)
reconnectDelay *= 2
} else {
util.RandomSleep(time.Second, 0, 0.5)
}
reconnectCounts++
now := time.Now()
if now.After(cutoffTime) {
// reset
cutoffTime = now.Add(time.Minute)
reconnectDelay = time.Second
reconnectCounts = 1
}
for {
xl.Info("try to reconnect to server...")
conn, session, err := svr.login()
if err != nil {
xl.Warn("reconnect to server error: %v, wait %v for another retry", err, delayTime)
util.RandomSleep(delayTime, 0.9, 1.1)
delayTime = delayTime * 2
if delayTime > maxDelayTime {
delayTime = maxDelayTime
}
continue
}
// reconnect success, init delayTime
delayTime = time.Second
ctl := NewControl(svr.ctx, svr.runID, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort, svr.authSetter)
ctl.Run()
svr.ctlMu.Lock()
if svr.ctl != nil {
svr.ctl.Close()
}
svr.ctl = ctl
svr.ctlMu.Unlock()
break
}
}
}
// login creates a connection to frps and registers it self as a client
// conn: control connection
// session: if it's not nil, using tcp mux
func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) {
xl := xlog.FromContextSafe(svr.ctx)
var tlsConfig *tls.Config
if svr.cfg.TLSEnable {
sn := svr.cfg.TLSServerName
if sn == "" {
sn = svr.cfg.ServerAddr
}
tlsConfig, err = transport.NewClientTLSConfig(
svr.cfg.TLSCertFile,
svr.cfg.TLSKeyFile,
svr.cfg.TLSTrustedCaFile,
sn)
if err != nil {
xl.Warn("fail to build tls configuration when service login, err: %v", err)
return
}
}
proxyType, addr, auth, err := libdial.ParseProxyURL(svr.cfg.HTTPProxy)
if err != nil {
xl.Error("fail to parse proxy url")
return
}
dialOptions := []libdial.DialOption{}
protocol := svr.cfg.Protocol
if protocol == "websocket" {
protocol = "tcp"
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: frpNet.DialHookWebsocket()}))
}
if svr.cfg.ConnectServerLocalIP != "" {
dialOptions = append(dialOptions, libdial.WithLocalAddr(svr.cfg.ConnectServerLocalIP))
}
dialOptions = append(dialOptions,
libdial.WithProtocol(protocol),
libdial.WithTimeout(time.Duration(svr.cfg.DialServerTimeout)*time.Second),
libdial.WithKeepAlive(time.Duration(svr.cfg.DialServerKeepAlive)*time.Second),
libdial.WithProxy(proxyType, addr),
libdial.WithProxyAuth(auth),
libdial.WithTLSConfig(tlsConfig),
libdial.WithAfterHook(libdial.AfterHook{
Hook: frpNet.DialHookCustomTLSHeadByte(tlsConfig != nil, svr.cfg.DisableCustomTLSFirstByte),
}),
)
conn, err = libdial.Dial(
net.JoinHostPort(svr.cfg.ServerAddr, strconv.Itoa(svr.cfg.ServerPort)),
dialOptions...,
)
if err != nil {
return
}
defer func() {
if err != nil {
conn.Close()
if session != nil {
session.Close()
}
}
}()
if svr.cfg.TCPMux {
fmuxCfg := fmux.DefaultConfig()
fmuxCfg.KeepAliveInterval = time.Duration(svr.cfg.TCPMuxKeepaliveInterval) * time.Second
fmuxCfg.LogOutput = io.Discard
session, err = fmux.Client(conn, fmuxCfg)
if err != nil {
return
}
stream, errRet := session.OpenStream()
if errRet != nil {
session.Close()
err = errRet
return
}
conn = stream
}
loginMsg := &msg.Login{
Arch: runtime.GOARCH,
Os: runtime.GOOS,
PoolCount: svr.cfg.PoolCount,
User: svr.cfg.User,
Version: version.Full(),
Timestamp: time.Now().Unix(),
RunID: svr.runID,
Metas: svr.cfg.Metas,
}
// Add auth
if err = svr.authSetter.SetLogin(loginMsg); err != nil {
return
}
if err = msg.WriteMsg(conn, loginMsg); err != nil {
return
}
var loginRespMsg msg.LoginResp
conn.SetReadDeadline(time.Now().Add(10 * time.Second))
if err = msg.ReadMsgInto(conn, &loginRespMsg); err != nil {
return
}
conn.SetReadDeadline(time.Time{})
if loginRespMsg.Error != "" {
err = fmt.Errorf("%s", loginRespMsg.Error)
xl.Error("%s", loginRespMsg.Error)
return
}
svr.runID = loginRespMsg.RunID
xl.ResetPrefixes()
xl.AppendPrefix(svr.runID)
svr.serverUDPPort = loginRespMsg.ServerUDPPort
xl.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunID, loginRespMsg.ServerUDPPort)
return
}
func (svr *Service) ReloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) error {
svr.cfgMu.Lock()
svr.pxyCfgs = pxyCfgs
svr.visitorCfgs = visitorCfgs
svr.cfgMu.Unlock()
svr.ctlMu.RLock()
ctl := svr.ctl
svr.ctlMu.RUnlock()
if ctl != nil {
return svr.ctl.ReloadConf(pxyCfgs, visitorCfgs)
}
return nil
}
func (svr *Service) Close() {
svr.GracefulClose(time.Duration(0))
}
func (svr *Service) GracefulClose(d time.Duration) {
atomic.StoreUint32(&svr.exit, 1)
svr.ctlMu.RLock()
if svr.ctl != nil {
svr.ctl.GracefulClose(d)
}
svr.ctlMu.RUnlock()
svr.cancel()
}

567
client/visitor.go Normal file
View File

@@ -0,0 +1,567 @@
// 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 client
import (
"bytes"
"context"
"fmt"
"io"
"net"
"strconv"
"sync"
"time"
"github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/msg"
"github.com/fatedier/frp/pkg/proto/udp"
frpNet "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/pkg/util/util"
"github.com/fatedier/frp/pkg/util/xlog"
"github.com/fatedier/golib/errors"
frpIo "github.com/fatedier/golib/io"
"github.com/fatedier/golib/pool"
fmux "github.com/hashicorp/yamux"
)
// Visitor is used for forward traffics from local port tot remote service.
type Visitor interface {
Run() error
Close()
}
func NewVisitor(ctx context.Context, ctl *Control, cfg config.VisitorConf) (visitor Visitor) {
xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(cfg.GetBaseInfo().ProxyName)
baseVisitor := BaseVisitor{
ctl: ctl,
ctx: xlog.NewContext(ctx, xl),
}
switch cfg := cfg.(type) {
case *config.STCPVisitorConf:
visitor = &STCPVisitor{
BaseVisitor: &baseVisitor,
cfg: cfg,
}
case *config.XTCPVisitorConf:
visitor = &XTCPVisitor{
BaseVisitor: &baseVisitor,
cfg: cfg,
}
case *config.SUDPVisitorConf:
visitor = &SUDPVisitor{
BaseVisitor: &baseVisitor,
cfg: cfg,
checkCloseCh: make(chan struct{}),
}
}
return
}
type BaseVisitor struct {
ctl *Control
l net.Listener
closed bool
mu sync.RWMutex
ctx context.Context
}
type STCPVisitor struct {
*BaseVisitor
cfg *config.STCPVisitorConf
}
func (sv *STCPVisitor) Run() (err error) {
sv.l, err = net.Listen("tcp", net.JoinHostPort(sv.cfg.BindAddr, strconv.Itoa(sv.cfg.BindPort)))
if err != nil {
return
}
go sv.worker()
return
}
func (sv *STCPVisitor) Close() {
sv.l.Close()
}
func (sv *STCPVisitor) worker() {
xl := xlog.FromContextSafe(sv.ctx)
for {
conn, err := sv.l.Accept()
if err != nil {
xl.Warn("stcp local listener closed")
return
}
go sv.handleConn(conn)
}
}
func (sv *STCPVisitor) handleConn(userConn net.Conn) {
xl := xlog.FromContextSafe(sv.ctx)
defer userConn.Close()
xl.Debug("get a new stcp user connection")
visitorConn, err := sv.ctl.connectServer()
if err != nil {
return
}
defer visitorConn.Close()
now := time.Now().Unix()
newVisitorConnMsg := &msg.NewVisitorConn{
ProxyName: sv.cfg.ServerName,
SignKey: util.GetAuthKey(sv.cfg.Sk, now),
Timestamp: now,
UseEncryption: sv.cfg.UseEncryption,
UseCompression: sv.cfg.UseCompression,
}
err = msg.WriteMsg(visitorConn, newVisitorConnMsg)
if err != nil {
xl.Warn("send newVisitorConnMsg to server error: %v", err)
return
}
var newVisitorConnRespMsg msg.NewVisitorConnResp
visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg)
if err != nil {
xl.Warn("get newVisitorConnRespMsg error: %v", err)
return
}
visitorConn.SetReadDeadline(time.Time{})
if newVisitorConnRespMsg.Error != "" {
xl.Warn("start new visitor connection error: %s", newVisitorConnRespMsg.Error)
return
}
var remote io.ReadWriteCloser
remote = visitorConn
if sv.cfg.UseEncryption {
remote, err = frpIo.WithEncryption(remote, []byte(sv.cfg.Sk))
if err != nil {
xl.Error("create encryption stream error: %v", err)
return
}
}
if sv.cfg.UseCompression {
remote = frpIo.WithCompression(remote)
}
frpIo.Join(userConn, remote)
}
type XTCPVisitor struct {
*BaseVisitor
cfg *config.XTCPVisitorConf
}
func (sv *XTCPVisitor) Run() (err error) {
sv.l, err = net.Listen("tcp", net.JoinHostPort(sv.cfg.BindAddr, strconv.Itoa(sv.cfg.BindPort)))
if err != nil {
return
}
go sv.worker()
return
}
func (sv *XTCPVisitor) Close() {
sv.l.Close()
}
func (sv *XTCPVisitor) worker() {
xl := xlog.FromContextSafe(sv.ctx)
for {
conn, err := sv.l.Accept()
if err != nil {
xl.Warn("xtcp local listener closed")
return
}
go sv.handleConn(conn)
}
}
func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
xl := xlog.FromContextSafe(sv.ctx)
defer userConn.Close()
xl.Debug("get a new xtcp user connection")
if sv.ctl.serverUDPPort == 0 {
xl.Error("xtcp is not supported by server")
return
}
raddr, err := net.ResolveUDPAddr("udp",
fmt.Sprintf("%s:%d", sv.ctl.clientCfg.ServerAddr, sv.ctl.serverUDPPort))
if err != nil {
xl.Error("resolve server UDP addr error")
return
}
visitorConn, err := net.DialUDP("udp", nil, raddr)
if err != nil {
xl.Warn("dial server udp addr error: %v", err)
return
}
defer visitorConn.Close()
now := time.Now().Unix()
natHoleVisitorMsg := &msg.NatHoleVisitor{
ProxyName: sv.cfg.ServerName,
SignKey: util.GetAuthKey(sv.cfg.Sk, now),
Timestamp: now,
}
err = msg.WriteMsg(visitorConn, natHoleVisitorMsg)
if err != nil {
xl.Warn("send natHoleVisitorMsg to server error: %v", err)
return
}
// Wait for client address at most 10 seconds.
var natHoleRespMsg msg.NatHoleResp
visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
buf := pool.GetBuf(1024)
n, err := visitorConn.Read(buf)
if err != nil {
xl.Warn("get natHoleRespMsg error: %v", err)
return
}
err = msg.ReadMsgInto(bytes.NewReader(buf[:n]), &natHoleRespMsg)
if err != nil {
xl.Warn("get natHoleRespMsg error: %v", err)
return
}
visitorConn.SetReadDeadline(time.Time{})
pool.PutBuf(buf)
if natHoleRespMsg.Error != "" {
xl.Error("natHoleRespMsg get error info: %s", natHoleRespMsg.Error)
return
}
xl.Trace("get natHoleRespMsg, sid [%s], client address [%s], visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
// Close visitorConn, so we can use it's local address.
visitorConn.Close()
// send sid message to client
laddr, _ := net.ResolveUDPAddr("udp", visitorConn.LocalAddr().String())
daddr, err := net.ResolveUDPAddr("udp", natHoleRespMsg.ClientAddr)
if err != nil {
xl.Error("resolve client udp address error: %v", err)
return
}
lConn, err := net.DialUDP("udp", laddr, daddr)
if err != nil {
xl.Error("dial client udp address error: %v", err)
return
}
defer lConn.Close()
lConn.Write([]byte(natHoleRespMsg.Sid))
// read ack sid from client
sidBuf := pool.GetBuf(1024)
lConn.SetReadDeadline(time.Now().Add(8 * time.Second))
n, err = lConn.Read(sidBuf)
if err != nil {
xl.Warn("get sid from client error: %v", err)
return
}
lConn.SetReadDeadline(time.Time{})
if string(sidBuf[:n]) != natHoleRespMsg.Sid {
xl.Warn("incorrect sid from client")
return
}
pool.PutBuf(sidBuf)
xl.Info("nat hole connection make success, sid [%s]", natHoleRespMsg.Sid)
// wrap kcp connection
var remote io.ReadWriteCloser
remote, err = frpNet.NewKCPConnFromUDP(lConn, true, natHoleRespMsg.ClientAddr)
if err != nil {
xl.Error("create kcp connection from udp connection error: %v", err)
return
}
fmuxCfg := fmux.DefaultConfig()
fmuxCfg.KeepAliveInterval = 5 * time.Second
fmuxCfg.LogOutput = io.Discard
sess, err := fmux.Client(remote, fmuxCfg)
if err != nil {
xl.Error("create yamux session error: %v", err)
return
}
defer sess.Close()
muxConn, err := sess.Open()
if err != nil {
xl.Error("open yamux stream error: %v", err)
return
}
var muxConnRWCloser io.ReadWriteCloser = muxConn
if sv.cfg.UseEncryption {
muxConnRWCloser, err = frpIo.WithEncryption(muxConnRWCloser, []byte(sv.cfg.Sk))
if err != nil {
xl.Error("create encryption stream error: %v", err)
return
}
}
if sv.cfg.UseCompression {
muxConnRWCloser = frpIo.WithCompression(muxConnRWCloser)
}
frpIo.Join(userConn, muxConnRWCloser)
xl.Debug("join connections closed")
}
type SUDPVisitor struct {
*BaseVisitor
checkCloseCh chan struct{}
// udpConn is the listener of udp packet
udpConn *net.UDPConn
readCh chan *msg.UDPPacket
sendCh chan *msg.UDPPacket
cfg *config.SUDPVisitorConf
}
// SUDP Run start listen a udp port
func (sv *SUDPVisitor) Run() (err error) {
xl := xlog.FromContextSafe(sv.ctx)
addr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(sv.cfg.BindAddr, strconv.Itoa(sv.cfg.BindPort)))
if err != nil {
return fmt.Errorf("sudp ResolveUDPAddr error: %v", err)
}
sv.udpConn, err = net.ListenUDP("udp", addr)
if err != nil {
return fmt.Errorf("listen udp port %s error: %v", addr.String(), err)
}
sv.sendCh = make(chan *msg.UDPPacket, 1024)
sv.readCh = make(chan *msg.UDPPacket, 1024)
xl.Info("sudp start to work, listen on %s", addr)
go sv.dispatcher()
go udp.ForwardUserConn(sv.udpConn, sv.readCh, sv.sendCh, int(sv.ctl.clientCfg.UDPPacketSize))
return
}
func (sv *SUDPVisitor) dispatcher() {
xl := xlog.FromContextSafe(sv.ctx)
var (
visitorConn net.Conn
err error
firstPacket *msg.UDPPacket
)
for {
select {
case firstPacket = <-sv.sendCh:
if firstPacket == nil {
xl.Info("frpc sudp visitor proxy is closed")
return
}
case <-sv.checkCloseCh:
xl.Info("frpc sudp visitor proxy is closed")
return
}
visitorConn, err = sv.getNewVisitorConn()
if err != nil {
xl.Warn("newVisitorConn to frps error: %v, try to reconnect", err)
continue
}
// visitorConn always be closed when worker done.
sv.worker(visitorConn, firstPacket)
select {
case <-sv.checkCloseCh:
return
default:
}
}
}
func (sv *SUDPVisitor) worker(workConn net.Conn, firstPacket *msg.UDPPacket) {
xl := xlog.FromContextSafe(sv.ctx)
xl.Debug("starting sudp proxy worker")
wg := &sync.WaitGroup{}
wg.Add(2)
closeCh := make(chan struct{})
// udp service -> frpc -> frps -> frpc visitor -> user
workConnReaderFn := func(conn net.Conn) {
defer func() {
conn.Close()
close(closeCh)
wg.Done()
}()
for {
var (
rawMsg msg.Message
errRet error
)
// frpc will send heartbeat in workConn to frpc visitor for keeping alive
conn.SetReadDeadline(time.Now().Add(60 * time.Second))
if rawMsg, errRet = msg.ReadMsg(conn); errRet != nil {
xl.Warn("read from workconn for user udp conn error: %v", errRet)
return
}
conn.SetReadDeadline(time.Time{})
switch m := rawMsg.(type) {
case *msg.Ping:
xl.Debug("frpc visitor get ping message from frpc")
continue
case *msg.UDPPacket:
if errRet := errors.PanicToError(func() {
sv.readCh <- m
xl.Trace("frpc visitor get udp packet from workConn: %s", m.Content)
}); errRet != nil {
xl.Info("reader goroutine for udp work connection closed")
return
}
}
}
}
// udp service <- frpc <- frps <- frpc visitor <- user
workConnSenderFn := func(conn net.Conn) {
defer func() {
conn.Close()
wg.Done()
}()
var errRet error
if firstPacket != nil {
if errRet = msg.WriteMsg(conn, firstPacket); errRet != nil {
xl.Warn("sender goroutine for udp work connection closed: %v", errRet)
return
}
xl.Trace("send udp package to workConn: %s", firstPacket.Content)
}
for {
select {
case udpMsg, ok := <-sv.sendCh:
if !ok {
xl.Info("sender goroutine for udp work connection closed")
return
}
if errRet = msg.WriteMsg(conn, udpMsg); errRet != nil {
xl.Warn("sender goroutine for udp work connection closed: %v", errRet)
return
}
xl.Trace("send udp package to workConn: %s", udpMsg.Content)
case <-closeCh:
return
}
}
}
go workConnReaderFn(workConn)
go workConnSenderFn(workConn)
wg.Wait()
xl.Info("sudp worker is closed")
}
func (sv *SUDPVisitor) getNewVisitorConn() (net.Conn, error) {
xl := xlog.FromContextSafe(sv.ctx)
visitorConn, err := sv.ctl.connectServer()
if err != nil {
return nil, fmt.Errorf("frpc connect frps error: %v", err)
}
now := time.Now().Unix()
newVisitorConnMsg := &msg.NewVisitorConn{
ProxyName: sv.cfg.ServerName,
SignKey: util.GetAuthKey(sv.cfg.Sk, now),
Timestamp: now,
UseEncryption: sv.cfg.UseEncryption,
UseCompression: sv.cfg.UseCompression,
}
err = msg.WriteMsg(visitorConn, newVisitorConnMsg)
if err != nil {
return nil, fmt.Errorf("frpc send newVisitorConnMsg to frps error: %v", err)
}
var newVisitorConnRespMsg msg.NewVisitorConnResp
visitorConn.SetReadDeadline(time.Now().Add(10 * time.Second))
err = msg.ReadMsgInto(visitorConn, &newVisitorConnRespMsg)
if err != nil {
return nil, fmt.Errorf("frpc read newVisitorConnRespMsg error: %v", err)
}
visitorConn.SetReadDeadline(time.Time{})
if newVisitorConnRespMsg.Error != "" {
return nil, fmt.Errorf("start new visitor connection error: %s", newVisitorConnRespMsg.Error)
}
var remote io.ReadWriteCloser
remote = visitorConn
if sv.cfg.UseEncryption {
remote, err = frpIo.WithEncryption(remote, []byte(sv.cfg.Sk))
if err != nil {
xl.Error("create encryption stream error: %v", err)
return nil, err
}
}
if sv.cfg.UseCompression {
remote = frpIo.WithCompression(remote)
}
return frpNet.WrapReadWriteCloserToConn(remote, visitorConn), nil
}
func (sv *SUDPVisitor) Close() {
sv.mu.Lock()
defer sv.mu.Unlock()
select {
case <-sv.checkCloseCh:
return
default:
close(sv.checkCloseCh)
}
if sv.udpConn != nil {
sv.udpConn.Close()
}
close(sv.readCh)
close(sv.sendCh)
}

146
client/visitor_manager.go Normal file
View 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 client
import (
"context"
"sync"
"time"
"github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/util/xlog"
)
type VisitorManager struct {
ctl *Control
cfgs map[string]config.VisitorConf
visitors map[string]Visitor
checkInterval time.Duration
mu sync.Mutex
ctx context.Context
stopCh chan struct{}
}
func NewVisitorManager(ctx context.Context, ctl *Control) *VisitorManager {
return &VisitorManager{
ctl: ctl,
cfgs: make(map[string]config.VisitorConf),
visitors: make(map[string]Visitor),
checkInterval: 10 * time.Second,
ctx: ctx,
stopCh: make(chan struct{}),
}
}
func (vm *VisitorManager) Run() {
xl := xlog.FromContextSafe(vm.ctx)
ticker := time.NewTicker(vm.checkInterval)
defer ticker.Stop()
for {
select {
case <-vm.stopCh:
xl.Info("gracefully shutdown visitor manager")
return
case <-ticker.C:
vm.mu.Lock()
for _, cfg := range vm.cfgs {
name := cfg.GetBaseInfo().ProxyName
if _, exist := vm.visitors[name]; !exist {
xl.Info("try to start visitor [%s]", name)
vm.startVisitor(cfg)
}
}
vm.mu.Unlock()
}
}
}
// Hold lock before calling this function.
func (vm *VisitorManager) startVisitor(cfg config.VisitorConf) (err error) {
xl := xlog.FromContextSafe(vm.ctx)
name := cfg.GetBaseInfo().ProxyName
visitor := NewVisitor(vm.ctx, vm.ctl, cfg)
err = visitor.Run()
if err != nil {
xl.Warn("start error: %v", err)
} else {
vm.visitors[name] = visitor
xl.Info("start visitor success")
}
return
}
func (vm *VisitorManager) Reload(cfgs map[string]config.VisitorConf) {
xl := xlog.FromContextSafe(vm.ctx)
vm.mu.Lock()
defer vm.mu.Unlock()
delNames := make([]string, 0)
for name, oldCfg := range vm.cfgs {
del := false
cfg, ok := cfgs[name]
if !ok {
del = true
} else {
if !oldCfg.Compare(cfg) {
del = true
}
}
if del {
delNames = append(delNames, name)
delete(vm.cfgs, name)
if visitor, ok := vm.visitors[name]; ok {
visitor.Close()
}
delete(vm.visitors, name)
}
}
if len(delNames) > 0 {
xl.Info("visitor removed: %v", delNames)
}
addNames := make([]string, 0)
for name, cfg := range cfgs {
if _, ok := vm.cfgs[name]; !ok {
vm.cfgs[name] = cfg
addNames = append(addNames, name)
vm.startVisitor(cfg)
}
}
if len(addNames) > 0 {
xl.Info("visitor added: %v", addNames)
}
return
}
func (vm *VisitorManager) Close() {
vm.mu.Lock()
defer vm.mu.Unlock()
for _, v := range vm.visitors {
v.Close()
}
select {
case <-vm.stopCh:
default:
close(vm.stopCh)
}
}

View File

@@ -12,23 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package msg
package main
type GeneralRes struct {
Code int64 `json:"code"`
Msg string `json:"msg"`
}
import (
_ "github.com/fatedier/frp/assets/frpc"
"github.com/fatedier/frp/cmd/frpc/sub"
)
type ClientCtlReq struct {
Type int64 `json:"type"`
ProxyName string `json:"proxy_name"`
Passwd string `json:"passwd"`
}
type ClientCtlRes struct {
GeneralRes
}
type ServerCtlReq struct {
Type int64 `json:"type"`
func main() {
sub.Execute()
}

90
cmd/frpc/sub/http.go Normal file
View File

@@ -0,0 +1,90 @@
// 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/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/consts"
"github.com/spf13/cobra"
)
func init() {
RegisterCommonFlags(httpCmd)
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 {
clientCfg, err := parseClientCommonCfgFromCmd()
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(clientCfg, proxyConfs, nil, "")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
return nil
},
}

82
cmd/frpc/sub/https.go Normal file
View File

@@ -0,0 +1,82 @@
// 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/pkg/config"
"github.com/fatedier/frp/pkg/consts"
)
func init() {
RegisterCommonFlags(httpsCmd)
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 {
clientCfg, err := parseClientCommonCfgFromCmd()
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(clientCfg, proxyConfs, nil, "")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
return nil
},
}

84
cmd/frpc/sub/reload.go Normal file
View File

@@ -0,0 +1,84 @@
// 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"
"fmt"
"io"
"net/http"
"os"
"strings"
"github.com/fatedier/frp/pkg/config"
"github.com/spf13/cobra"
)
func init() {
rootCmd.AddCommand(reloadCmd)
}
var reloadCmd = &cobra.Command{
Use: "reload",
Short: "Hot-Reload frpc configuration",
RunE: func(cmd *cobra.Command, args []string) error {
cfg, _, _, err := config.ParseClientConfig(cfgFile)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
err = reload(cfg)
if err != nil {
fmt.Printf("frpc reload error: %v\n", err)
os.Exit(1)
}
fmt.Printf("reload success\n")
return nil
},
}
func reload(clientCfg config.ClientCommonConf) error {
if clientCfg.AdminPort == 0 {
return fmt.Errorf("admin_port shoud be set if you want to use reload feature")
}
req, err := http.NewRequest("GET", "http://"+
clientCfg.AdminAddr+":"+fmt.Sprintf("%d", clientCfg.AdminPort)+"/api/reload", nil)
if err != nil {
return err
}
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(clientCfg.AdminUser+":"+
clientCfg.AdminPwd))
req.Header.Add("Authorization", authStr)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode == 200 {
return nil
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
return fmt.Errorf("code [%d], %s", resp.StatusCode, strings.TrimSpace(string(body)))
}

231
cmd/frpc/sub/root.go Normal file
View File

@@ -0,0 +1,231 @@
// 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"
"io/fs"
"net"
"os"
"os/signal"
"path/filepath"
"strconv"
"sync"
"syscall"
"time"
"github.com/fatedier/frp/client"
"github.com/fatedier/frp/pkg/auth"
"github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/util/log"
"github.com/fatedier/frp/pkg/util/version"
"github.com/spf13/cobra"
)
const (
CfgFileTypeIni = iota
CfgFileTypeCmd
)
var (
cfgFile string
cfgDir string
showVersion bool
serverAddr string
user string
protocol string
token string
logLevel string
logFile string
logMaxDays int
disableLogColor bool
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
multiplexer string
serverName string
bindAddr string
bindPort int
tlsEnable bool
)
func init() {
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "./frpc.ini", "config file of frpc")
rootCmd.PersistentFlags().StringVarP(&cfgDir, "config_dir", "", "", "config directory, run one frpc service for each file in config directory")
rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
}
func RegisterCommonFlags(cmd *cobra.Command) {
cmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
cmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
cmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket")
cmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
cmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
cmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
cmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
cmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")
cmd.PersistentFlags().BoolVarP(&tlsEnable, "tls_enable", "", false, "enable frpc tls")
}
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
}
// If cfgDir is not empty, run multiple frpc service for each config file in cfgDir.
// Note that it's only designed for testing. It's not guaranteed to be stable.
if cfgDir != "" {
var wg sync.WaitGroup
filepath.WalkDir(cfgDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return nil
}
if d.IsDir() {
return nil
}
wg.Add(1)
time.Sleep(time.Millisecond)
go func() {
defer wg.Done()
err := runClient(path)
if err != nil {
fmt.Printf("frpc service error for config file [%s]\n", path)
}
}()
return nil
})
wg.Wait()
return nil
}
// Do not show command usage here.
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, doneCh chan struct{}) {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
<-ch
svr.GracefulClose(500 * time.Millisecond)
close(doneCh)
}
func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
cfg = config.GetDefaultClientConf()
ipStr, portStr, err := net.SplitHostPort(serverAddr)
if err != nil {
err = fmt.Errorf("invalid server_addr: %v", err)
return
}
cfg.ServerAddr = ipStr
cfg.ServerPort, err = strconv.Atoi(portStr)
if err != nil {
err = fmt.Errorf("invalid server_addr: %v", err)
return
}
cfg.User = user
cfg.Protocol = protocol
cfg.LogLevel = logLevel
cfg.LogFile = logFile
cfg.LogMaxDays = int64(logMaxDays)
cfg.DisableLogColor = disableLogColor
// Only token authentication is supported in cmd mode
cfg.ClientConfig = auth.GetDefaultClientConf()
cfg.Token = token
cfg.TLSEnable = tlsEnable
cfg.Complete()
if err = cfg.Validate(); err != nil {
err = fmt.Errorf("Parse config error: %v", err)
return
}
return
}
func runClient(cfgFilePath string) error {
cfg, pxyCfgs, visitorCfgs, err := config.ParseClientConfig(cfgFilePath)
if err != nil {
return err
}
return startService(cfg, pxyCfgs, visitorCfgs, cfgFilePath)
}
func startService(
cfg config.ClientCommonConf,
pxyCfgs map[string]config.ProxyConf,
visitorCfgs map[string]config.VisitorConf,
cfgFile string,
) (err error) {
log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel,
cfg.LogMaxDays, cfg.DisableLogColor)
if cfgFile != "" {
log.Trace("start frpc service for config file [%s]", cfgFile)
defer log.Trace("frpc service for config file [%s] stopped", cfgFile)
}
svr, errRet := client.NewService(cfg, pxyCfgs, visitorCfgs, cfgFile)
if errRet != nil {
err = errRet
return
}
kcpDoneCh := make(chan struct{})
// Capture the exit signal if we use kcp.
if cfg.Protocol == "kcp" {
go handleSignal(svr, kcpDoneCh)
}
err = svr.Run()
if err == nil && cfg.Protocol == "kcp" {
<-kcpDoneCh
}
return
}

147
cmd/frpc/sub/status.go Normal file
View File

@@ -0,0 +1,147 @@
// 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"
"net/http"
"os"
"strings"
"github.com/fatedier/frp/client"
"github.com/fatedier/frp/pkg/config"
"github.com/rodaine/table"
"github.com/spf13/cobra"
)
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 {
cfg, _, _, err := config.ParseClientConfig(cfgFile)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if err = status(cfg); err != nil {
fmt.Printf("frpc get status error: %v\n", err)
os.Exit(1)
}
return nil
},
}
func status(clientCfg config.ClientCommonConf) error {
if clientCfg.AdminPort == 0 {
return fmt.Errorf("admin_port shoud be set if you want to get proxy status")
}
req, err := http.NewRequest("GET", "http://"+
clientCfg.AdminAddr+":"+fmt.Sprintf("%d", clientCfg.AdminPort)+"/api/status", nil)
if err != nil {
return err
}
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(clientCfg.AdminUser+":"+
clientCfg.AdminPwd))
req.Header.Add("Authorization", authStr)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
}
body, err := io.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.Println("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.Println("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.Println("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.Println("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.Println("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.Println("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
}

107
cmd/frpc/sub/stcp.go Normal file
View File

@@ -0,0 +1,107 @@
// 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/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/consts"
"github.com/spf13/cobra"
)
func init() {
RegisterCommonFlags(stcpCmd)
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 {
clientCfg, err := parseClientCommonCfgFromCmd()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
proxyConfs := make(map[string]config.ProxyConf)
visitorConfs := make(map[string]config.VisitorConf)
var prefix string
if user != "" {
prefix = user + "."
}
if role == "server" {
cfg := &config.STCPProxyConf{}
cfg.ProxyName = prefix + proxyName
cfg.ProxyType = consts.STCPProxy
cfg.UseEncryption = useEncryption
cfg.UseCompression = useCompression
cfg.Role = role
cfg.Sk = sk
cfg.LocalIP = localIP
cfg.LocalPort = localPort
err = cfg.CheckForCli()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
proxyConfs[cfg.ProxyName] = cfg
} else if role == "visitor" {
cfg := &config.STCPVisitorConf{}
cfg.ProxyName = prefix + proxyName
cfg.ProxyType = consts.STCPProxy
cfg.UseEncryption = useEncryption
cfg.UseCompression = useCompression
cfg.Role = role
cfg.Sk = sk
cfg.ServerName = serverName
cfg.BindAddr = bindAddr
cfg.BindPort = bindPort
err = cfg.Check()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
visitorConfs[cfg.ProxyName] = cfg
} else {
fmt.Println("invalid role")
os.Exit(1)
}
err = startService(clientCfg, proxyConfs, visitorConfs, "")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
return nil
},
}

106
cmd/frpc/sub/sudp.go Normal file
View File

@@ -0,0 +1,106 @@
// 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/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/consts"
"github.com/spf13/cobra"
)
func init() {
RegisterCommonFlags(sudpCmd)
sudpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
sudpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role")
sudpCmd.PersistentFlags().StringVarP(&sk, "sk", "", "", "secret key")
sudpCmd.PersistentFlags().StringVarP(&serverName, "server_name", "", "", "server name")
sudpCmd.PersistentFlags().StringVarP(&localIP, "local_ip", "i", "127.0.0.1", "local ip")
sudpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
sudpCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "", "bind addr")
sudpCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "", 0, "bind port")
sudpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
sudpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
rootCmd.AddCommand(sudpCmd)
}
var sudpCmd = &cobra.Command{
Use: "sudp",
Short: "Run frpc with a single sudp proxy",
RunE: func(cmd *cobra.Command, args []string) error {
clientCfg, err := parseClientCommonCfgFromCmd()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
proxyConfs := make(map[string]config.ProxyConf)
visitorConfs := make(map[string]config.VisitorConf)
var prefix string
if user != "" {
prefix = user + "."
}
if role == "server" {
cfg := &config.SUDPProxyConf{}
cfg.ProxyName = prefix + proxyName
cfg.ProxyType = consts.SUDPProxy
cfg.UseEncryption = useEncryption
cfg.UseCompression = useCompression
cfg.Role = role
cfg.Sk = sk
cfg.LocalIP = localIP
cfg.LocalPort = localPort
err = cfg.CheckForCli()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
proxyConfs[cfg.ProxyName] = cfg
} else if role == "visitor" {
cfg := &config.SUDPVisitorConf{}
cfg.ProxyName = prefix + proxyName
cfg.ProxyType = consts.SUDPProxy
cfg.UseEncryption = useEncryption
cfg.UseCompression = useCompression
cfg.Role = role
cfg.Sk = sk
cfg.ServerName = serverName
cfg.BindAddr = bindAddr
cfg.BindPort = bindPort
err = cfg.Check()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
visitorConfs[cfg.ProxyName] = cfg
} else {
fmt.Println("invalid role")
os.Exit(1)
}
err = startService(clientCfg, proxyConfs, visitorConfs, "")
if err != nil {
os.Exit(1)
}
return nil
},
}

79
cmd/frpc/sub/tcp.go Normal file
View File

@@ -0,0 +1,79 @@
// 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/pkg/config"
"github.com/fatedier/frp/pkg/consts"
)
func init() {
RegisterCommonFlags(tcpCmd)
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 {
clientCfg, err := parseClientCommonCfgFromCmd()
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(clientCfg, proxyConfs, nil, "")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
return nil
},
}

84
cmd/frpc/sub/tcpmux.go Normal file
View File

@@ -0,0 +1,84 @@
// Copyright 2020 guylewin, guy@lewin.co.il
//
// 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/pkg/config"
"github.com/fatedier/frp/pkg/consts"
)
func init() {
RegisterCommonFlags(tcpMuxCmd)
tcpMuxCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
tcpMuxCmd.PersistentFlags().StringVarP(&localIP, "local_ip", "i", "127.0.0.1", "local ip")
tcpMuxCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
tcpMuxCmd.PersistentFlags().StringVarP(&customDomains, "custom_domain", "d", "", "custom domain")
tcpMuxCmd.PersistentFlags().StringVarP(&subDomain, "sd", "", "", "sub domain")
tcpMuxCmd.PersistentFlags().StringVarP(&multiplexer, "mux", "", "", "multiplexer")
tcpMuxCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
tcpMuxCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
rootCmd.AddCommand(tcpMuxCmd)
}
var tcpMuxCmd = &cobra.Command{
Use: "tcpmux",
Short: "Run frpc with a single tcpmux proxy",
RunE: func(cmd *cobra.Command, args []string) error {
clientCfg, err := parseClientCommonCfgFromCmd()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
cfg := &config.TCPMuxProxyConf{}
var prefix string
if user != "" {
prefix = user + "."
}
cfg.ProxyName = prefix + proxyName
cfg.ProxyType = consts.TCPMuxProxy
cfg.LocalIP = localIP
cfg.LocalPort = localPort
cfg.CustomDomains = strings.Split(customDomains, ",")
cfg.SubDomain = subDomain
cfg.Multiplexer = multiplexer
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(clientCfg, proxyConfs, nil, "")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
return nil
},
}

79
cmd/frpc/sub/udp.go Normal file
View File

@@ -0,0 +1,79 @@
// 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/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/consts"
"github.com/spf13/cobra"
)
func init() {
RegisterCommonFlags(udpCmd)
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 {
clientCfg, err := parseClientCommonCfgFromCmd()
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(clientCfg, proxyConfs, nil, "")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
return nil
},
}

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

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

107
cmd/frpc/sub/xtcp.go Normal file
View File

@@ -0,0 +1,107 @@
// 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/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/consts"
"github.com/spf13/cobra"
)
func init() {
RegisterCommonFlags(xtcpCmd)
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 {
clientCfg, err := parseClientCommonCfgFromCmd()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
proxyConfs := make(map[string]config.ProxyConf)
visitorConfs := make(map[string]config.VisitorConf)
var prefix string
if user != "" {
prefix = user + "."
}
if role == "server" {
cfg := &config.XTCPProxyConf{}
cfg.ProxyName = prefix + proxyName
cfg.ProxyType = consts.XTCPProxy
cfg.UseEncryption = useEncryption
cfg.UseCompression = useCompression
cfg.Role = role
cfg.Sk = sk
cfg.LocalIP = localIP
cfg.LocalPort = localPort
err = cfg.CheckForCli()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
proxyConfs[cfg.ProxyName] = cfg
} else if role == "visitor" {
cfg := &config.XTCPVisitorConf{}
cfg.ProxyName = prefix + proxyName
cfg.ProxyType = consts.XTCPProxy
cfg.UseEncryption = useEncryption
cfg.UseCompression = useCompression
cfg.Role = role
cfg.Sk = sk
cfg.ServerName = serverName
cfg.BindAddr = bindAddr
cfg.BindPort = bindPort
err = cfg.Check()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
visitorConfs[cfg.ProxyName] = cfg
} else {
fmt.Println("invalid role")
os.Exit(1)
}
err = startService(clientCfg, proxyConfs, visitorConfs, "")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
return nil
},
}

32
cmd/frps/main.go Normal file
View File

@@ -0,0 +1,32 @@
// 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 (
"math/rand"
"time"
"github.com/fatedier/golib/crypto"
_ "github.com/fatedier/frp/assets/frps"
_ "github.com/fatedier/frp/pkg/metrics"
)
func main() {
crypto.DefaultSalt = "frp"
rand.Seed(time.Now().UnixNano())
Execute()
}

212
cmd/frps/root.go Normal file
View File

@@ -0,0 +1,212 @@
// 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"
"os"
"github.com/fatedier/frp/pkg/auth"
"github.com/fatedier/frp/pkg/config"
"github.com/fatedier/frp/pkg/util/log"
"github.com/fatedier/frp/pkg/util/util"
"github.com/fatedier/frp/pkg/util/version"
"github.com/fatedier/frp/server"
"github.com/spf13/cobra"
)
const (
CfgFileTypeIni = iota
CfgFileTypeCmd
)
var (
cfgFile string
showVersion bool
bindAddr string
bindPort int
bindUDPPort int
kcpBindPort int
proxyBindAddr string
vhostHTTPPort int
vhostHTTPSPort int
vhostHTTPTimeout int64
dashboardAddr string
dashboardPort int
dashboardUser string
dashboardPwd string
enablePrometheus bool
assetsDir string
logFile string
logLevel string
logMaxDays int64
disableLogColor bool
token string
subDomainHost string
tcpMux bool
allowPorts string
maxPoolCount int64
maxPortsPerClient int64
tlsOnly bool
)
func init() {
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file of frps")
rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frps")
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().Int64VarP(&vhostHTTPTimeout, "vhost_http_timeout", "", 60, "vhost http response header timeout")
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().BoolVarP(&enablePrometheus, "enable_prometheus", "", false, "enable prometheus dashboard")
rootCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "log file")
rootCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
rootCmd.PersistentFlags().Int64VarP(&logMaxDays, "log_max_days", "", 3, "log max days")
rootCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")
rootCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
rootCmd.PersistentFlags().StringVarP(&subDomainHost, "subdomain_host", "", "", "subdomain host")
rootCmd.PersistentFlags().StringVarP(&allowPorts, "allow_ports", "", "", "allow ports")
rootCmd.PersistentFlags().Int64VarP(&maxPortsPerClient, "max_ports_per_client", "", 0, "max ports per client")
rootCmd.PersistentFlags().BoolVarP(&tlsOnly, "tls_only", "", false, "frps tls only")
}
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 cfg config.ServerCommonConf
var err error
if cfgFile != "" {
var content []byte
content, err = config.GetRenderedConfFromFile(cfgFile)
if err != nil {
return err
}
cfg, err = parseServerCommonCfg(CfgFileTypeIni, content)
} else {
cfg, err = parseServerCommonCfg(CfgFileTypeCmd, nil)
}
if err != nil {
return err
}
err = runServer(cfg)
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, source []byte) (cfg config.ServerCommonConf, err error) {
if fileType == CfgFileTypeIni {
cfg, err = config.UnmarshalServerConfFromIni(source)
} else if fileType == CfgFileTypeCmd {
cfg, err = parseServerCommonCfgFromCmd()
}
if err != nil {
return
}
cfg.Complete()
err = cfg.Validate()
if err != nil {
err = fmt.Errorf("Parse config error: %v", err)
return
}
return
}
func parseServerCommonCfgFromCmd() (cfg config.ServerCommonConf, err error) {
cfg = config.GetDefaultServerConf()
cfg.BindAddr = bindAddr
cfg.BindPort = bindPort
cfg.BindUDPPort = bindUDPPort
cfg.KCPBindPort = kcpBindPort
cfg.ProxyBindAddr = proxyBindAddr
cfg.VhostHTTPPort = vhostHTTPPort
cfg.VhostHTTPSPort = vhostHTTPSPort
cfg.VhostHTTPTimeout = vhostHTTPTimeout
cfg.DashboardAddr = dashboardAddr
cfg.DashboardPort = dashboardPort
cfg.DashboardUser = dashboardUser
cfg.DashboardPwd = dashboardPwd
cfg.EnablePrometheus = enablePrometheus
cfg.LogFile = logFile
cfg.LogLevel = logLevel
cfg.LogMaxDays = logMaxDays
cfg.SubDomainHost = subDomainHost
cfg.TLSOnly = tlsOnly
// Only token authentication is supported in cmd mode
cfg.ServerConfig = auth.GetDefaultServerConf()
cfg.Token = token
if len(allowPorts) > 0 {
// e.g. 1000-2000,2001,2002,3000-4000
ports, errRet := util.ParseRangeNumbers(allowPorts)
if errRet != nil {
err = fmt.Errorf("Parse conf error: allow_ports: %v", errRet)
return
}
for _, port := range ports {
cfg.AllowPorts[int(port)] = struct{}{}
}
}
cfg.MaxPortsPerClient = maxPortsPerClient
cfg.DisableLogColor = disableLogColor
return
}
func runServer(cfg config.ServerCommonConf) (err error) {
log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel, cfg.LogMaxDays, cfg.DisableLogColor)
if cfgFile != "" {
log.Info("frps uses config file: %s", cfgFile)
} else {
log.Info("frps uses command line arguments for config")
}
svr, err := server.NewService(cfg)
if err != nil {
return err
}
log.Info("frps started successfully")
svr.Run()
return
}

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

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

View File

@@ -1,14 +1,9 @@
# [common] is integral section
[common]
server_addr = 0.0.0.0
server_addr = 127.0.0.1
server_port = 7000
# console or real logFile path like ./frpc.log
log_file = console
# debug, info, warn, error
log_level = debug
# test1 is the proxy name same as server's configuration
[test1]
passwd = 123
[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 6000

350
conf/frpc_full.ini Normal file
View File

@@ -0,0 +1,350 @@
# [common] is integral section
[common]
# A literal address or host name for IPv6 must be enclosed
# in square brackets, as in "[::1]:80", "[ipv6-host]:http" or "[ipv6-host%zone]:80"
# For single "server_addr" field, no need square brackets, like "server_addr = ::".
server_addr = 0.0.0.0
server_port = 7000
# The maximum amount of time a dial to server will wait for a connect to complete. Default value is 10 seconds.
# dial_server_timeout = 10
# dial_server_keepalive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
# If negative, keep-alive probes are disabled.
# dial_server_keepalive = 7200
# if you want to connect frps by http proxy or socks5 proxy or ntlm proxy, you can set http_proxy here or in global environment variables
# it only works when protocol is tcp
# http_proxy = http://user:passwd@192.168.1.128:8080
# http_proxy = socks5://user:passwd@192.168.1.128:1080
# http_proxy = ntlm://user:passwd@192.168.1.128:2080
# console or real logFile path like ./frpc.log
log_file = ./frpc.log
# trace, debug, info, warn, error
log_level = info
log_max_days = 3
# disable log colors when log_file is console, default is false
disable_log_color = false
# for authentication, should be same as your frps.ini
# authenticate_heartbeats specifies whether to include authentication token in heartbeats sent to frps. By default, this value is false.
authenticate_heartbeats = false
# authenticate_new_work_conns specifies whether to include authentication token in new work connections sent to frps. By default, this value is false.
authenticate_new_work_conns = false
# auth token
token = 12345678
# oidc_client_id specifies the client ID to use to get a token in OIDC authentication if AuthenticationMethod == "oidc".
# By default, this value is "".
oidc_client_id =
# oidc_client_secret specifies the client secret to use to get a token in OIDC authentication if AuthenticationMethod == "oidc".
# By default, this value is "".
oidc_client_secret =
# oidc_audience specifies the audience of the token in OIDC authentication if AuthenticationMethod == "oidc". By default, this value is "".
oidc_audience =
# oidc_token_endpoint_url specifies the URL which implements OIDC Token Endpoint.
# It will be used to get an OIDC token if AuthenticationMethod == "oidc". By default, this value is "".
oidc_token_endpoint_url =
# oidc_additional_xxx specifies additional parameters to be sent to the OIDC Token Endpoint.
# For example, if you want to specify the "audience" parameter, you can set as follow.
# frp will add "audience=<value>" "var1=<value>" to the additional parameters.
# oidc_additional_audience = https://dev.auth.com/api/v2/
# oidc_additional_var1 = foobar
# set admin address for control frpc's action by http api such as reload
admin_addr = 127.0.0.1
admin_port = 7400
admin_user = admin
admin_pwd = admin
# Admin assets directory. By default, these assets are bundled with frpc.
# assets_dir = ./static
# connections will be established in advance, default value is zero
pool_count = 5
# if tcp stream multiplexing is used, default is true, it must be same with frps
# tcp_mux = true
# specify keep alive interval for tcp mux.
# only valid if tcp_mux is true.
# tcp_mux_keepalive_interval = 60
# your proxy name will be changed to {user}.{proxy}
user = your_name
# decide if exit program when first login failed, otherwise continuous relogin to frps
# default is true
login_fail_exit = true
# communication protocol used to connect to server
# now it supports tcp, kcp and websocket, default is tcp
protocol = tcp
# set client binding ip when connect server, default is empty.
# only when protocol = tcp or websocket, the value will be used.
connect_server_local_ip = 0.0.0.0
# if tls_enable is true, frpc will connect frps by tls
tls_enable = true
# tls_cert_file = client.crt
# tls_key_file = client.key
# tls_trusted_ca_file = ca.crt
# tls_server_name = example.com
# 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 separated by ','
# default is empty, means all proxies
# start = ssh,dns
# heartbeat configure, it's not recommended to modify the default value
# The default value of heartbeat_interval is 10 and heartbeat_timeout is 90. Set negative value
# to disable it.
# heartbeat_interval = 30
# heartbeat_timeout = 90
# additional meta info for client
meta_var1 = 123
meta_var2 = 234
# specify udp packet size, unit is byte. If not set, the default value is 1500.
# This parameter should be same between client and server.
# It affects the udp and sudp proxy.
udp_packet_size = 1500
# include other config files for proxies.
# includes = ./confd/*.ini
# By default, frpc will connect frps with first custom byte if tls is enabled.
# If DisableCustomTLSFirstByte is true, frpc will not send that custom byte.
disable_custom_tls_first_byte = false
# Enable golang pprof handlers in admin listener.
# Admin port must be set first.
pprof_enable = false
# 'ssh' is the unique proxy name
# if user in [common] section is not empty, it will be changed to {user}.{proxy} such as 'your_name.ssh'
[ssh]
# tcp | udp | http | https | stcp | xtcp, default is tcp
type = tcp
local_ip = 127.0.0.1
local_port = 22
# limit bandwidth for this proxy, unit is KB and MB
bandwidth_limit = 1MB
# true or false, if true, messages between frps and frpc will be encrypted, default is false
use_encryption = false
# if true, message will be compressed
use_compression = false
# remote port listen by frps
remote_port = 6001
# frps will load balancing connections for proxies in same group
group = test_group
# group should have same group key
group_key = 123456
# enable health check for the backend service, it support 'tcp' and 'http' now
# frpc will connect local service's port to detect it's healthy status
health_check_type = tcp
# health check connection timeout
health_check_timeout_s = 3
# if continuous failed in 3 times, the proxy will be removed from frps
health_check_max_failed = 3
# every 10 seconds will do a health check
health_check_interval_s = 10
# additional meta info for each proxy
meta_var1 = 123
meta_var2 = 234
[ssh_random]
type = tcp
local_ip = 127.0.0.1
local_port = 22
# 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
local_port = 53
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
local_ip = 127.0.0.1
local_port = 80
use_encryption = false
use_compression = true
# http username and password are safety certification for http protocol
# if not set, you can access this custom_domains without certification
http_user = admin
http_pwd = admin
# if domain for frps is frps.com, then you can access [web01] proxy by URL http://web01.frps.com
subdomain = web01
custom_domains = web01.yourdomain.com
# locations is only available for http type
locations = /,/pic
host_header_rewrite = example.com
# params with prefix "header_" will be used to update http request headers
header_X-From-Where = frp
health_check_type = http
# frpc will send a GET http request '/status' to local http service
# http service is alive when it return 2xx http response code
health_check_url = /status
health_check_interval_s = 10
health_check_max_failed = 3
health_check_timeout_s = 3
[web02]
type = https
local_ip = 127.0.0.1
local_port = 8000
use_encryption = false
use_compression = false
subdomain = web01
custom_domains = web02.yourdomain.com
# if not empty, frpc will use proxy protocol to transfer connection info to your local service
# v1 or v2 or empty
proxy_protocol_version = v2
[plugin_unix_domain_socket]
type = tcp
remote_port = 6003
# if plugin is defined, local_ip and local_port is useless
# plugin will handle connections got from frps
plugin = unix_domain_socket
# params with prefix "plugin_" that plugin needed
plugin_unix_path = /var/run/docker.sock
[plugin_http_proxy]
type = tcp
remote_port = 6004
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
[plugin_https2http]
type = https
custom_domains = test.yourdomain.com
plugin = https2http
plugin_local_addr = 127.0.0.1:80
plugin_crt_path = ./server.crt
plugin_key_path = ./server.key
plugin_host_header_rewrite = 127.0.0.1
plugin_header_X-From-Where = frp
[plugin_https2https]
type = https
custom_domains = test.yourdomain.com
plugin = https2https
plugin_local_addr = 127.0.0.1:443
plugin_crt_path = ./server.crt
plugin_key_path = ./server.key
plugin_host_header_rewrite = 127.0.0.1
plugin_header_X-From-Where = frp
[plugin_http2https]
type = http
custom_domains = test.yourdomain.com
plugin = http2https
plugin_local_addr = 127.0.0.1:443
plugin_host_header_rewrite = 127.0.0.1
plugin_header_X-From-Where = frp
[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
type = stcp
# sk used for authentication for visitors
sk = abcdefg
local_ip = 127.0.0.1
local_port = 22
use_encryption = false
use_compression = false
# user of frpc should be same in both stcp server and stcp visitor
[secret_tcp_visitor]
# frpc role visitor -> frps -> frpc role server
role = visitor
type = stcp
# the server name you want to visitor
server_name = secret_tcp
sk = abcdefg
# connect this address to visitor stcp server
bind_addr = 127.0.0.1
bind_port = 9000
use_encryption = false
use_compression = false
[p2p_tcp]
type = xtcp
sk = abcdefg
local_ip = 127.0.0.1
local_port = 22
use_encryption = false
use_compression = false
[p2p_tcp_visitor]
role = visitor
type = xtcp
server_name = p2p_tcp
sk = abcdefg
bind_addr = 127.0.0.1
bind_port = 9001
use_encryption = false
use_compression = false
[tcpmuxhttpconnect]
type = tcpmux
multiplexer = httpconnect
local_ip = 127.0.0.1
local_port = 10701
custom_domains = tunnel1

View File

@@ -1,14 +1,2 @@
# [common] is integral section
[common]
bind_addr = 0.0.0.0
bind_port = 7000
# console or real logFile path like ./frps.log
log_file = console
# debug, info, warn, error
log_level = debug
# test1 is the proxy name, client will use this name and passwd to connect to server
[test1]
passwd = 123
bind_addr = 0.0.0.0
listen_port = 6000

152
conf/frps_full.ini Normal file
View File

@@ -0,0 +1,152 @@
# [common] is integral section
[common]
# A literal address or host name for IPv6 must be enclosed
# in square brackets, as in "[::1]:80", "[ipv6-host]:http" or "[ipv6-host%zone]:80"
# For single "bind_addr" field, no need square brackets, like "bind_addr = ::".
bind_addr = 0.0.0.0
bind_port = 7000
# udp port to help make udp hole to penetrate nat
bind_udp_port = 7001
# udp port used for kcp protocol, it can be same with 'bind_port'
# if not set, kcp is disabled in frps
kcp_bind_port = 7000
# specify which address proxy will listen for, default value is same with bind_addr
# 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
# response header timeout(seconds) for vhost http server, default is 60s
# vhost_http_timeout = 60
# tcpmux_httpconnect_port specifies the port that the server listens for TCP
# HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP
# requests on one single port. If it's not - it will listen on this value for
# HTTP CONNECT requests. By default, this value is 0.
# tcpmux_httpconnect_port = 1337
# set dashboard_addr and dashboard_port to view dashboard of frps
# dashboard_addr's default value is same with bind_addr
# dashboard is available only if dashboard_port is set
dashboard_addr = 0.0.0.0
dashboard_port = 7500
# dashboard user and passwd for basic auth protect
dashboard_user = admin
dashboard_pwd = admin
# enable_prometheus will export prometheus metrics on {dashboard_addr}:{dashboard_port} in /metrics api.
enable_prometheus = true
# dashboard assets directory(only for debug mode)
# assets_dir = ./static
# console or real logFile path like ./frps.log
log_file = ./frps.log
# trace, debug, info, warn, error
log_level = info
log_max_days = 3
# disable log colors when log_file is console, default is false
disable_log_color = false
# DetailedErrorsToClient defines whether to send the specific error (with debug info) to frpc. By default, this value is true.
detailed_errors_to_client = true
# authentication_method specifies what authentication method to use authenticate frpc with frps.
# If "token" is specified - token will be read into login message.
# If "oidc" is specified - OIDC (Open ID Connect) token will be issued using OIDC settings. By default, this value is "token".
authentication_method = token
# authenticate_heartbeats specifies whether to include authentication token in heartbeats sent to frps. By default, this value is false.
authenticate_heartbeats = false
# AuthenticateNewWorkConns specifies whether to include authentication token in new work connections sent to frps. By default, this value is false.
authenticate_new_work_conns = false
# auth token
token = 12345678
# oidc_issuer specifies the issuer to verify OIDC tokens with.
# By default, this value is "".
oidc_issuer =
# oidc_audience specifies the audience OIDC tokens should contain when validated.
# By default, this value is "".
oidc_audience =
# oidc_skip_expiry_check specifies whether to skip checking if the OIDC token is expired.
# By default, this value is false.
oidc_skip_expiry_check = false
# oidc_skip_issuer_check specifies whether to skip checking if the OIDC token's issuer claim matches the issuer specified in OidcIssuer.
# By default, this value is false.
oidc_skip_issuer_check = false
# heartbeat configure, it's not recommended to modify the default value
# the default value of heartbeat_timeout is 90. Set negative value to disable it.
# heartbeat_timeout = 90
# user_conn_timeout configure, it's not recommended to modify the default value
# the default value of user_conn_timeout is 10
# user_conn_timeout = 10
# only allow frpc to bind ports you list, if you set nothing, there won't be any limit
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
# tls_only specifies whether to only accept TLS-encrypted connections. By default, the value is false.
tls_only = false
# tls_cert_file = server.crt
# tls_key_file = server.key
# tls_trusted_ca_file = ca.crt
# if subdomain_host is not empty, you can set subdomain when type is http or https in frpc's configure file
# when subdomain is test, the host used by routing is test.frps.com
subdomain_host = frps.com
# if tcp stream multiplexing is used, default is true
# tcp_mux = true
# specify keep alive interval for tcp mux.
# only valid if tcp_mux is true.
# tcp_mux_keepalive_interval = 60
# tcp_keepalive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
# If negative, keep-alive probes are disabled.
# tcp_keepalive = 7200
# custom 404 page for HTTP requests
# custom_404_page = /path/to/404.html
# specify udp packet size, unit is byte. If not set, the default value is 1500.
# This parameter should be same between client and server.
# It affects the udp and sudp proxy.
udp_packet_size = 1500
# Enable golang pprof handlers in dashboard listener.
# Dashboard port must be set first
pprof_enable = false
[plugin.user-manager]
addr = 127.0.0.1:9000
path = /handler
ops = Login
[plugin.port-manager]
addr = 127.0.0.1:9001
path = /handler
ops = NewProxy

15
conf/systemd/frpc.service Normal file
View File

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

View File

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

14
conf/systemd/frps.service Normal file
View File

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

View File

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

BIN
doc/pic/dashboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
doc/pic/donate-alipay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
doc/pic/sponsor_doppler.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
doc/pic/sponsor_workos.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
doc/pic/zsxq.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -1,68 +0,0 @@
# Quick Start
frp is easier to use compared with other similar projects.
We will use a simple demo to demonstrate 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).
### 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`.
### 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).
### 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
# test is the custom name of proxy and there can be many proxies with unique name in one configure file
[test]
passwd = 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
# test is proxy name same with configure in frps.ini
[test]
passwd = 123
# local port which need to be transferred
local_port = 22
```

View File

@@ -1,66 +0,0 @@
# frp 使用文档
frp 相比于其他项目而言非常易于部署和使用这里我们用一个简单的示例演示如何通过一台拥有公网IP地址的服务器B访问处于内网环境中的服务器A的ssh端口服务器B的IP地址为 x.x.x.x测试时替换为真实的IP地址
### 下载源码
推荐直接使用 `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` 拷贝到相应目录下。
### 编译
进入下载后的源码根目录,执行 `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上存在的真实用户
### 配置文件
#### frps.ini
```ini
[common]
bind_addr = 0.0.0.0
# 用于接收 frpc 连接的端口
bind_port = 7000
log_file = ./frps.log
log_level = info
# test 为代理的自定义名称可以有多个不能重复和frpc中名称对应
[test]
passwd = 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
# test需要和 frps.ini 中配置一致
[test]
passwd = 123
# 需要转发的本地端口
local_port = 22
```

262
doc/server_plugin.md Normal file
View File

@@ -0,0 +1,262 @@
### Server Plugin
frp server plugin is aimed to extend frp's ability without modifying the Golang code.
An external server should run in a different process receiving RPC calls from frps.
Before frps is doing some operations, it will send RPC requests to notify the external RPC server and act according to its response.
### RPC request
RPC requests are based on JSON over HTTP.
When a server plugin accepts an operation request, it can respond with three different responses:
* Reject operation and return a reason.
* Allow operation and keep original content.
* Allow operation and return modified content.
### Interface
HTTP path can be configured for each manage plugin in frps. We'll assume for this example that it's `/handler`.
A request to the RPC server will look like:
```
POST /handler?version=0.1.0&op=Login
{
"version": "0.1.0",
"op": "Login",
"content": {
... // Operation info
}
}
Request Header:
X-Frp-Reqid: for tracing
```
The response can look like any of the following:
* Non-200 HTTP response status code (this will automatically tell frps that the request should fail)
* Reject operation:
```
{
"reject": true,
"reject_reason": "invalid user"
}
```
* Allow operation and keep original content:
```
{
"reject": false,
"unchange": true
}
```
* Allow operation and modify content
```
{
"unchange": "false",
"content": {
... // Replaced content
}
}
```
### Operation
Currently `Login`, `NewProxy`, `CloseProxy`, `Ping`, `NewWorkConn` and `NewUserConn` operations are supported.
#### Login
Client login operation
```
{
"content": {
"version": <string>,
"hostname": <string>,
"os": <string>,
"arch": <string>,
"user": <string>,
"timestamp": <int64>,
"privilege_key": <string>,
"run_id": <string>,
"pool_count": <int>,
"metas": map<string>string,
"client_address": <string>
}
}
```
#### NewProxy
Create new proxy
```
{
"content": {
"user": {
"user": <string>,
"metas": map<string>string
"run_id": <string>
},
"proxy_name": <string>,
"proxy_type": <string>,
"use_encryption": <bool>,
"use_compression": <bool>,
"group": <string>,
"group_key": <string>,
// tcp and udp only
"remote_port": <int>,
// http and https only
"custom_domains": []<string>,
"subdomain": <string>,
"locations": <string>,
"http_user": <string>,
"http_pwd": <string>,
"host_header_rewrite": <string>,
"headers": map<string>string,
// stcp only
"sk": <string>,
// tcpmux only
"multiplexer": <string>
"metas": map<string>string
}
}
```
#### CloseProxy
A previously created proxy is closed.
Please note that one request will be sent for every proxy that is closed, do **NOT** use this
if you have too many proxies bound to a single client, as this may exhaust the server's resources.
```
{
"content": {
"user": {
"user": <string>,
"metas": map<string>string
"run_id": <string>
},
"proxy_name": <string>
}
}
```
#### Ping
Heartbeat from frpc
```
{
"content": {
"user": {
"user": <string>,
"metas": map<string>string
"run_id": <string>
},
"timestamp": <int64>,
"privilege_key": <string>
}
}
```
#### NewWorkConn
New work connection received from frpc (RPC sent after `run_id` is matched with an existing frp connection)
```
{
"content": {
"user": {
"user": <string>,
"metas": map<string>string
"run_id": <string>
},
"run_id": <string>
"timestamp": <int64>,
"privilege_key": <string>
}
}
```
#### NewUserConn
New user connection received from proxy (support `tcp`, `stcp`, `https` and `tcpmux`) .
```
{
"content": {
"user": {
"user": <string>,
"metas": map<string>string
"run_id": <string>
},
"proxy_name": <string>,
"proxy_type": <string>,
"remote_addr": <string>
}
}
```
### Server Plugin Configuration
```ini
# frps.ini
[common]
bind_port = 7000
[plugin.user-manager]
addr = 127.0.0.1:9000
path = /handler
ops = Login
[plugin.port-manager]
addr = 127.0.0.1:9001
path = /handler
ops = NewProxy
```
- addr: the address where the external RPC service listens. Defaults to http. For https, specify the schema: `addr = https://127.0.0.1:9001`.
- path: http request url path for the POST request.
- ops: operations plugin needs to handle (e.g. "Login", "NewProxy", ...).
- tls_verify: When the schema is https, we verify by default. Set this value to false if you want to skip verification.
### Metadata
Metadata will be sent to the server plugin in each RPC request.
There are 2 types of metadata entries - 1 under `[common]` and the other under each proxy configuration.
Metadata entries under `[common]` will be sent in `Login` under the key `metas`, and in any other RPC request under `user.metas`.
Metadata entries under each proxy configuration will be sent in `NewProxy` op only, under `metas`.
Metadata entries start with `meta_`. This is an example of metadata entries in `[common]` and under the proxy named `[ssh]`:
```
# frpc.ini
[common]
server_addr = 127.0.0.1
server_port = 7000
user = fake
meta_token = fake
meta_version = 1.0.0
[ssh]
type = tcp
local_port = 22
remote_port = 6000
meta_id = 123
```

View File

@@ -0,0 +1,14 @@
FROM alpine:3 AS temp
COPY bin/frpc /tmp
RUN chmod -R 777 /tmp/frpc
FROM alpine:3
WORKDIR /app
COPY --from=temp /tmp/frpc /usr/bin
ENTRYPOINT ["/usr/bin/frpc"]

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