├── FRPsource
├── assets
│ ├── assets.go
│ ├── frpc
│ │ ├── static
│ │ │ ├── 6f0a76321d30f3c8120915e57f7bd77e.ttf
│ │ │ ├── favicon.ico
│ │ │ ├── index.html
│ │ │ ├── manifest.js
│ │ │ └── vendor.js
│ │ └── statik
│ │ │ └── statik.go
│ └── frps
│ │ ├── static
│ │ ├── 6f0a76321d30f3c8120915e57f7bd77e.ttf
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── manifest.js
│ │ └── vendor.js
│ │ └── statik
│ │ └── statik.go
├── client
│ ├── admin.go
│ ├── admin_api.go
│ ├── control.go
│ ├── event
│ │ └── event.go
│ ├── health
│ │ └── health.go
│ ├── proxy
│ │ ├── proxy.go
│ │ ├── proxy_manager.go
│ │ └── proxy_wrapper.go
│ ├── service.go
│ ├── visitor.go
│ └── visitor_manager.go
├── cmd
│ ├── frpc
│ │ ├── builder.exe
│ │ ├── main.go
│ │ ├── sub
│ │ │ └── root.go
│ │ └── upx.exe
│ └── frps
│ │ ├── main.go
│ │ └── root.go
├── go.mod
├── go.sum
├── pkg
│ ├── auth
│ │ ├── auth.go
│ │ ├── oidc.go
│ │ └── token.go
│ ├── config
│ │ ├── client_common.go
│ │ ├── proxy.go
│ │ ├── server_common.go
│ │ ├── types.go
│ │ ├── types_test.go
│ │ ├── value.go
│ │ └── visitor.go
│ ├── consts
│ │ └── consts.go
│ ├── errors
│ │ └── errors.go
│ ├── metrics
│ │ ├── aggregate
│ │ │ └── server.go
│ │ ├── mem
│ │ │ ├── server.go
│ │ │ └── types.go
│ │ ├── metrics.go
│ │ └── prometheus
│ │ │ └── server.go
│ ├── msg
│ │ ├── ctl.go
│ │ └── msg.go
│ ├── nathole
│ │ └── nathole.go
│ ├── plugin
│ │ ├── client
│ │ │ ├── http2https.go
│ │ │ ├── http_proxy.go
│ │ │ ├── https2http.go
│ │ │ ├── plugin.go
│ │ │ ├── socks5.go
│ │ │ ├── static_file.go
│ │ │ └── unix_domain_socket.go
│ │ └── server
│ │ │ ├── http.go
│ │ │ ├── manager.go
│ │ │ ├── plugin.go
│ │ │ ├── tracer.go
│ │ │ └── types.go
│ ├── proto
│ │ └── udp
│ │ │ ├── udp.go
│ │ │ └── udp_test.go
│ ├── transport
│ │ └── tls.go
│ └── util
│ │ ├── limit
│ │ ├── reader.go
│ │ └── writer.go
│ │ ├── log
│ │ └── log.go
│ │ ├── metric
│ │ ├── counter.go
│ │ ├── counter_test.go
│ │ ├── date_counter.go
│ │ ├── date_counter_test.go
│ │ └── metrics.go
│ │ ├── net
│ │ ├── conn.go
│ │ ├── http.go
│ │ ├── kcp.go
│ │ ├── listener.go
│ │ ├── tls.go
│ │ ├── udp.go
│ │ └── websocket.go
│ │ ├── tcpmux
│ │ └── httpconnect.go
│ │ ├── util
│ │ ├── http.go
│ │ ├── util.go
│ │ └── util_test.go
│ │ ├── version
│ │ ├── version.go
│ │ └── version_test.go
│ │ ├── vhost
│ │ ├── http.go
│ │ ├── https.go
│ │ ├── resource.go
│ │ ├── reverseproxy.go
│ │ ├── router.go
│ │ └── vhost.go
│ │ └── xlog
│ │ ├── ctx.go
│ │ └── xlog.go
└── server
│ ├── control.go
│ ├── controller
│ └── resource.go
│ ├── dashboard.go
│ ├── dashboard_api.go
│ ├── group
│ ├── group.go
│ ├── http.go
│ ├── tcp.go
│ └── tcpmux.go
│ ├── metrics
│ └── metrics.go
│ ├── ports
│ └── ports.go
│ ├── proxy
│ ├── http.go
│ ├── https.go
│ ├── proxy.go
│ ├── stcp.go
│ ├── sudp.go
│ ├── tcp.go
│ ├── tcpmux.go
│ ├── udp.go
│ └── xtcp.go
│ ├── service.go
│ └── visitor
│ └── visitor.go
├── README.md
└── buildersource
├── builder.sln
└── builder
├── builder.aps
├── builder.cpp
├── builder.h
├── builder.rc
├── builder.vcxproj
├── builder.vcxproj.filters
├── builder.vcxproj.user
├── builderDlg.cpp
├── builderDlg.h
├── framework.h
├── pch.cpp
├── pch.h
├── res
├── builder.ico
└── builder.rc2
├── resource.h
└── targetver.h
/FRPsource/assets/assets.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package assets
16 |
17 | //go:generate statik -src=./frps/static -dest=./frps
18 | //go:generate statik -src=./frpc/static -dest=./frpc
19 | //go:generate go fmt ./frps/statik/statik.go
20 | //go:generate go fmt ./frpc/statik/statik.go
21 |
22 | import (
23 | "io/ioutil"
24 | "net/http"
25 | "os"
26 | "path"
27 |
28 | "github.com/rakyll/statik/fs"
29 | )
30 |
31 | var (
32 | // store static files in memory by statik
33 | FileSystem http.FileSystem
34 |
35 | // if prefix is not empty, we get file content from disk
36 | prefixPath string
37 | )
38 |
39 | // if path is empty, load assets in memory
40 | // or set FileSystem using disk files
41 | func Load(path string) (err error) {
42 | prefixPath = path
43 | if prefixPath != "" {
44 | FileSystem = http.Dir(prefixPath)
45 | return nil
46 | } else {
47 | FileSystem, err = fs.New()
48 | }
49 | return err
50 | }
51 |
52 | func ReadFile(file string) (content string, err error) {
53 | if prefixPath == "" {
54 | file, err := FileSystem.Open(path.Join("/", file))
55 | if err != nil {
56 | return content, err
57 | }
58 | defer file.Close()
59 | buf, err := ioutil.ReadAll(file)
60 | if err != nil {
61 | return content, err
62 | }
63 | content = string(buf)
64 | } else {
65 | file, err := os.Open(path.Join(prefixPath, file))
66 | if err != nil {
67 | return content, err
68 | }
69 | defer file.Close()
70 | buf, err := ioutil.ReadAll(file)
71 | if err != nil {
72 | return content, err
73 | }
74 | content = string(buf)
75 | }
76 | return content, err
77 | }
78 |
--------------------------------------------------------------------------------
/FRPsource/assets/frpc/static/6f0a76321d30f3c8120915e57f7bd77e.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evilashz/frpBuilder/a1b0c0397739efd707ea784b23a6018f7bb38ac3/FRPsource/assets/frpc/static/6f0a76321d30f3c8120915e57f7bd77e.ttf
--------------------------------------------------------------------------------
/FRPsource/assets/frpc/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evilashz/frpBuilder/a1b0c0397739efd707ea784b23a6018f7bb38ac3/FRPsource/assets/frpc/static/favicon.ico
--------------------------------------------------------------------------------
/FRPsource/assets/frpc/static/index.html:
--------------------------------------------------------------------------------
1 |
frp client admin UI
--------------------------------------------------------------------------------
/FRPsource/assets/frpc/static/manifest.js:
--------------------------------------------------------------------------------
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 frps dashboard
--------------------------------------------------------------------------------
/FRPsource/assets/frps/static/manifest.js:
--------------------------------------------------------------------------------
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= monitor.maxFailedTimes && monitor.statusFailedFn != nil {
119 | xl.Warn("health check status change to failed")
120 | monitor.statusOK = false
121 | monitor.statusFailedFn()
122 | }
123 | }
124 |
125 | time.Sleep(monitor.interval)
126 | }
127 | }
128 |
129 | func (monitor *Monitor) doCheck(ctx context.Context) error {
130 | switch monitor.checkType {
131 | case "tcp":
132 | return monitor.doTCPCheck(ctx)
133 | case "http":
134 | return monitor.doHTTPCheck(ctx)
135 | default:
136 | return ErrHealthCheckType
137 | }
138 | }
139 |
140 | func (monitor *Monitor) doTCPCheck(ctx context.Context) error {
141 | // if tcp address is not specified, always return nil
142 | if monitor.addr == "" {
143 | return nil
144 | }
145 |
146 | var d net.Dialer
147 | conn, err := d.DialContext(ctx, "tcp", monitor.addr)
148 | if err != nil {
149 | return err
150 | }
151 | conn.Close()
152 | return nil
153 | }
154 |
155 | func (monitor *Monitor) doHTTPCheck(ctx context.Context) error {
156 | req, err := http.NewRequest("GET", monitor.url, nil)
157 | if err != nil {
158 | return err
159 | }
160 | resp, err := http.DefaultClient.Do(req)
161 | if err != nil {
162 | return err
163 | }
164 | defer resp.Body.Close()
165 | io.Copy(ioutil.Discard, resp.Body)
166 |
167 | if resp.StatusCode/100 != 2 {
168 | return fmt.Errorf("do http health check, StatusCode is [%d] not 2xx", resp.StatusCode)
169 | }
170 | return nil
171 | }
172 |
--------------------------------------------------------------------------------
/FRPsource/client/proxy/proxy_manager.go:
--------------------------------------------------------------------------------
1 | package proxy
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net"
7 | "sync"
8 |
9 | "github.com/fatedier/frp/client/event"
10 | "github.com/fatedier/frp/pkg/config"
11 | "github.com/fatedier/frp/pkg/msg"
12 | "github.com/fatedier/frp/pkg/util/xlog"
13 |
14 | "github.com/fatedier/golib/errors"
15 | )
16 |
17 | type Manager struct {
18 | sendCh chan (msg.Message)
19 | proxies map[string]*Wrapper
20 |
21 | closed bool
22 | mu sync.RWMutex
23 |
24 | clientCfg config.ClientCommonConf
25 |
26 | // The UDP port that the server is listening on
27 | serverUDPPort int
28 |
29 | ctx context.Context
30 | }
31 |
32 | func NewManager(ctx context.Context, msgSendCh chan (msg.Message), clientCfg config.ClientCommonConf, serverUDPPort int) *Manager {
33 | return &Manager{
34 | sendCh: msgSendCh,
35 | proxies: make(map[string]*Wrapper),
36 | closed: false,
37 | clientCfg: clientCfg,
38 | serverUDPPort: serverUDPPort,
39 | ctx: ctx,
40 | }
41 | }
42 |
43 | func (pm *Manager) StartProxy(name string, remoteAddr string, serverRespErr string) error {
44 | pm.mu.RLock()
45 | pxy, ok := pm.proxies[name]
46 | pm.mu.RUnlock()
47 | if !ok {
48 | return fmt.Errorf("proxy [%s] not found", name)
49 | }
50 |
51 | err := pxy.SetRunningStatus(remoteAddr, serverRespErr)
52 | if err != nil {
53 | return err
54 | }
55 | return nil
56 | }
57 |
58 | func (pm *Manager) Close() {
59 | pm.mu.Lock()
60 | defer pm.mu.Unlock()
61 | for _, pxy := range pm.proxies {
62 | pxy.Stop()
63 | }
64 | pm.proxies = make(map[string]*Wrapper)
65 | }
66 |
67 | func (pm *Manager) HandleWorkConn(name string, workConn net.Conn, m *msg.StartWorkConn) {
68 | pm.mu.RLock()
69 | pw, ok := pm.proxies[name]
70 | pm.mu.RUnlock()
71 | if ok {
72 | pw.InWorkConn(workConn, m)
73 | } else {
74 | workConn.Close()
75 | }
76 | }
77 |
78 | func (pm *Manager) HandleEvent(evType event.Type, payload interface{}) error {
79 | var m msg.Message
80 | switch e := payload.(type) {
81 | case *event.StartProxyPayload:
82 | m = e.NewProxyMsg
83 | case *event.CloseProxyPayload:
84 | m = e.CloseProxyMsg
85 | default:
86 | return event.ErrPayloadType
87 | }
88 |
89 | err := errors.PanicToError(func() {
90 | pm.sendCh <- m
91 | })
92 | return err
93 | }
94 |
95 | func (pm *Manager) GetAllProxyStatus() []*WorkingStatus {
96 | ps := make([]*WorkingStatus, 0)
97 | pm.mu.RLock()
98 | defer pm.mu.RUnlock()
99 | for _, pxy := range pm.proxies {
100 | ps = append(ps, pxy.GetStatus())
101 | }
102 | return ps
103 | }
104 |
105 | func (pm *Manager) Reload(pxyCfgs map[string]config.ProxyConf) {
106 | xl := xlog.FromContextSafe(pm.ctx)
107 | pm.mu.Lock()
108 | defer pm.mu.Unlock()
109 |
110 | delPxyNames := make([]string, 0)
111 | for name, pxy := range pm.proxies {
112 | del := false
113 | cfg, ok := pxyCfgs[name]
114 | if !ok {
115 | del = true
116 | } else {
117 | if !pxy.Cfg.Compare(cfg) {
118 | del = true
119 | }
120 | }
121 |
122 | if del {
123 | delPxyNames = append(delPxyNames, name)
124 | delete(pm.proxies, name)
125 |
126 | pxy.Stop()
127 | }
128 | }
129 | if len(delPxyNames) > 0 {
130 | xl.Info("proxy removed: %v", delPxyNames)
131 | }
132 |
133 | addPxyNames := make([]string, 0)
134 | for name, cfg := range pxyCfgs {
135 | if _, ok := pm.proxies[name]; !ok {
136 | pxy := NewWrapper(pm.ctx, cfg, pm.clientCfg, pm.HandleEvent, pm.serverUDPPort)
137 | pm.proxies[name] = pxy
138 | addPxyNames = append(addPxyNames, name)
139 |
140 | pxy.Start()
141 | }
142 | }
143 | if len(addPxyNames) > 0 {
144 | xl.Info("proxy added: %v", addPxyNames)
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/FRPsource/client/visitor_manager.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package client
16 |
17 | import (
18 | "context"
19 | "sync"
20 | "time"
21 |
22 | "github.com/fatedier/frp/pkg/config"
23 | "github.com/fatedier/frp/pkg/util/xlog"
24 | )
25 |
26 | type VisitorManager struct {
27 | ctl *Control
28 |
29 | cfgs map[string]config.VisitorConf
30 | visitors map[string]Visitor
31 |
32 | checkInterval time.Duration
33 |
34 | mu sync.Mutex
35 | ctx context.Context
36 |
37 | stopCh chan struct{}
38 | }
39 |
40 | func NewVisitorManager(ctx context.Context, ctl *Control) *VisitorManager {
41 | return &VisitorManager{
42 | ctl: ctl,
43 | cfgs: make(map[string]config.VisitorConf),
44 | visitors: make(map[string]Visitor),
45 | checkInterval: 10 * time.Second,
46 | ctx: ctx,
47 | stopCh: make(chan struct{}),
48 | }
49 | }
50 |
51 | func (vm *VisitorManager) Run() {
52 | xl := xlog.FromContextSafe(vm.ctx)
53 |
54 | ticker := time.NewTicker(vm.checkInterval)
55 | defer ticker.Stop()
56 |
57 | for {
58 | select {
59 | case <-vm.stopCh:
60 | xl.Info("gracefully shutdown visitor manager")
61 | return
62 | case <-ticker.C:
63 | vm.mu.Lock()
64 | for _, cfg := range vm.cfgs {
65 | name := cfg.GetBaseInfo().ProxyName
66 | if _, exist := vm.visitors[name]; !exist {
67 | xl.Info("try to start visitor [%s]", name)
68 | vm.startVisitor(cfg)
69 | }
70 | }
71 | vm.mu.Unlock()
72 | }
73 | }
74 | }
75 |
76 | // Hold lock before calling this function.
77 | func (vm *VisitorManager) startVisitor(cfg config.VisitorConf) (err error) {
78 | xl := xlog.FromContextSafe(vm.ctx)
79 | name := cfg.GetBaseInfo().ProxyName
80 | visitor := NewVisitor(vm.ctx, vm.ctl, cfg)
81 | err = visitor.Run()
82 | if err != nil {
83 | xl.Warn("start error: %v", err)
84 | } else {
85 | vm.visitors[name] = visitor
86 | xl.Info("start visitor success")
87 | }
88 | return
89 | }
90 |
91 | func (vm *VisitorManager) Reload(cfgs map[string]config.VisitorConf) {
92 | xl := xlog.FromContextSafe(vm.ctx)
93 | vm.mu.Lock()
94 | defer vm.mu.Unlock()
95 |
96 | delNames := make([]string, 0)
97 | for name, oldCfg := range vm.cfgs {
98 | del := false
99 | cfg, ok := cfgs[name]
100 | if !ok {
101 | del = true
102 | } else {
103 | if !oldCfg.Compare(cfg) {
104 | del = true
105 | }
106 | }
107 |
108 | if del {
109 | delNames = append(delNames, name)
110 | delete(vm.cfgs, name)
111 | if visitor, ok := vm.visitors[name]; ok {
112 | visitor.Close()
113 | }
114 | delete(vm.visitors, name)
115 | }
116 | }
117 | if len(delNames) > 0 {
118 | xl.Info("visitor removed: %v", delNames)
119 | }
120 |
121 | addNames := make([]string, 0)
122 | for name, cfg := range cfgs {
123 | if _, ok := vm.cfgs[name]; !ok {
124 | vm.cfgs[name] = cfg
125 | addNames = append(addNames, name)
126 | vm.startVisitor(cfg)
127 | }
128 | }
129 | if len(addNames) > 0 {
130 | xl.Info("visitor added: %v", addNames)
131 | }
132 | return
133 | }
134 |
135 | func (vm *VisitorManager) Close() {
136 | vm.mu.Lock()
137 | defer vm.mu.Unlock()
138 | for _, v := range vm.visitors {
139 | v.Close()
140 | }
141 | select {
142 | case <-vm.stopCh:
143 | default:
144 | close(vm.stopCh)
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/FRPsource/cmd/frpc/builder.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evilashz/frpBuilder/a1b0c0397739efd707ea784b23a6018f7bb38ac3/FRPsource/cmd/frpc/builder.exe
--------------------------------------------------------------------------------
/FRPsource/cmd/frpc/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "math/rand"
19 | "time"
20 |
21 | _ "github.com/fatedier/frp/assets/frpc/statik"
22 | "github.com/fatedier/frp/cmd/frpc/sub"
23 |
24 | "github.com/fatedier/golib/crypto"
25 | )
26 |
27 | func main() {
28 | crypto.DefaultSalt = "frp"
29 | rand.Seed(time.Now().UnixNano())
30 |
31 | sub.Execute()
32 | }
33 |
--------------------------------------------------------------------------------
/FRPsource/cmd/frpc/upx.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evilashz/frpBuilder/a1b0c0397739efd707ea784b23a6018f7bb38ac3/FRPsource/cmd/frpc/upx.exe
--------------------------------------------------------------------------------
/FRPsource/cmd/frps/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "math/rand"
19 | "time"
20 |
21 | "github.com/fatedier/golib/crypto"
22 |
23 | _ "github.com/fatedier/frp/assets/frps/statik"
24 | _ "github.com/fatedier/frp/pkg/metrics"
25 | )
26 |
27 | func main() {
28 | crypto.DefaultSalt = "frp"
29 | rand.Seed(time.Now().UnixNano())
30 |
31 | Execute()
32 | }
33 |
--------------------------------------------------------------------------------
/FRPsource/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/fatedier/frp
2 |
3 | go 1.12
4 |
5 | require (
6 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
7 | github.com/coreos/go-oidc v2.2.1+incompatible
8 | github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb
9 | github.com/fatedier/golib v0.1.1-0.20200901083111-1f870741e185
10 | github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible
11 | github.com/google/uuid v1.1.1
12 | github.com/gorilla/mux v1.7.3
13 | github.com/gorilla/websocket v1.4.0
14 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d
15 | github.com/inconshreveable/mousetrap v1.0.0 // indirect
16 | github.com/klauspost/cpuid v1.2.0 // indirect
17 | github.com/klauspost/reedsolomon v1.9.1 // indirect
18 | github.com/mattn/go-runewidth v0.0.4 // indirect
19 | github.com/onsi/ginkgo v1.12.3
20 | github.com/onsi/gomega v1.10.1
21 | github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc
22 | github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
23 | github.com/prometheus/client_golang v1.4.1
24 | github.com/rakyll/statik v0.1.1
25 | github.com/rodaine/table v1.0.0
26 | github.com/spf13/cobra v0.0.3
27 | github.com/stretchr/testify v1.4.0
28 | github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047 // indirect
29 | github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554 // indirect
30 | github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 // indirect
31 | github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec
32 | github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae // indirect
33 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7
34 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
35 | golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 // indirect
36 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0
37 | gopkg.in/square/go-jose.v2 v2.4.1 // indirect
38 | k8s.io/apimachinery v0.18.3
39 | )
40 |
--------------------------------------------------------------------------------
/FRPsource/pkg/auth/auth.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 guylewin, guy@lewin.co.il
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package auth
16 |
17 | import (
18 | "fmt"
19 |
20 | "github.com/fatedier/frp/pkg/consts"
21 | "github.com/fatedier/frp/pkg/msg"
22 |
23 | "github.com/vaughan0/go-ini"
24 | )
25 |
26 | type baseConfig struct {
27 | // AuthenticationMethod specifies what authentication method to use to
28 | // authenticate frpc with frps. If "token" is specified - token will be
29 | // read into login message. If "oidc" is specified - OIDC (Open ID Connect)
30 | // token will be issued using OIDC settings. By default, this value is "token".
31 | AuthenticationMethod string `json:"authentication_method"`
32 | // AuthenticateHeartBeats specifies whether to include authentication token in
33 | // heartbeats sent to frps. By default, this value is false.
34 | AuthenticateHeartBeats bool `json:"authenticate_heartbeats"`
35 | // AuthenticateNewWorkConns specifies whether to include authentication token in
36 | // new work connections sent to frps. By default, this value is false.
37 | AuthenticateNewWorkConns bool `json:"authenticate_new_work_conns"`
38 | }
39 |
40 | func getDefaultBaseConf() baseConfig {
41 | return baseConfig{
42 | AuthenticationMethod: "token",
43 | AuthenticateHeartBeats: false,
44 | AuthenticateNewWorkConns: false,
45 | }
46 | }
47 |
48 | func unmarshalBaseConfFromIni(conf ini.File) baseConfig {
49 | var (
50 | tmpStr string
51 | ok bool
52 | )
53 |
54 | cfg := getDefaultBaseConf()
55 |
56 | if tmpStr, ok = conf.Get("common", "authentication_method"); ok {
57 | cfg.AuthenticationMethod = tmpStr
58 | }
59 |
60 | if tmpStr, ok = conf.Get("common", "authenticate_heartbeats"); ok && tmpStr == "true" {
61 | cfg.AuthenticateHeartBeats = true
62 | } else {
63 | cfg.AuthenticateHeartBeats = false
64 | }
65 |
66 | if tmpStr, ok = conf.Get("common", "authenticate_new_work_conns"); ok && tmpStr == "true" {
67 | cfg.AuthenticateNewWorkConns = true
68 | } else {
69 | cfg.AuthenticateNewWorkConns = false
70 | }
71 |
72 | return cfg
73 | }
74 |
75 | type ClientConfig struct {
76 | baseConfig
77 | oidcClientConfig
78 | tokenConfig
79 | }
80 |
81 | func GetDefaultClientConf() ClientConfig {
82 | return ClientConfig{
83 | baseConfig: getDefaultBaseConf(),
84 | oidcClientConfig: getDefaultOidcClientConf(),
85 | tokenConfig: getDefaultTokenConf(),
86 | }
87 | }
88 |
89 | func UnmarshalClientConfFromIni(conf ini.File) (cfg ClientConfig) {
90 | cfg.baseConfig = unmarshalBaseConfFromIni(conf)
91 | cfg.oidcClientConfig = unmarshalOidcClientConfFromIni(conf)
92 | cfg.tokenConfig = unmarshalTokenConfFromIni(conf)
93 | return cfg
94 | }
95 |
96 | type ServerConfig struct {
97 | baseConfig
98 | oidcServerConfig
99 | tokenConfig
100 | }
101 |
102 | func GetDefaultServerConf() ServerConfig {
103 | return ServerConfig{
104 | baseConfig: getDefaultBaseConf(),
105 | oidcServerConfig: getDefaultOidcServerConf(),
106 | tokenConfig: getDefaultTokenConf(),
107 | }
108 | }
109 |
110 | func UnmarshalServerConfFromIni(conf ini.File) (cfg ServerConfig) {
111 | cfg.baseConfig = unmarshalBaseConfFromIni(conf)
112 | cfg.oidcServerConfig = unmarshalOidcServerConfFromIni(conf)
113 | cfg.tokenConfig = unmarshalTokenConfFromIni(conf)
114 | return cfg
115 | }
116 |
117 | type Setter interface {
118 | SetLogin(*msg.Login) error
119 | SetPing(*msg.Ping) error
120 | SetNewWorkConn(*msg.NewWorkConn) error
121 | }
122 |
123 | func NewAuthSetter(cfg ClientConfig) (authProvider Setter) {
124 | switch cfg.AuthenticationMethod {
125 | case consts.TokenAuthMethod:
126 | authProvider = NewTokenAuth(cfg.baseConfig, cfg.tokenConfig)
127 | case consts.OidcAuthMethod:
128 | authProvider = NewOidcAuthSetter(cfg.baseConfig, cfg.oidcClientConfig)
129 | default:
130 | panic(fmt.Sprintf("wrong authentication method: '%s'", cfg.AuthenticationMethod))
131 | }
132 |
133 | return authProvider
134 | }
135 |
136 | type Verifier interface {
137 | VerifyLogin(*msg.Login) error
138 | VerifyPing(*msg.Ping) error
139 | VerifyNewWorkConn(*msg.NewWorkConn) error
140 | }
141 |
142 | func NewAuthVerifier(cfg ServerConfig) (authVerifier Verifier) {
143 | switch cfg.AuthenticationMethod {
144 | case consts.TokenAuthMethod:
145 | authVerifier = NewTokenAuth(cfg.baseConfig, cfg.tokenConfig)
146 | case consts.OidcAuthMethod:
147 | authVerifier = NewOidcAuthVerifier(cfg.baseConfig, cfg.oidcServerConfig)
148 | }
149 |
150 | return authVerifier
151 | }
152 |
--------------------------------------------------------------------------------
/FRPsource/pkg/auth/token.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 guylewin, guy@lewin.co.il
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package auth
16 |
17 | import (
18 | "fmt"
19 | "time"
20 |
21 | "github.com/fatedier/frp/pkg/msg"
22 | "github.com/fatedier/frp/pkg/util/util"
23 |
24 | "github.com/vaughan0/go-ini"
25 | )
26 |
27 | type tokenConfig struct {
28 | // Token specifies the authorization token used to create keys to be sent
29 | // to the server. The server must have a matching token for authorization
30 | // to succeed. By default, this value is "".
31 | Token string `json:"token"`
32 | }
33 |
34 | func getDefaultTokenConf() tokenConfig {
35 | return tokenConfig{
36 | Token: "",
37 | }
38 | }
39 |
40 | func unmarshalTokenConfFromIni(conf ini.File) tokenConfig {
41 | var (
42 | tmpStr string
43 | ok bool
44 | )
45 |
46 | cfg := getDefaultTokenConf()
47 |
48 | if tmpStr, ok = conf.Get("common", "token"); ok {
49 | cfg.Token = tmpStr
50 | }
51 |
52 | return cfg
53 | }
54 |
55 | type TokenAuthSetterVerifier struct {
56 | baseConfig
57 |
58 | token string
59 | }
60 |
61 | func NewTokenAuth(baseCfg baseConfig, cfg tokenConfig) *TokenAuthSetterVerifier {
62 | return &TokenAuthSetterVerifier{
63 | baseConfig: baseCfg,
64 | token: cfg.Token,
65 | }
66 | }
67 |
68 | func (auth *TokenAuthSetterVerifier) SetLogin(loginMsg *msg.Login) (err error) {
69 | loginMsg.PrivilegeKey = util.GetAuthKey(auth.token, loginMsg.Timestamp)
70 | return nil
71 | }
72 |
73 | func (auth *TokenAuthSetterVerifier) SetPing(pingMsg *msg.Ping) error {
74 | if !auth.AuthenticateHeartBeats {
75 | return nil
76 | }
77 |
78 | pingMsg.Timestamp = time.Now().Unix()
79 | pingMsg.PrivilegeKey = util.GetAuthKey(auth.token, pingMsg.Timestamp)
80 | return nil
81 | }
82 |
83 | func (auth *TokenAuthSetterVerifier) SetNewWorkConn(newWorkConnMsg *msg.NewWorkConn) error {
84 | if !auth.AuthenticateNewWorkConns {
85 | return nil
86 | }
87 |
88 | newWorkConnMsg.Timestamp = time.Now().Unix()
89 | newWorkConnMsg.PrivilegeKey = util.GetAuthKey(auth.token, newWorkConnMsg.Timestamp)
90 | return nil
91 | }
92 |
93 | func (auth *TokenAuthSetterVerifier) VerifyLogin(loginMsg *msg.Login) error {
94 | if util.GetAuthKey(auth.token, loginMsg.Timestamp) != loginMsg.PrivilegeKey {
95 | return fmt.Errorf("token in login doesn't match token from configuration")
96 | }
97 | return nil
98 | }
99 |
100 | func (auth *TokenAuthSetterVerifier) VerifyPing(pingMsg *msg.Ping) error {
101 | if !auth.AuthenticateHeartBeats {
102 | return nil
103 | }
104 |
105 | if util.GetAuthKey(auth.token, pingMsg.Timestamp) != pingMsg.PrivilegeKey {
106 | return fmt.Errorf("token in heartbeat doesn't match token from configuration")
107 | }
108 | return nil
109 | }
110 |
111 | func (auth *TokenAuthSetterVerifier) VerifyNewWorkConn(newWorkConnMsg *msg.NewWorkConn) error {
112 | if !auth.AuthenticateNewWorkConns {
113 | return nil
114 | }
115 |
116 | if util.GetAuthKey(auth.token, newWorkConnMsg.Timestamp) != newWorkConnMsg.PrivilegeKey {
117 | return fmt.Errorf("token in NewWorkConn doesn't match token from configuration")
118 | }
119 | return nil
120 | }
121 |
--------------------------------------------------------------------------------
/FRPsource/pkg/config/types.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package config
16 |
17 | import (
18 | "encoding/json"
19 | "errors"
20 | "strconv"
21 | "strings"
22 | )
23 |
24 | const (
25 | MB = 1024 * 1024
26 | KB = 1024
27 | )
28 |
29 | type BandwidthQuantity struct {
30 | s string // MB or KB
31 |
32 | i int64 // bytes
33 | }
34 |
35 | func NewBandwidthQuantity(s string) (BandwidthQuantity, error) {
36 | q := BandwidthQuantity{}
37 | err := q.UnmarshalString(s)
38 | if err != nil {
39 | return q, err
40 | }
41 | return q, nil
42 | }
43 |
44 | func (q *BandwidthQuantity) Equal(u *BandwidthQuantity) bool {
45 | if q == nil && u == nil {
46 | return true
47 | }
48 | if q != nil && u != nil {
49 | return q.i == u.i
50 | }
51 | return false
52 | }
53 |
54 | func (q *BandwidthQuantity) String() string {
55 | return q.s
56 | }
57 |
58 | func (q *BandwidthQuantity) UnmarshalString(s string) error {
59 | s = strings.TrimSpace(s)
60 | if s == "" {
61 | return nil
62 | }
63 |
64 | var (
65 | base int64
66 | f float64
67 | err error
68 | )
69 | if strings.HasSuffix(s, "MB") {
70 | base = MB
71 | fstr := strings.TrimSuffix(s, "MB")
72 | f, err = strconv.ParseFloat(fstr, 64)
73 | if err != nil {
74 | return err
75 | }
76 | } else if strings.HasSuffix(s, "KB") {
77 | base = KB
78 | fstr := strings.TrimSuffix(s, "KB")
79 | f, err = strconv.ParseFloat(fstr, 64)
80 | if err != nil {
81 | return err
82 | }
83 | } else {
84 | return errors.New("unit not support")
85 | }
86 |
87 | q.s = s
88 | q.i = int64(f * float64(base))
89 | return nil
90 | }
91 |
92 | func (q *BandwidthQuantity) UnmarshalJSON(b []byte) error {
93 | if len(b) == 4 && string(b) == "null" {
94 | return nil
95 | }
96 |
97 | var str string
98 | err := json.Unmarshal(b, &str)
99 | if err != nil {
100 | return err
101 | }
102 |
103 | return q.UnmarshalString(str)
104 | }
105 |
106 | func (q *BandwidthQuantity) MarshalJSON() ([]byte, error) {
107 | return []byte("\"" + q.s + "\""), nil
108 | }
109 |
110 | func (q *BandwidthQuantity) Bytes() int64 {
111 | return q.i
112 | }
113 |
--------------------------------------------------------------------------------
/FRPsource/pkg/config/types_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package config
16 |
17 | import (
18 | "encoding/json"
19 | "testing"
20 |
21 | "github.com/stretchr/testify/assert"
22 | )
23 |
24 | type Wrap struct {
25 | B BandwidthQuantity `json:"b"`
26 | Int int `json:"int"`
27 | }
28 |
29 | func TestBandwidthQuantity(t *testing.T) {
30 | assert := assert.New(t)
31 |
32 | var w Wrap
33 | err := json.Unmarshal([]byte(`{"b":"1KB","int":5}`), &w)
34 | assert.NoError(err)
35 | assert.EqualValues(1*KB, w.B.Bytes())
36 |
37 | buf, err := json.Marshal(&w)
38 | assert.NoError(err)
39 | assert.Equal(`{"b":"1KB","int":5}`, string(buf))
40 | }
41 |
--------------------------------------------------------------------------------
/FRPsource/pkg/config/value.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "bytes"
5 | "io/ioutil"
6 | "os"
7 | "strings"
8 | "text/template"
9 | )
10 |
11 | var (
12 | glbEnvs map[string]string
13 | )
14 |
15 | func init() {
16 | glbEnvs = make(map[string]string)
17 | envs := os.Environ()
18 | for _, env := range envs {
19 | kv := strings.Split(env, "=")
20 | if len(kv) != 2 {
21 | continue
22 | }
23 | glbEnvs[kv[0]] = kv[1]
24 | }
25 | }
26 |
27 | type Values struct {
28 | Envs map[string]string // environment vars
29 | }
30 |
31 | func GetValues() *Values {
32 | return &Values{
33 | Envs: glbEnvs,
34 | }
35 | }
36 |
37 | func RenderContent(in string) (out string, err error) {
38 | tmpl, errRet := template.New("frp").Parse(in)
39 | if errRet != nil {
40 | err = errRet
41 | return
42 | }
43 |
44 | buffer := bytes.NewBufferString("")
45 | v := GetValues()
46 | err = tmpl.Execute(buffer, v)
47 | if err != nil {
48 | return
49 | }
50 | out = buffer.String()
51 | return
52 | }
53 |
54 | func GetRenderedConfFromFile(path string) (out string, err error) {
55 | var b []byte
56 | b, err = ioutil.ReadFile(path)
57 | if err != nil {
58 | return
59 | }
60 | content := string(b)
61 |
62 | out, err = RenderContent(content)
63 | return
64 | }
65 |
--------------------------------------------------------------------------------
/FRPsource/pkg/consts/consts.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package consts
16 |
17 | var (
18 | // proxy status
19 | Idle string = "idle"
20 | Working string = "working"
21 | Closed string = "closed"
22 | Online string = "online"
23 | Offline string = "offline"
24 |
25 | // proxy type
26 | TCPProxy string = "tcp"
27 | UDPProxy string = "udp"
28 | TCPMuxProxy string = "tcpmux"
29 | HTTPProxy string = "http"
30 | HTTPSProxy string = "https"
31 | STCPProxy string = "stcp"
32 | XTCPProxy string = "xtcp"
33 | SUDPProxy string = "sudp"
34 |
35 | // authentication method
36 | TokenAuthMethod string = "token"
37 | OidcAuthMethod string = "oidc"
38 |
39 | // TCP multiplexer
40 | HTTPConnectTCPMultiplexer string = "httpconnect"
41 | )
42 |
--------------------------------------------------------------------------------
/FRPsource/pkg/errors/errors.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package errors
16 |
17 | import (
18 | "errors"
19 | )
20 |
21 | var (
22 | ErrMsgType = errors.New("message type error")
23 | ErrCtlClosed = errors.New("control is closed")
24 | )
25 |
--------------------------------------------------------------------------------
/FRPsource/pkg/metrics/aggregate/server.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package aggregate
16 |
17 | import (
18 | "github.com/fatedier/frp/pkg/metrics/mem"
19 | "github.com/fatedier/frp/pkg/metrics/prometheus"
20 | "github.com/fatedier/frp/server/metrics"
21 | )
22 |
23 | // EnableMem start to mark metrics to memory monitor system.
24 | func EnableMem() {
25 | sm.Add(mem.ServerMetrics)
26 | }
27 |
28 | // EnablePrometheus start to mark metrics to prometheus.
29 | func EnablePrometheus() {
30 | sm.Add(prometheus.ServerMetrics)
31 | }
32 |
33 | var sm *serverMetrics = &serverMetrics{}
34 |
35 | func init() {
36 | metrics.Register(sm)
37 | }
38 |
39 | type serverMetrics struct {
40 | ms []metrics.ServerMetrics
41 | }
42 |
43 | func (m *serverMetrics) Add(sm metrics.ServerMetrics) {
44 | m.ms = append(m.ms, sm)
45 | }
46 |
47 | func (m *serverMetrics) NewClient() {
48 | for _, v := range m.ms {
49 | v.NewClient()
50 | }
51 | }
52 |
53 | func (m *serverMetrics) CloseClient() {
54 | for _, v := range m.ms {
55 | v.CloseClient()
56 | }
57 | }
58 |
59 | func (m *serverMetrics) NewProxy(name string, proxyType string) {
60 | for _, v := range m.ms {
61 | v.NewProxy(name, proxyType)
62 | }
63 | }
64 |
65 | func (m *serverMetrics) CloseProxy(name string, proxyType string) {
66 | for _, v := range m.ms {
67 | v.CloseProxy(name, proxyType)
68 | }
69 | }
70 |
71 | func (m *serverMetrics) OpenConnection(name string, proxyType string) {
72 | for _, v := range m.ms {
73 | v.OpenConnection(name, proxyType)
74 | }
75 | }
76 |
77 | func (m *serverMetrics) CloseConnection(name string, proxyType string) {
78 | for _, v := range m.ms {
79 | v.CloseConnection(name, proxyType)
80 | }
81 | }
82 |
83 | func (m *serverMetrics) AddTrafficIn(name string, proxyType string, trafficBytes int64) {
84 | for _, v := range m.ms {
85 | v.AddTrafficIn(name, proxyType, trafficBytes)
86 | }
87 | }
88 |
89 | func (m *serverMetrics) AddTrafficOut(name string, proxyType string, trafficBytes int64) {
90 | for _, v := range m.ms {
91 | v.AddTrafficOut(name, proxyType, trafficBytes)
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/FRPsource/pkg/metrics/mem/types.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package mem
16 |
17 | import (
18 | "time"
19 |
20 | "github.com/fatedier/frp/pkg/util/metric"
21 | )
22 |
23 | const (
24 | ReserveDays = 7
25 | )
26 |
27 | type ServerStats struct {
28 | TotalTrafficIn int64
29 | TotalTrafficOut int64
30 | CurConns int64
31 | ClientCounts int64
32 | ProxyTypeCounts map[string]int64
33 | }
34 |
35 | type ProxyStats struct {
36 | Name string
37 | Type string
38 | TodayTrafficIn int64
39 | TodayTrafficOut int64
40 | LastStartTime string
41 | LastCloseTime string
42 | CurConns int64
43 | }
44 |
45 | type ProxyTrafficInfo struct {
46 | Name string
47 | TrafficIn []int64
48 | TrafficOut []int64
49 | }
50 |
51 | type ProxyStatistics struct {
52 | Name string
53 | ProxyType string
54 | TrafficIn metric.DateCounter
55 | TrafficOut metric.DateCounter
56 | CurConns metric.Counter
57 | LastStartTime time.Time
58 | LastCloseTime time.Time
59 | }
60 |
61 | type ServerStatistics struct {
62 | TotalTrafficIn metric.DateCounter
63 | TotalTrafficOut metric.DateCounter
64 | CurConns metric.Counter
65 |
66 | // counter for clients
67 | ClientCounts metric.Counter
68 |
69 | // counter for proxy types
70 | ProxyTypeCounts map[string]metric.Counter
71 |
72 | // statistics for different proxies
73 | // key is proxy name
74 | ProxyStatistics map[string]*ProxyStatistics
75 | }
76 |
77 | type Collector interface {
78 | GetServer() *ServerStats
79 | GetProxiesByType(proxyType string) []*ProxyStats
80 | GetProxiesByTypeAndName(proxyType string, proxyName string) *ProxyStats
81 | GetProxyTraffic(name string) *ProxyTrafficInfo
82 | }
83 |
--------------------------------------------------------------------------------
/FRPsource/pkg/metrics/metrics.go:
--------------------------------------------------------------------------------
1 | package metrics
2 |
3 | import (
4 | "github.com/fatedier/frp/pkg/metrics/aggregate"
5 | )
6 |
7 | var EnableMem = aggregate.EnableMem
8 | var EnablePrometheus = aggregate.EnablePrometheus
9 |
--------------------------------------------------------------------------------
/FRPsource/pkg/metrics/prometheus/server.go:
--------------------------------------------------------------------------------
1 | package prometheus
2 |
3 | import (
4 | "github.com/fatedier/frp/server/metrics"
5 |
6 | "github.com/prometheus/client_golang/prometheus"
7 | )
8 |
9 | const (
10 | namespace = "frp"
11 | serverSubsystem = "server"
12 | )
13 |
14 | var ServerMetrics metrics.ServerMetrics = newServerMetrics()
15 |
16 | type serverMetrics struct {
17 | clientCount prometheus.Gauge
18 | proxyCount *prometheus.GaugeVec
19 | connectionCount *prometheus.GaugeVec
20 | trafficIn *prometheus.CounterVec
21 | trafficOut *prometheus.CounterVec
22 | }
23 |
24 | func (m *serverMetrics) NewClient() {
25 | m.clientCount.Inc()
26 | }
27 |
28 | func (m *serverMetrics) CloseClient() {
29 | m.clientCount.Dec()
30 | }
31 |
32 | func (m *serverMetrics) NewProxy(name string, proxyType string) {
33 | m.proxyCount.WithLabelValues(proxyType).Inc()
34 | }
35 |
36 | func (m *serverMetrics) CloseProxy(name string, proxyType string) {
37 | m.proxyCount.WithLabelValues(proxyType).Dec()
38 | }
39 |
40 | func (m *serverMetrics) OpenConnection(name string, proxyType string) {
41 | m.connectionCount.WithLabelValues(name, proxyType).Inc()
42 | }
43 |
44 | func (m *serverMetrics) CloseConnection(name string, proxyType string) {
45 | m.connectionCount.WithLabelValues(name, proxyType).Dec()
46 | }
47 |
48 | func (m *serverMetrics) AddTrafficIn(name string, proxyType string, trafficBytes int64) {
49 | m.trafficIn.WithLabelValues(name, proxyType).Add(float64(trafficBytes))
50 | }
51 |
52 | func (m *serverMetrics) AddTrafficOut(name string, proxyType string, trafficBytes int64) {
53 | m.trafficOut.WithLabelValues(name, proxyType).Add(float64(trafficBytes))
54 | }
55 |
56 | func newServerMetrics() *serverMetrics {
57 | m := &serverMetrics{
58 | clientCount: prometheus.NewGauge(prometheus.GaugeOpts{
59 | Namespace: namespace,
60 | Subsystem: serverSubsystem,
61 | Name: "client_counts",
62 | Help: "The current client counts of frps",
63 | }),
64 | proxyCount: prometheus.NewGaugeVec(prometheus.GaugeOpts{
65 | Namespace: namespace,
66 | Subsystem: serverSubsystem,
67 | Name: "proxy_counts",
68 | Help: "The current proxy counts",
69 | }, []string{"type"}),
70 | connectionCount: prometheus.NewGaugeVec(prometheus.GaugeOpts{
71 | Namespace: namespace,
72 | Subsystem: serverSubsystem,
73 | Name: "connection_counts",
74 | Help: "The current connection counts",
75 | }, []string{"name", "type"}),
76 | trafficIn: prometheus.NewCounterVec(prometheus.CounterOpts{
77 | Namespace: namespace,
78 | Subsystem: serverSubsystem,
79 | Name: "traffic_in",
80 | Help: "The total in traffic",
81 | }, []string{"name", "type"}),
82 | trafficOut: prometheus.NewCounterVec(prometheus.CounterOpts{
83 | Namespace: namespace,
84 | Subsystem: serverSubsystem,
85 | Name: "traffic_out",
86 | Help: "The total out traffic",
87 | }, []string{"name", "type"}),
88 | }
89 | prometheus.MustRegister(m.clientCount)
90 | prometheus.MustRegister(m.proxyCount)
91 | prometheus.MustRegister(m.connectionCount)
92 | prometheus.MustRegister(m.trafficIn)
93 | prometheus.MustRegister(m.trafficOut)
94 | return m
95 | }
96 |
--------------------------------------------------------------------------------
/FRPsource/pkg/msg/ctl.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package msg
16 |
17 | import (
18 | "io"
19 |
20 | jsonMsg "github.com/fatedier/golib/msg/json"
21 | )
22 |
23 | type Message = jsonMsg.Message
24 |
25 | var (
26 | msgCtl *jsonMsg.MsgCtl
27 | )
28 |
29 | func init() {
30 | msgCtl = jsonMsg.NewMsgCtl()
31 | for typeByte, msg := range msgTypeMap {
32 | msgCtl.RegisterMsg(typeByte, msg)
33 | }
34 | }
35 |
36 | func ReadMsg(c io.Reader) (msg Message, err error) {
37 | return msgCtl.ReadMsg(c)
38 | }
39 |
40 | func ReadMsgInto(c io.Reader, msg Message) (err error) {
41 | return msgCtl.ReadMsgInto(c, msg)
42 | }
43 |
44 | func WriteMsg(c io.Writer, msg interface{}) (err error) {
45 | return msgCtl.WriteMsg(c, msg)
46 | }
47 |
--------------------------------------------------------------------------------
/FRPsource/pkg/msg/msg.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package msg
16 |
17 | import "net"
18 |
19 | const (
20 | TypeLogin = 'o'
21 | TypeLoginResp = '1'
22 | TypeNewProxy = 'p'
23 | TypeNewProxyResp = '2'
24 | TypeCloseProxy = 'c'
25 | TypeNewWorkConn = 'w'
26 | TypeReqWorkConn = 'r'
27 | TypeStartWorkConn = 's'
28 | TypeNewVisitorConn = 'v'
29 | TypeNewVisitorConnResp = '3'
30 | TypePing = 'h'
31 | TypePong = '4'
32 | TypeUDPPacket = 'u'
33 | TypeNatHoleVisitor = 'i'
34 | TypeNatHoleClient = 'n'
35 | TypeNatHoleResp = 'm'
36 | TypeNatHoleClientDetectOK = 'd'
37 | TypeNatHoleSid = '5'
38 | )
39 |
40 | var (
41 | msgTypeMap = map[byte]interface{}{
42 | TypeLogin: Login{},
43 | TypeLoginResp: LoginResp{},
44 | TypeNewProxy: NewProxy{},
45 | TypeNewProxyResp: NewProxyResp{},
46 | TypeCloseProxy: CloseProxy{},
47 | TypeNewWorkConn: NewWorkConn{},
48 | TypeReqWorkConn: ReqWorkConn{},
49 | TypeStartWorkConn: StartWorkConn{},
50 | TypeNewVisitorConn: NewVisitorConn{},
51 | TypeNewVisitorConnResp: NewVisitorConnResp{},
52 | TypePing: Ping{},
53 | TypePong: Pong{},
54 | TypeUDPPacket: UDPPacket{},
55 | TypeNatHoleVisitor: NatHoleVisitor{},
56 | TypeNatHoleClient: NatHoleClient{},
57 | TypeNatHoleResp: NatHoleResp{},
58 | TypeNatHoleClientDetectOK: NatHoleClientDetectOK{},
59 | TypeNatHoleSid: NatHoleSid{},
60 | }
61 | )
62 |
63 | // When frpc start, client send this message to login to server.
64 | type Login struct {
65 | Version string `json:"version"`
66 | Hostname string `json:"hostname"`
67 | Os string `json:"os"`
68 | Arch string `json:"arch"`
69 | User string `json:"user"`
70 | PrivilegeKey string `json:"privilege_key"`
71 | Timestamp int64 `json:"timestamp"`
72 | RunID string `json:"run_id"`
73 | Metas map[string]string `json:"metas"`
74 |
75 | // Some global configures.
76 | PoolCount int `json:"pool_count"`
77 | }
78 |
79 | type LoginResp struct {
80 | Version string `json:"version"`
81 | RunID string `json:"run_id"`
82 | ServerUDPPort int `json:"server_udp_port"`
83 | Error string `json:"error"`
84 | }
85 |
86 | // When frpc login success, send this message to frps for running a new proxy.
87 | type NewProxy struct {
88 | ProxyName string `json:"proxy_name"`
89 | ProxyType string `json:"proxy_type"`
90 | UseEncryption bool `json:"use_encryption"`
91 | UseCompression bool `json:"use_compression"`
92 | Group string `json:"group"`
93 | GroupKey string `json:"group_key"`
94 | Metas map[string]string `json:"metas"`
95 |
96 | // tcp and udp only
97 | RemotePort int `json:"remote_port"`
98 |
99 | // http and https only
100 | CustomDomains []string `json:"custom_domains"`
101 | SubDomain string `json:"subdomain"`
102 | Locations []string `json:"locations"`
103 | HTTPUser string `json:"http_user"`
104 | HTTPPwd string `json:"http_pwd"`
105 | HostHeaderRewrite string `json:"host_header_rewrite"`
106 | Headers map[string]string `json:"headers"`
107 |
108 | // stcp
109 | Sk string `json:"sk"`
110 |
111 | // tcpmux
112 | Multiplexer string `json:"multiplexer"`
113 | }
114 |
115 | type NewProxyResp struct {
116 | ProxyName string `json:"proxy_name"`
117 | RemoteAddr string `json:"remote_addr"`
118 | Error string `json:"error"`
119 | }
120 |
121 | type CloseProxy struct {
122 | ProxyName string `json:"proxy_name"`
123 | }
124 |
125 | type NewWorkConn struct {
126 | RunID string `json:"run_id"`
127 | PrivilegeKey string `json:"privilege_key"`
128 | Timestamp int64 `json:"timestamp"`
129 | }
130 |
131 | type ReqWorkConn struct {
132 | }
133 |
134 | type StartWorkConn struct {
135 | ProxyName string `json:"proxy_name"`
136 | SrcAddr string `json:"src_addr"`
137 | DstAddr string `json:"dst_addr"`
138 | SrcPort uint16 `json:"src_port"`
139 | DstPort uint16 `json:"dst_port"`
140 | Error string `json:"error"`
141 | }
142 |
143 | type NewVisitorConn struct {
144 | ProxyName string `json:"proxy_name"`
145 | SignKey string `json:"sign_key"`
146 | Timestamp int64 `json:"timestamp"`
147 | UseEncryption bool `json:"use_encryption"`
148 | UseCompression bool `json:"use_compression"`
149 | }
150 |
151 | type NewVisitorConnResp struct {
152 | ProxyName string `json:"proxy_name"`
153 | Error string `json:"error"`
154 | }
155 |
156 | type Ping struct {
157 | PrivilegeKey string `json:"privilege_key"`
158 | Timestamp int64 `json:"timestamp"`
159 | }
160 |
161 | type Pong struct {
162 | Error string `json:"error"`
163 | }
164 |
165 | type UDPPacket struct {
166 | Content string `json:"c"`
167 | LocalAddr *net.UDPAddr `json:"l"`
168 | RemoteAddr *net.UDPAddr `json:"r"`
169 | }
170 |
171 | type NatHoleVisitor struct {
172 | ProxyName string `json:"proxy_name"`
173 | SignKey string `json:"sign_key"`
174 | Timestamp int64 `json:"timestamp"`
175 | }
176 |
177 | type NatHoleClient struct {
178 | ProxyName string `json:"proxy_name"`
179 | Sid string `json:"sid"`
180 | }
181 |
182 | type NatHoleResp struct {
183 | Sid string `json:"sid"`
184 | VisitorAddr string `json:"visitor_addr"`
185 | ClientAddr string `json:"client_addr"`
186 | Error string `json:"error"`
187 | }
188 |
189 | type NatHoleClientDetectOK struct {
190 | }
191 |
192 | type NatHoleSid struct {
193 | Sid string `json:"sid"`
194 | }
195 |
--------------------------------------------------------------------------------
/FRPsource/pkg/nathole/nathole.go:
--------------------------------------------------------------------------------
1 | package nathole
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "net"
7 | "sync"
8 | "time"
9 |
10 | "github.com/fatedier/frp/pkg/msg"
11 | "github.com/fatedier/frp/pkg/util/log"
12 | "github.com/fatedier/frp/pkg/util/util"
13 |
14 | "github.com/fatedier/golib/errors"
15 | "github.com/fatedier/golib/pool"
16 | )
17 |
18 | // Timeout seconds.
19 | var NatHoleTimeout int64 = 10
20 |
21 | type SidRequest struct {
22 | Sid string
23 | NotifyCh chan struct{}
24 | }
25 |
26 | type Controller struct {
27 | listener *net.UDPConn
28 |
29 | clientCfgs map[string]*ClientCfg
30 | sessions map[string]*Session
31 |
32 | mu sync.RWMutex
33 | }
34 |
35 | func NewController(udpBindAddr string) (nc *Controller, err error) {
36 | addr, err := net.ResolveUDPAddr("udp", udpBindAddr)
37 | if err != nil {
38 | return nil, err
39 | }
40 | lconn, err := net.ListenUDP("udp", addr)
41 | if err != nil {
42 | return nil, err
43 | }
44 | nc = &Controller{
45 | listener: lconn,
46 | clientCfgs: make(map[string]*ClientCfg),
47 | sessions: make(map[string]*Session),
48 | }
49 | return nc, nil
50 | }
51 |
52 | func (nc *Controller) ListenClient(name string, sk string) (sidCh chan *SidRequest) {
53 | clientCfg := &ClientCfg{
54 | Name: name,
55 | Sk: sk,
56 | SidCh: make(chan *SidRequest),
57 | }
58 | nc.mu.Lock()
59 | nc.clientCfgs[name] = clientCfg
60 | nc.mu.Unlock()
61 | return clientCfg.SidCh
62 | }
63 |
64 | func (nc *Controller) CloseClient(name string) {
65 | nc.mu.Lock()
66 | defer nc.mu.Unlock()
67 | delete(nc.clientCfgs, name)
68 | }
69 |
70 | func (nc *Controller) Run() {
71 | for {
72 | buf := pool.GetBuf(1024)
73 | n, raddr, err := nc.listener.ReadFromUDP(buf)
74 | if err != nil {
75 | log.Trace("nat hole listener read from udp error: %v", err)
76 | return
77 | }
78 |
79 | rd := bytes.NewReader(buf[:n])
80 | rawMsg, err := msg.ReadMsg(rd)
81 | if err != nil {
82 | log.Trace("read nat hole message error: %v", err)
83 | continue
84 | }
85 |
86 | switch m := rawMsg.(type) {
87 | case *msg.NatHoleVisitor:
88 | go nc.HandleVisitor(m, raddr)
89 | case *msg.NatHoleClient:
90 | go nc.HandleClient(m, raddr)
91 | default:
92 | log.Trace("error nat hole message type")
93 | continue
94 | }
95 | pool.PutBuf(buf)
96 | }
97 | }
98 |
99 | func (nc *Controller) GenSid() string {
100 | t := time.Now().Unix()
101 | id, _ := util.RandID()
102 | return fmt.Sprintf("%d%s", t, id)
103 | }
104 |
105 | func (nc *Controller) HandleVisitor(m *msg.NatHoleVisitor, raddr *net.UDPAddr) {
106 | sid := nc.GenSid()
107 | session := &Session{
108 | Sid: sid,
109 | VisitorAddr: raddr,
110 | NotifyCh: make(chan struct{}, 0),
111 | }
112 | nc.mu.Lock()
113 | clientCfg, ok := nc.clientCfgs[m.ProxyName]
114 | if !ok {
115 | nc.mu.Unlock()
116 | errInfo := fmt.Sprintf("xtcp server for [%s] doesn't exist", m.ProxyName)
117 | log.Debug(errInfo)
118 | nc.listener.WriteToUDP(nc.GenNatHoleResponse(nil, errInfo), raddr)
119 | return
120 | }
121 | if m.SignKey != util.GetAuthKey(clientCfg.Sk, m.Timestamp) {
122 | nc.mu.Unlock()
123 | errInfo := fmt.Sprintf("xtcp connection of [%s] auth failed", m.ProxyName)
124 | log.Debug(errInfo)
125 | nc.listener.WriteToUDP(nc.GenNatHoleResponse(nil, errInfo), raddr)
126 | return
127 | }
128 |
129 | nc.sessions[sid] = session
130 | nc.mu.Unlock()
131 | log.Trace("handle visitor message, sid [%s]", sid)
132 |
133 | defer func() {
134 | nc.mu.Lock()
135 | delete(nc.sessions, sid)
136 | nc.mu.Unlock()
137 | }()
138 |
139 | err := errors.PanicToError(func() {
140 | clientCfg.SidCh <- &SidRequest{
141 | Sid: sid,
142 | NotifyCh: session.NotifyCh,
143 | }
144 | })
145 | if err != nil {
146 | return
147 | }
148 |
149 | // Wait client connections.
150 | select {
151 | case <-session.NotifyCh:
152 | resp := nc.GenNatHoleResponse(session, "")
153 | log.Trace("send nat hole response to visitor")
154 | nc.listener.WriteToUDP(resp, raddr)
155 | case <-time.After(time.Duration(NatHoleTimeout) * time.Second):
156 | return
157 | }
158 | }
159 |
160 | func (nc *Controller) HandleClient(m *msg.NatHoleClient, raddr *net.UDPAddr) {
161 | nc.mu.RLock()
162 | session, ok := nc.sessions[m.Sid]
163 | nc.mu.RUnlock()
164 | if !ok {
165 | return
166 | }
167 | log.Trace("handle client message, sid [%s]", session.Sid)
168 | session.ClientAddr = raddr
169 |
170 | resp := nc.GenNatHoleResponse(session, "")
171 | log.Trace("send nat hole response to client")
172 | nc.listener.WriteToUDP(resp, raddr)
173 | }
174 |
175 | func (nc *Controller) GenNatHoleResponse(session *Session, errInfo string) []byte {
176 | var (
177 | sid string
178 | visitorAddr string
179 | clientAddr string
180 | )
181 | if session != nil {
182 | sid = session.Sid
183 | visitorAddr = session.VisitorAddr.String()
184 | clientAddr = session.ClientAddr.String()
185 | }
186 | m := &msg.NatHoleResp{
187 | Sid: sid,
188 | VisitorAddr: visitorAddr,
189 | ClientAddr: clientAddr,
190 | Error: errInfo,
191 | }
192 | b := bytes.NewBuffer(nil)
193 | err := msg.WriteMsg(b, m)
194 | if err != nil {
195 | return []byte("")
196 | }
197 | return b.Bytes()
198 | }
199 |
200 | type Session struct {
201 | Sid string
202 | VisitorAddr *net.UDPAddr
203 | ClientAddr *net.UDPAddr
204 |
205 | NotifyCh chan struct{}
206 | }
207 |
208 | type ClientCfg struct {
209 | Name string
210 | Sk string
211 | SidCh chan *SidRequest
212 | }
213 |
--------------------------------------------------------------------------------
/FRPsource/pkg/plugin/client/http2https.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package plugin
16 |
17 | import (
18 | "crypto/tls"
19 | "fmt"
20 | "io"
21 | "net"
22 | "net/http"
23 | "net/http/httputil"
24 | "strings"
25 |
26 | frpNet "github.com/fatedier/frp/pkg/util/net"
27 | )
28 |
29 | const PluginHTTP2HTTPS = "http2https"
30 |
31 | func init() {
32 | Register(PluginHTTP2HTTPS, NewHTTP2HTTPSPlugin)
33 | }
34 |
35 | type HTTP2HTTPSPlugin struct {
36 | hostHeaderRewrite string
37 | localAddr string
38 | headers map[string]string
39 |
40 | l *Listener
41 | s *http.Server
42 | }
43 |
44 | func NewHTTP2HTTPSPlugin(params map[string]string) (Plugin, error) {
45 | localAddr := params["plugin_local_addr"]
46 | hostHeaderRewrite := params["plugin_host_header_rewrite"]
47 | headers := make(map[string]string)
48 | for k, v := range params {
49 | if !strings.HasPrefix(k, "plugin_header_") {
50 | continue
51 | }
52 | if k = strings.TrimPrefix(k, "plugin_header_"); k != "" {
53 | headers[k] = v
54 | }
55 | }
56 |
57 | if localAddr == "" {
58 | return nil, fmt.Errorf("plugin_local_addr is required")
59 | }
60 |
61 | listener := NewProxyListener()
62 |
63 | p := &HTTPS2HTTPPlugin{
64 | localAddr: localAddr,
65 | hostHeaderRewrite: hostHeaderRewrite,
66 | headers: headers,
67 | l: listener,
68 | }
69 |
70 | tr := &http.Transport{
71 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
72 | }
73 |
74 | rp := &httputil.ReverseProxy{
75 | Director: func(req *http.Request) {
76 | req.URL.Scheme = "https"
77 | req.URL.Host = p.localAddr
78 | if p.hostHeaderRewrite != "" {
79 | req.Host = p.hostHeaderRewrite
80 | }
81 | for k, v := range p.headers {
82 | req.Header.Set(k, v)
83 | }
84 | },
85 | Transport: tr,
86 | }
87 |
88 | p.s = &http.Server{
89 | Handler: rp,
90 | }
91 |
92 | go p.s.Serve(listener)
93 |
94 | return p, nil
95 | }
96 |
97 | func (p *HTTP2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
98 | wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
99 | p.l.PutConn(wrapConn)
100 | }
101 |
102 | func (p *HTTP2HTTPSPlugin) Name() string {
103 | return PluginHTTP2HTTPS
104 | }
105 |
106 | func (p *HTTP2HTTPSPlugin) Close() error {
107 | if err := p.s.Close(); err != nil {
108 | return err
109 | }
110 | return nil
111 | }
112 |
--------------------------------------------------------------------------------
/FRPsource/pkg/plugin/client/https2http.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package plugin
16 |
17 | import (
18 | "crypto/tls"
19 | "fmt"
20 | "io"
21 | "net"
22 | "net/http"
23 | "net/http/httputil"
24 | "strings"
25 |
26 | frpNet "github.com/fatedier/frp/pkg/util/net"
27 | )
28 |
29 | const PluginHTTPS2HTTP = "https2http"
30 |
31 | func init() {
32 | Register(PluginHTTPS2HTTP, NewHTTPS2HTTPPlugin)
33 | }
34 |
35 | type HTTPS2HTTPPlugin struct {
36 | crtPath string
37 | keyPath string
38 | hostHeaderRewrite string
39 | localAddr string
40 | headers map[string]string
41 |
42 | l *Listener
43 | s *http.Server
44 | }
45 |
46 | func NewHTTPS2HTTPPlugin(params map[string]string) (Plugin, error) {
47 | crtPath := params["plugin_crt_path"]
48 | keyPath := params["plugin_key_path"]
49 | localAddr := params["plugin_local_addr"]
50 | hostHeaderRewrite := params["plugin_host_header_rewrite"]
51 | headers := make(map[string]string)
52 | for k, v := range params {
53 | if !strings.HasPrefix(k, "plugin_header_") {
54 | continue
55 | }
56 | if k = strings.TrimPrefix(k, "plugin_header_"); k != "" {
57 | headers[k] = v
58 | }
59 | }
60 |
61 | if crtPath == "" {
62 | return nil, fmt.Errorf("plugin_crt_path is required")
63 | }
64 | if keyPath == "" {
65 | return nil, fmt.Errorf("plugin_key_path is required")
66 | }
67 | if localAddr == "" {
68 | return nil, fmt.Errorf("plugin_local_addr is required")
69 | }
70 |
71 | listener := NewProxyListener()
72 |
73 | p := &HTTPS2HTTPPlugin{
74 | crtPath: crtPath,
75 | keyPath: keyPath,
76 | localAddr: localAddr,
77 | hostHeaderRewrite: hostHeaderRewrite,
78 | headers: headers,
79 | l: listener,
80 | }
81 |
82 | rp := &httputil.ReverseProxy{
83 | Director: func(req *http.Request) {
84 | req.URL.Scheme = "http"
85 | req.URL.Host = p.localAddr
86 | if p.hostHeaderRewrite != "" {
87 | req.Host = p.hostHeaderRewrite
88 | }
89 | for k, v := range p.headers {
90 | req.Header.Set(k, v)
91 | }
92 | },
93 | }
94 |
95 | p.s = &http.Server{
96 | Handler: rp,
97 | }
98 |
99 | tlsConfig, err := p.genTLSConfig()
100 | if err != nil {
101 | return nil, fmt.Errorf("gen TLS config error: %v", err)
102 | }
103 | ln := tls.NewListener(listener, tlsConfig)
104 |
105 | go p.s.Serve(ln)
106 | return p, nil
107 | }
108 |
109 | func (p *HTTPS2HTTPPlugin) genTLSConfig() (*tls.Config, error) {
110 | cert, err := tls.LoadX509KeyPair(p.crtPath, p.keyPath)
111 | if err != nil {
112 | return nil, err
113 | }
114 |
115 | config := &tls.Config{Certificates: []tls.Certificate{cert}}
116 | return config, nil
117 | }
118 |
119 | func (p *HTTPS2HTTPPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
120 | wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
121 | p.l.PutConn(wrapConn)
122 | }
123 |
124 | func (p *HTTPS2HTTPPlugin) Name() string {
125 | return PluginHTTPS2HTTP
126 | }
127 |
128 | func (p *HTTPS2HTTPPlugin) Close() error {
129 | if err := p.s.Close(); err != nil {
130 | return err
131 | }
132 | return nil
133 | }
134 |
--------------------------------------------------------------------------------
/FRPsource/pkg/plugin/client/plugin.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package plugin
16 |
17 | import (
18 | "fmt"
19 | "io"
20 | "net"
21 | "sync"
22 |
23 | "github.com/fatedier/golib/errors"
24 | )
25 |
26 | // Creators is used for create plugins to handle connections.
27 | var creators = make(map[string]CreatorFn)
28 |
29 | // params has prefix "plugin_"
30 | type CreatorFn func(params map[string]string) (Plugin, error)
31 |
32 | func Register(name string, fn CreatorFn) {
33 | creators[name] = fn
34 | }
35 |
36 | func Create(name string, params map[string]string) (p Plugin, err error) {
37 | if fn, ok := creators[name]; ok {
38 | p, err = fn(params)
39 | } else {
40 | err = fmt.Errorf("plugin [%s] is not registered", name)
41 | }
42 | return
43 | }
44 |
45 | type Plugin interface {
46 | Name() string
47 |
48 | // extraBufToLocal will send to local connection first, then join conn with local connection
49 | Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte)
50 | Close() error
51 | }
52 |
53 | type Listener struct {
54 | conns chan net.Conn
55 | closed bool
56 | mu sync.Mutex
57 | }
58 |
59 | func NewProxyListener() *Listener {
60 | return &Listener{
61 | conns: make(chan net.Conn, 64),
62 | }
63 | }
64 |
65 | func (l *Listener) Accept() (net.Conn, error) {
66 | conn, ok := <-l.conns
67 | if !ok {
68 | return nil, fmt.Errorf("listener closed")
69 | }
70 | return conn, nil
71 | }
72 |
73 | func (l *Listener) PutConn(conn net.Conn) error {
74 | err := errors.PanicToError(func() {
75 | l.conns <- conn
76 | })
77 | return err
78 | }
79 |
80 | func (l *Listener) Close() error {
81 | l.mu.Lock()
82 | defer l.mu.Unlock()
83 | if !l.closed {
84 | close(l.conns)
85 | l.closed = true
86 | }
87 | return nil
88 | }
89 |
90 | func (l *Listener) Addr() net.Addr {
91 | return (*net.TCPAddr)(nil)
92 | }
93 |
--------------------------------------------------------------------------------
/FRPsource/pkg/plugin/client/socks5.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package plugin
16 |
17 | import (
18 | "io"
19 | "io/ioutil"
20 | "log"
21 | "net"
22 |
23 | frpNet "github.com/fatedier/frp/pkg/util/net"
24 |
25 | gosocks5 "github.com/armon/go-socks5"
26 | )
27 |
28 | const PluginSocks5 = "socks5"
29 |
30 | func init() {
31 | Register(PluginSocks5, NewSocks5Plugin)
32 | }
33 |
34 | type Socks5Plugin struct {
35 | Server *gosocks5.Server
36 |
37 | user string
38 | passwd string
39 | }
40 |
41 | func NewSocks5Plugin(params map[string]string) (p Plugin, err error) {
42 | user := params["plugin_user"]
43 | passwd := params["plugin_passwd"]
44 |
45 | cfg := &gosocks5.Config{
46 | Logger: log.New(ioutil.Discard, "", log.LstdFlags),
47 | }
48 | if user != "" || passwd != "" {
49 | cfg.Credentials = gosocks5.StaticCredentials(map[string]string{user: passwd})
50 | }
51 | sp := &Socks5Plugin{}
52 | sp.Server, err = gosocks5.New(cfg)
53 | p = sp
54 | return
55 | }
56 |
57 | func (sp *Socks5Plugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
58 | defer conn.Close()
59 | wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
60 | sp.Server.ServeConn(wrapConn)
61 | }
62 |
63 | func (sp *Socks5Plugin) Name() string {
64 | return PluginSocks5
65 | }
66 |
67 | func (sp *Socks5Plugin) Close() error {
68 | return nil
69 | }
70 |
--------------------------------------------------------------------------------
/FRPsource/pkg/plugin/client/static_file.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package plugin
16 |
17 | import (
18 | "io"
19 | "net"
20 | "net/http"
21 |
22 | frpNet "github.com/fatedier/frp/pkg/util/net"
23 |
24 | "github.com/gorilla/mux"
25 | )
26 |
27 | const PluginStaticFile = "static_file"
28 |
29 | func init() {
30 | Register(PluginStaticFile, NewStaticFilePlugin)
31 | }
32 |
33 | type StaticFilePlugin struct {
34 | localPath string
35 | stripPrefix string
36 | httpUser string
37 | httpPasswd string
38 |
39 | l *Listener
40 | s *http.Server
41 | }
42 |
43 | func NewStaticFilePlugin(params map[string]string) (Plugin, error) {
44 | localPath := params["plugin_local_path"]
45 | stripPrefix := params["plugin_strip_prefix"]
46 | httpUser := params["plugin_http_user"]
47 | httpPasswd := params["plugin_http_passwd"]
48 |
49 | listener := NewProxyListener()
50 |
51 | sp := &StaticFilePlugin{
52 | localPath: localPath,
53 | stripPrefix: stripPrefix,
54 | httpUser: httpUser,
55 | httpPasswd: httpPasswd,
56 |
57 | l: listener,
58 | }
59 | var prefix string
60 | if stripPrefix != "" {
61 | prefix = "/" + stripPrefix + "/"
62 | } else {
63 | prefix = "/"
64 | }
65 |
66 | router := mux.NewRouter()
67 | router.Use(frpNet.NewHTTPAuthMiddleware(httpUser, httpPasswd).Middleware)
68 | router.PathPrefix(prefix).Handler(frpNet.MakeHTTPGzipHandler(http.StripPrefix(prefix, http.FileServer(http.Dir(localPath))))).Methods("GET")
69 | sp.s = &http.Server{
70 | Handler: router,
71 | }
72 | go sp.s.Serve(listener)
73 | return sp, nil
74 | }
75 |
76 | func (sp *StaticFilePlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
77 | wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn)
78 | sp.l.PutConn(wrapConn)
79 | }
80 |
81 | func (sp *StaticFilePlugin) Name() string {
82 | return PluginStaticFile
83 | }
84 |
85 | func (sp *StaticFilePlugin) Close() error {
86 | sp.s.Close()
87 | sp.l.Close()
88 | return nil
89 | }
90 |
--------------------------------------------------------------------------------
/FRPsource/pkg/plugin/client/unix_domain_socket.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package plugin
16 |
17 | import (
18 | "fmt"
19 | "io"
20 | "net"
21 |
22 | frpIo "github.com/fatedier/golib/io"
23 | )
24 |
25 | const PluginUnixDomainSocket = "unix_domain_socket"
26 |
27 | func init() {
28 | Register(PluginUnixDomainSocket, NewUnixDomainSocketPlugin)
29 | }
30 |
31 | type UnixDomainSocketPlugin struct {
32 | UnixAddr *net.UnixAddr
33 | }
34 |
35 | func NewUnixDomainSocketPlugin(params map[string]string) (p Plugin, err error) {
36 | unixPath, ok := params["plugin_unix_path"]
37 | if !ok {
38 | err = fmt.Errorf("plugin_unix_path not found")
39 | return
40 | }
41 |
42 | unixAddr, errRet := net.ResolveUnixAddr("unix", unixPath)
43 | if errRet != nil {
44 | err = errRet
45 | return
46 | }
47 |
48 | p = &UnixDomainSocketPlugin{
49 | UnixAddr: unixAddr,
50 | }
51 | return
52 | }
53 |
54 | func (uds *UnixDomainSocketPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) {
55 | localConn, err := net.DialUnix("unix", nil, uds.UnixAddr)
56 | if err != nil {
57 | return
58 | }
59 | if len(extraBufToLocal) > 0 {
60 | localConn.Write(extraBufToLocal)
61 | }
62 |
63 | frpIo.Join(localConn, conn)
64 | }
65 |
66 | func (uds *UnixDomainSocketPlugin) Name() string {
67 | return PluginUnixDomainSocket
68 | }
69 |
70 | func (uds *UnixDomainSocketPlugin) Close() error {
71 | return nil
72 | }
73 |
--------------------------------------------------------------------------------
/FRPsource/pkg/plugin/server/http.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package plugin
16 |
17 | import (
18 | "bytes"
19 | "context"
20 | "encoding/json"
21 | "fmt"
22 | "io/ioutil"
23 | "net/http"
24 | "net/url"
25 | "reflect"
26 | )
27 |
28 | type HTTPPluginOptions struct {
29 | Name string
30 | Addr string
31 | Path string
32 | Ops []string
33 | }
34 |
35 | type httpPlugin struct {
36 | options HTTPPluginOptions
37 |
38 | url string
39 | client *http.Client
40 | }
41 |
42 | func NewHTTPPluginOptions(options HTTPPluginOptions) Plugin {
43 | return &httpPlugin{
44 | options: options,
45 | url: fmt.Sprintf("http://%s%s", options.Addr, options.Path),
46 | client: &http.Client{},
47 | }
48 | }
49 |
50 | func (p *httpPlugin) Name() string {
51 | return p.options.Name
52 | }
53 |
54 | func (p *httpPlugin) IsSupport(op string) bool {
55 | for _, v := range p.options.Ops {
56 | if v == op {
57 | return true
58 | }
59 | }
60 | return false
61 | }
62 |
63 | func (p *httpPlugin) Handle(ctx context.Context, op string, content interface{}) (*Response, interface{}, error) {
64 | r := &Request{
65 | Version: APIVersion,
66 | Op: op,
67 | Content: content,
68 | }
69 | var res Response
70 | res.Content = reflect.New(reflect.TypeOf(content)).Interface()
71 | if err := p.do(ctx, r, &res); err != nil {
72 | return nil, nil, err
73 | }
74 | return &res, res.Content, nil
75 | }
76 |
77 | func (p *httpPlugin) do(ctx context.Context, r *Request, res *Response) error {
78 | buf, err := json.Marshal(r)
79 | if err != nil {
80 | return err
81 | }
82 | v := url.Values{}
83 | v.Set("version", r.Version)
84 | v.Set("op", r.Op)
85 | req, err := http.NewRequest("POST", p.url+"?"+v.Encode(), bytes.NewReader(buf))
86 | if err != nil {
87 | return err
88 | }
89 | req = req.WithContext(ctx)
90 | req.Header.Set("X-Frp-Reqid", GetReqidFromContext(ctx))
91 | req.Header.Set("Content-Type", "application/json")
92 | resp, err := p.client.Do(req)
93 | if err != nil {
94 | return err
95 | }
96 | defer resp.Body.Close()
97 |
98 | if resp.StatusCode != http.StatusOK {
99 | return fmt.Errorf("do http request error code: %d", resp.StatusCode)
100 | }
101 | buf, err = ioutil.ReadAll(resp.Body)
102 | if err != nil {
103 | return err
104 | }
105 | if err = json.Unmarshal(buf, res); err != nil {
106 | return err
107 | }
108 | return nil
109 | }
110 |
--------------------------------------------------------------------------------
/FRPsource/pkg/plugin/server/plugin.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package plugin
16 |
17 | import (
18 | "context"
19 | )
20 |
21 | const (
22 | APIVersion = "0.1.0"
23 |
24 | OpLogin = "Login"
25 | OpNewProxy = "NewProxy"
26 | OpPing = "Ping"
27 | OpNewWorkConn = "NewWorkConn"
28 | OpNewUserConn = "NewUserConn"
29 | )
30 |
31 | type Plugin interface {
32 | Name() string
33 | IsSupport(op string) bool
34 | Handle(ctx context.Context, op string, content interface{}) (res *Response, retContent interface{}, err error)
35 | }
36 |
--------------------------------------------------------------------------------
/FRPsource/pkg/plugin/server/tracer.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package plugin
16 |
17 | import (
18 | "context"
19 | )
20 |
21 | type key int
22 |
23 | const (
24 | reqidKey key = 0
25 | )
26 |
27 | func NewReqidContext(ctx context.Context, reqid string) context.Context {
28 | return context.WithValue(ctx, reqidKey, reqid)
29 | }
30 |
31 | func GetReqidFromContext(ctx context.Context) string {
32 | ret, _ := ctx.Value(reqidKey).(string)
33 | return ret
34 | }
35 |
--------------------------------------------------------------------------------
/FRPsource/pkg/plugin/server/types.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package plugin
16 |
17 | import (
18 | "github.com/fatedier/frp/pkg/msg"
19 | )
20 |
21 | type Request struct {
22 | Version string `json:"version"`
23 | Op string `json:"op"`
24 | Content interface{} `json:"content"`
25 | }
26 |
27 | type Response struct {
28 | Reject bool `json:"reject"`
29 | RejectReason string `json:"reject_reason"`
30 | Unchange bool `json:"unchange"`
31 | Content interface{} `json:"content"`
32 | }
33 |
34 | type LoginContent struct {
35 | msg.Login
36 | }
37 |
38 | type UserInfo struct {
39 | User string `json:"user"`
40 | Metas map[string]string `json:"metas"`
41 | RunID string `json:"run_id"`
42 | }
43 |
44 | type NewProxyContent struct {
45 | User UserInfo `json:"user"`
46 | msg.NewProxy
47 | }
48 |
49 | type PingContent struct {
50 | User UserInfo `json:"user"`
51 | msg.Ping
52 | }
53 |
54 | type NewWorkConnContent struct {
55 | User UserInfo `json:"user"`
56 | msg.NewWorkConn
57 | }
58 |
59 | type NewUserConnContent struct {
60 | User UserInfo `json:"user"`
61 | ProxyName string `json:"proxy_name"`
62 | ProxyType string `json:"proxy_type"`
63 | RemoteAddr string `json:"remote_addr"`
64 | }
65 |
--------------------------------------------------------------------------------
/FRPsource/pkg/proto/udp/udp.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package udp
16 |
17 | import (
18 | "encoding/base64"
19 | "net"
20 | "sync"
21 | "time"
22 |
23 | "github.com/fatedier/frp/pkg/msg"
24 |
25 | "github.com/fatedier/golib/errors"
26 | "github.com/fatedier/golib/pool"
27 | )
28 |
29 | func NewUDPPacket(buf []byte, laddr, raddr *net.UDPAddr) *msg.UDPPacket {
30 | return &msg.UDPPacket{
31 | Content: base64.StdEncoding.EncodeToString(buf),
32 | LocalAddr: laddr,
33 | RemoteAddr: raddr,
34 | }
35 | }
36 |
37 | func GetContent(m *msg.UDPPacket) (buf []byte, err error) {
38 | buf, err = base64.StdEncoding.DecodeString(m.Content)
39 | return
40 | }
41 |
42 | func ForwardUserConn(udpConn *net.UDPConn, readCh <-chan *msg.UDPPacket, sendCh chan<- *msg.UDPPacket, bufSize int) {
43 | // read
44 | go func() {
45 | for udpMsg := range readCh {
46 | buf, err := GetContent(udpMsg)
47 | if err != nil {
48 | continue
49 | }
50 | udpConn.WriteToUDP(buf, udpMsg.RemoteAddr)
51 | }
52 | }()
53 |
54 | // write
55 | buf := pool.GetBuf(bufSize)
56 | defer pool.PutBuf(buf)
57 | for {
58 | n, remoteAddr, err := udpConn.ReadFromUDP(buf)
59 | if err != nil {
60 | return
61 | }
62 | // buf[:n] will be encoded to string, so the bytes can be reused
63 | udpMsg := NewUDPPacket(buf[:n], nil, remoteAddr)
64 |
65 | select {
66 | case sendCh <- udpMsg:
67 | default:
68 | }
69 | }
70 | }
71 |
72 | func Forwarder(dstAddr *net.UDPAddr, readCh <-chan *msg.UDPPacket, sendCh chan<- msg.Message, bufSize int) {
73 | var (
74 | mu sync.RWMutex
75 | )
76 | udpConnMap := make(map[string]*net.UDPConn)
77 |
78 | // read from dstAddr and write to sendCh
79 | writerFn := func(raddr *net.UDPAddr, udpConn *net.UDPConn) {
80 | addr := raddr.String()
81 | defer func() {
82 | mu.Lock()
83 | delete(udpConnMap, addr)
84 | mu.Unlock()
85 | udpConn.Close()
86 | }()
87 |
88 | buf := pool.GetBuf(bufSize)
89 | for {
90 | udpConn.SetReadDeadline(time.Now().Add(30 * time.Second))
91 | n, _, err := udpConn.ReadFromUDP(buf)
92 | if err != nil {
93 | return
94 | }
95 |
96 | udpMsg := NewUDPPacket(buf[:n], nil, raddr)
97 | if err = errors.PanicToError(func() {
98 | select {
99 | case sendCh <- udpMsg:
100 | default:
101 | }
102 | }); err != nil {
103 | return
104 | }
105 | }
106 | }
107 |
108 | // read from readCh
109 | go func() {
110 | for udpMsg := range readCh {
111 | buf, err := GetContent(udpMsg)
112 | if err != nil {
113 | continue
114 | }
115 | mu.Lock()
116 | udpConn, ok := udpConnMap[udpMsg.RemoteAddr.String()]
117 | if !ok {
118 | udpConn, err = net.DialUDP("udp", nil, dstAddr)
119 | if err != nil {
120 | mu.Unlock()
121 | continue
122 | }
123 | udpConnMap[udpMsg.RemoteAddr.String()] = udpConn
124 | }
125 | mu.Unlock()
126 |
127 | _, err = udpConn.Write(buf)
128 | if err != nil {
129 | udpConn.Close()
130 | }
131 |
132 | if !ok {
133 | go writerFn(udpMsg.RemoteAddr, udpConn)
134 | }
135 | }
136 | }()
137 | }
138 |
--------------------------------------------------------------------------------
/FRPsource/pkg/proto/udp/udp_test.go:
--------------------------------------------------------------------------------
1 | package udp
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | )
8 |
9 | func TestUdpPacket(t *testing.T) {
10 | assert := assert.New(t)
11 |
12 | buf := []byte("hello world")
13 | udpMsg := NewUDPPacket(buf, nil, nil)
14 |
15 | newBuf, err := GetContent(udpMsg)
16 | assert.NoError(err)
17 | assert.EqualValues(buf, newBuf)
18 | }
19 |
--------------------------------------------------------------------------------
/FRPsource/pkg/transport/tls.go:
--------------------------------------------------------------------------------
1 | package transport
2 |
3 | import (
4 | "crypto/rand"
5 | "crypto/rsa"
6 | "crypto/tls"
7 | "crypto/x509"
8 | "encoding/pem"
9 | "io/ioutil"
10 | "math/big"
11 | )
12 |
13 | func newCustomTLSKeyPair(certfile, keyfile string) (*tls.Certificate, error) {
14 | tlsCert, err := tls.LoadX509KeyPair(certfile, keyfile)
15 | if err != nil {
16 | return nil, err
17 | }
18 | return &tlsCert, nil
19 | }
20 |
21 | func newRandomTLSKeyPair() *tls.Certificate {
22 | key, err := rsa.GenerateKey(rand.Reader, 1024)
23 | if err != nil {
24 | panic(err)
25 | }
26 | template := x509.Certificate{SerialNumber: big.NewInt(1)}
27 | certDER, err := x509.CreateCertificate(
28 | rand.Reader,
29 | &template,
30 | &template,
31 | &key.PublicKey,
32 | key)
33 | if err != nil {
34 | panic(err)
35 | }
36 | keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
37 | certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
38 |
39 | tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
40 | if err != nil {
41 | panic(err)
42 | }
43 | return &tlsCert
44 | }
45 |
46 | // Only supprt one ca file to add
47 | func newCertPool(caPath string) (*x509.CertPool, error) {
48 | pool := x509.NewCertPool()
49 |
50 | caCrt, err := ioutil.ReadFile(caPath)
51 | if err != nil {
52 | return nil, err
53 | }
54 |
55 | pool.AppendCertsFromPEM(caCrt)
56 |
57 | return pool, nil
58 | }
59 |
60 | func NewServerTLSConfig(certPath, keyPath, caPath string) (*tls.Config, error) {
61 | var base = &tls.Config{}
62 |
63 | if certPath == "" || keyPath == "" {
64 | // server will generate tls conf by itself
65 | cert := newRandomTLSKeyPair()
66 | base.Certificates = []tls.Certificate{*cert}
67 | } else {
68 | cert, err := newCustomTLSKeyPair(certPath, keyPath)
69 | if err != nil {
70 | return nil, err
71 | }
72 |
73 | base.Certificates = []tls.Certificate{*cert}
74 | }
75 |
76 | if caPath != "" {
77 | pool, err := newCertPool(caPath)
78 | if err != nil {
79 | return nil, err
80 | }
81 |
82 | base.ClientAuth = tls.RequireAndVerifyClientCert
83 | base.ClientCAs = pool
84 | }
85 |
86 | return base, nil
87 | }
88 |
89 | func NewClientTLSConfig(certPath, keyPath, caPath, servearName string) (*tls.Config, error) {
90 | var base = &tls.Config{}
91 |
92 | if certPath == "" || keyPath == "" {
93 | // client will not generate tls conf by itself
94 | } else {
95 | cert, err := newCustomTLSKeyPair(certPath, keyPath)
96 | if err != nil {
97 | return nil, err
98 | }
99 |
100 | base.Certificates = []tls.Certificate{*cert}
101 | }
102 |
103 | if caPath != "" {
104 | pool, err := newCertPool(caPath)
105 | if err != nil {
106 | return nil, err
107 | }
108 |
109 | base.RootCAs = pool
110 | base.ServerName = servearName
111 | base.InsecureSkipVerify = false
112 | } else {
113 | base.InsecureSkipVerify = true
114 | }
115 |
116 | return base, nil
117 | }
118 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/limit/reader.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package limit
16 |
17 | import (
18 | "context"
19 | "io"
20 |
21 | "golang.org/x/time/rate"
22 | )
23 |
24 | type Reader struct {
25 | r io.Reader
26 | limiter *rate.Limiter
27 | }
28 |
29 | func NewReader(r io.Reader, limiter *rate.Limiter) *Reader {
30 | return &Reader{
31 | r: r,
32 | limiter: limiter,
33 | }
34 | }
35 |
36 | func (r *Reader) Read(p []byte) (n int, err error) {
37 | b := r.limiter.Burst()
38 | if b < len(p) {
39 | p = p[:b]
40 | }
41 | n, err = r.r.Read(p)
42 | if err != nil {
43 | return
44 | }
45 |
46 | err = r.limiter.WaitN(context.Background(), n)
47 | if err != nil {
48 | return
49 | }
50 | return
51 | }
52 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/limit/writer.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package limit
16 |
17 | import (
18 | "context"
19 | "io"
20 |
21 | "golang.org/x/time/rate"
22 | )
23 |
24 | type Writer struct {
25 | w io.Writer
26 | limiter *rate.Limiter
27 | }
28 |
29 | func NewWriter(w io.Writer, limiter *rate.Limiter) *Writer {
30 | return &Writer{
31 | w: w,
32 | limiter: limiter,
33 | }
34 | }
35 |
36 | func (w *Writer) Write(p []byte) (n int, err error) {
37 | var nn int
38 | b := w.limiter.Burst()
39 | for {
40 | end := len(p)
41 | if end == 0 {
42 | break
43 | }
44 | if b < len(p) {
45 | end = b
46 | }
47 | err = w.limiter.WaitN(context.Background(), end)
48 | if err != nil {
49 | return
50 | }
51 |
52 | nn, err = w.w.Write(p[:end])
53 | n += nn
54 | if err != nil {
55 | return
56 | }
57 | p = p[end:]
58 | }
59 | return
60 | }
61 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/log/log.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package log
16 |
17 | import (
18 | "fmt"
19 |
20 | "github.com/fatedier/beego/logs"
21 | )
22 |
23 | // Log is the under log object
24 | var Log *logs.BeeLogger
25 |
26 | func init() {
27 | Log = logs.NewLogger(200)
28 | Log.EnableFuncCallDepth(true)
29 | Log.SetLogFuncCallDepth(Log.GetLogFuncCallDepth() + 1)
30 | }
31 |
32 | func InitLog(logWay string, logFile string, logLevel string, maxdays int64, disableLogColor bool) {
33 | SetLogFile(logWay, logFile, maxdays, disableLogColor)
34 | SetLogLevel(logLevel)
35 | }
36 |
37 | // SetLogFile to configure log params
38 | // logWay: file or console
39 | func SetLogFile(logWay string, logFile string, maxdays int64, disableLogColor bool) {
40 | if logWay == "console" {
41 | params := ""
42 | if disableLogColor {
43 | params = fmt.Sprintf(`{"color": false}`)
44 | }
45 | Log.SetLogger("console", params)
46 | } else {
47 | params := fmt.Sprintf(`{"filename": "%s", "maxdays": %d}`, logFile, maxdays)
48 | Log.SetLogger("file", params)
49 | }
50 | }
51 |
52 | // SetLogLevel set log level, default is warning
53 | // value: error, warning, info, debug, trace
54 | func SetLogLevel(logLevel string) {
55 | level := 4 // warning
56 | switch logLevel {
57 | case "error":
58 | level = 3
59 | case "warn":
60 | level = 4
61 | case "info":
62 | level = 6
63 | case "debug":
64 | level = 7
65 | case "trace":
66 | level = 8
67 | default:
68 | level = 4
69 | }
70 | Log.SetLevel(level)
71 | }
72 |
73 | // wrap log
74 |
75 | func Error(format string, v ...interface{}) {
76 | Log.Error(format, v...)
77 | }
78 |
79 | func Warn(format string, v ...interface{}) {
80 | Log.Warn(format, v...)
81 | }
82 |
83 | func Info(format string, v ...interface{}) {
84 | Log.Info(format, v...)
85 | }
86 |
87 | func Debug(format string, v ...interface{}) {
88 | Log.Debug(format, v...)
89 | }
90 |
91 | func Trace(format string, v ...interface{}) {
92 | Log.Trace(format, v...)
93 | }
94 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/metric/counter.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package metric
16 |
17 | import (
18 | "sync/atomic"
19 | )
20 |
21 | type Counter interface {
22 | Count() int32
23 | Inc(int32)
24 | Dec(int32)
25 | Snapshot() Counter
26 | Clear()
27 | }
28 |
29 | func NewCounter() Counter {
30 | return &StandardCounter{
31 | count: 0,
32 | }
33 | }
34 |
35 | type StandardCounter struct {
36 | count int32
37 | }
38 |
39 | func (c *StandardCounter) Count() int32 {
40 | return atomic.LoadInt32(&c.count)
41 | }
42 |
43 | func (c *StandardCounter) Inc(count int32) {
44 | atomic.AddInt32(&c.count, count)
45 | }
46 |
47 | func (c *StandardCounter) Dec(count int32) {
48 | atomic.AddInt32(&c.count, -count)
49 | }
50 |
51 | func (c *StandardCounter) Snapshot() Counter {
52 | tmp := &StandardCounter{
53 | count: atomic.LoadInt32(&c.count),
54 | }
55 | return tmp
56 | }
57 |
58 | func (c *StandardCounter) Clear() {
59 | atomic.StoreInt32(&c.count, 0)
60 | }
61 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/metric/counter_test.go:
--------------------------------------------------------------------------------
1 | package metric
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | )
8 |
9 | func TestCounter(t *testing.T) {
10 | assert := assert.New(t)
11 | c := NewCounter()
12 | c.Inc(10)
13 | assert.EqualValues(10, c.Count())
14 |
15 | c.Dec(5)
16 | assert.EqualValues(5, c.Count())
17 |
18 | cTmp := c.Snapshot()
19 | assert.EqualValues(5, cTmp.Count())
20 |
21 | c.Clear()
22 | assert.EqualValues(0, c.Count())
23 | }
24 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/metric/date_counter.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package metric
16 |
17 | import (
18 | "sync"
19 | "time"
20 | )
21 |
22 | type DateCounter interface {
23 | TodayCount() int64
24 | GetLastDaysCount(lastdays int64) []int64
25 | Inc(int64)
26 | Dec(int64)
27 | Snapshot() DateCounter
28 | Clear()
29 | }
30 |
31 | func NewDateCounter(reserveDays int64) DateCounter {
32 | if reserveDays <= 0 {
33 | reserveDays = 1
34 | }
35 | return newStandardDateCounter(reserveDays)
36 | }
37 |
38 | type StandardDateCounter struct {
39 | reserveDays int64
40 | counts []int64
41 |
42 | lastUpdateDate time.Time
43 | mu sync.Mutex
44 | }
45 |
46 | func newStandardDateCounter(reserveDays int64) *StandardDateCounter {
47 | now := time.Now()
48 | now = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
49 | s := &StandardDateCounter{
50 | reserveDays: reserveDays,
51 | counts: make([]int64, reserveDays),
52 | lastUpdateDate: now,
53 | }
54 | return s
55 | }
56 |
57 | func (c *StandardDateCounter) TodayCount() int64 {
58 | c.mu.Lock()
59 | defer c.mu.Unlock()
60 |
61 | c.rotate(time.Now())
62 | return c.counts[0]
63 | }
64 |
65 | func (c *StandardDateCounter) GetLastDaysCount(lastdays int64) []int64 {
66 | if lastdays > c.reserveDays {
67 | lastdays = c.reserveDays
68 | }
69 | counts := make([]int64, lastdays)
70 |
71 | c.mu.Lock()
72 | defer c.mu.Unlock()
73 | c.rotate(time.Now())
74 | for i := 0; i < int(lastdays); i++ {
75 | counts[i] = c.counts[i]
76 | }
77 | return counts
78 | }
79 |
80 | func (c *StandardDateCounter) Inc(count int64) {
81 | c.mu.Lock()
82 | defer c.mu.Unlock()
83 | c.rotate(time.Now())
84 | c.counts[0] += count
85 | }
86 |
87 | func (c *StandardDateCounter) Dec(count int64) {
88 | c.mu.Lock()
89 | defer c.mu.Unlock()
90 | c.rotate(time.Now())
91 | c.counts[0] -= count
92 | }
93 |
94 | func (c *StandardDateCounter) Snapshot() DateCounter {
95 | c.mu.Lock()
96 | defer c.mu.Unlock()
97 | tmp := newStandardDateCounter(c.reserveDays)
98 | for i := 0; i < int(c.reserveDays); i++ {
99 | tmp.counts[i] = c.counts[i]
100 | }
101 | return tmp
102 | }
103 |
104 | func (c *StandardDateCounter) Clear() {
105 | c.mu.Lock()
106 | defer c.mu.Unlock()
107 | for i := 0; i < int(c.reserveDays); i++ {
108 | c.counts[i] = 0
109 | }
110 | }
111 |
112 | // rotate
113 | // Must hold the lock before calling this function.
114 | func (c *StandardDateCounter) rotate(now time.Time) {
115 | now = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
116 | days := int(now.Sub(c.lastUpdateDate).Hours() / 24)
117 |
118 | defer func() {
119 | c.lastUpdateDate = now
120 | }()
121 |
122 | if days <= 0 {
123 | return
124 | } else if days >= int(c.reserveDays) {
125 | c.counts = make([]int64, c.reserveDays)
126 | return
127 | }
128 | newCounts := make([]int64, c.reserveDays)
129 |
130 | for i := days; i < int(c.reserveDays); i++ {
131 | newCounts[i] = c.counts[i-days]
132 | }
133 | c.counts = newCounts
134 | }
135 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/metric/date_counter_test.go:
--------------------------------------------------------------------------------
1 | package metric
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | )
8 |
9 | func TestDateCounter(t *testing.T) {
10 | assert := assert.New(t)
11 |
12 | dc := NewDateCounter(3)
13 | dc.Inc(10)
14 | assert.EqualValues(10, dc.TodayCount())
15 |
16 | dc.Dec(5)
17 | assert.EqualValues(5, dc.TodayCount())
18 |
19 | counts := dc.GetLastDaysCount(3)
20 | assert.EqualValues(3, len(counts))
21 | assert.EqualValues(5, counts[0])
22 | assert.EqualValues(0, counts[1])
23 | assert.EqualValues(0, counts[2])
24 |
25 | dcTmp := dc.Snapshot()
26 | assert.EqualValues(5, dcTmp.TodayCount())
27 | }
28 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/metric/metrics.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package metric
16 |
17 | // GaugeMetric represents a single numerical value that can arbitrarily go up
18 | // and down.
19 | type GaugeMetric interface {
20 | Inc()
21 | Dec()
22 | Set(float64)
23 | }
24 |
25 | // CounterMetric represents a single numerical value that only ever
26 | // goes up.
27 | type CounterMetric interface {
28 | Inc()
29 | }
30 |
31 | // HistogramMetric counts individual observations.
32 | type HistogramMetric interface {
33 | Observe(float64)
34 | }
35 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/net/http.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package net
16 |
17 | import (
18 | "compress/gzip"
19 | "io"
20 | "net/http"
21 | "strings"
22 | )
23 |
24 | type HTTPAuthWraper struct {
25 | h http.Handler
26 | user string
27 | passwd string
28 | }
29 |
30 | func NewHTTPBasicAuthWraper(h http.Handler, user, passwd string) http.Handler {
31 | return &HTTPAuthWraper{
32 | h: h,
33 | user: user,
34 | passwd: passwd,
35 | }
36 | }
37 |
38 | func (aw *HTTPAuthWraper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
39 | user, passwd, hasAuth := r.BasicAuth()
40 | if (aw.user == "" && aw.passwd == "") || (hasAuth && user == aw.user && passwd == aw.passwd) {
41 | aw.h.ServeHTTP(w, r)
42 | } else {
43 | w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
44 | http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
45 | }
46 | }
47 |
48 | type HTTPAuthMiddleware struct {
49 | user string
50 | passwd string
51 | }
52 |
53 | func NewHTTPAuthMiddleware(user, passwd string) *HTTPAuthMiddleware {
54 | return &HTTPAuthMiddleware{
55 | user: user,
56 | passwd: passwd,
57 | }
58 | }
59 |
60 | func (authMid *HTTPAuthMiddleware) Middleware(next http.Handler) http.Handler {
61 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
62 | reqUser, reqPasswd, hasAuth := r.BasicAuth()
63 | if (authMid.user == "" && authMid.passwd == "") ||
64 | (hasAuth && reqUser == authMid.user && reqPasswd == authMid.passwd) {
65 | next.ServeHTTP(w, r)
66 | } else {
67 | w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
68 | http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
69 | }
70 | })
71 | }
72 |
73 | func HTTPBasicAuth(h http.HandlerFunc, user, passwd string) http.HandlerFunc {
74 | return func(w http.ResponseWriter, r *http.Request) {
75 | reqUser, reqPasswd, hasAuth := r.BasicAuth()
76 | if (user == "" && passwd == "") ||
77 | (hasAuth && reqUser == user && reqPasswd == passwd) {
78 | h.ServeHTTP(w, r)
79 | } else {
80 | w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
81 | http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
82 | }
83 | }
84 | }
85 |
86 | type HTTPGzipWraper struct {
87 | h http.Handler
88 | }
89 |
90 | func (gw *HTTPGzipWraper) ServeHTTP(w http.ResponseWriter, r *http.Request) {
91 | if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
92 | gw.h.ServeHTTP(w, r)
93 | return
94 | }
95 | w.Header().Set("Content-Encoding", "gzip")
96 | gz := gzip.NewWriter(w)
97 | defer gz.Close()
98 | gzr := gzipResponseWriter{Writer: gz, ResponseWriter: w}
99 | gw.h.ServeHTTP(gzr, r)
100 | }
101 |
102 | func MakeHTTPGzipHandler(h http.Handler) http.Handler {
103 | return &HTTPGzipWraper{
104 | h: h,
105 | }
106 | }
107 |
108 | type gzipResponseWriter struct {
109 | io.Writer
110 | http.ResponseWriter
111 | }
112 |
113 | func (w gzipResponseWriter) Write(b []byte) (int, error) {
114 | return w.Writer.Write(b)
115 | }
116 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/net/kcp.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package net
16 |
17 | import (
18 | "fmt"
19 | "net"
20 |
21 | kcp "github.com/fatedier/kcp-go"
22 | )
23 |
24 | type KCPListener struct {
25 | listener net.Listener
26 | acceptCh chan net.Conn
27 | closeFlag bool
28 | }
29 |
30 | func ListenKcp(bindAddr string, bindPort int) (l *KCPListener, err error) {
31 | listener, err := kcp.ListenWithOptions(fmt.Sprintf("%s:%d", bindAddr, bindPort), nil, 10, 3)
32 | if err != nil {
33 | return l, err
34 | }
35 | listener.SetReadBuffer(4194304)
36 | listener.SetWriteBuffer(4194304)
37 |
38 | l = &KCPListener{
39 | listener: listener,
40 | acceptCh: make(chan net.Conn),
41 | closeFlag: false,
42 | }
43 |
44 | go func() {
45 | for {
46 | conn, err := listener.AcceptKCP()
47 | if err != nil {
48 | if l.closeFlag {
49 | close(l.acceptCh)
50 | return
51 | }
52 | continue
53 | }
54 | conn.SetStreamMode(true)
55 | conn.SetWriteDelay(true)
56 | conn.SetNoDelay(1, 20, 2, 1)
57 | conn.SetMtu(1350)
58 | conn.SetWindowSize(1024, 1024)
59 | conn.SetACKNoDelay(false)
60 |
61 | l.acceptCh <- conn
62 | }
63 | }()
64 | return l, err
65 | }
66 |
67 | func (l *KCPListener) Accept() (net.Conn, error) {
68 | conn, ok := <-l.acceptCh
69 | if !ok {
70 | return conn, fmt.Errorf("channel for kcp listener closed")
71 | }
72 | return conn, nil
73 | }
74 |
75 | func (l *KCPListener) Close() error {
76 | if !l.closeFlag {
77 | l.closeFlag = true
78 | l.listener.Close()
79 | }
80 | return nil
81 | }
82 |
83 | func (l *KCPListener) Addr() net.Addr {
84 | return l.listener.Addr()
85 | }
86 |
87 | func NewKCPConnFromUDP(conn *net.UDPConn, connected bool, raddr string) (net.Conn, error) {
88 | kcpConn, err := kcp.NewConnEx(1, connected, raddr, nil, 10, 3, conn)
89 | if err != nil {
90 | return nil, err
91 | }
92 | kcpConn.SetStreamMode(true)
93 | kcpConn.SetWriteDelay(true)
94 | kcpConn.SetNoDelay(1, 20, 2, 1)
95 | kcpConn.SetMtu(1350)
96 | kcpConn.SetWindowSize(1024, 1024)
97 | kcpConn.SetACKNoDelay(false)
98 | return kcpConn, nil
99 | }
100 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/net/listener.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package net
16 |
17 | import (
18 | "fmt"
19 | "net"
20 | "sync"
21 |
22 | "github.com/fatedier/golib/errors"
23 | )
24 |
25 | // Custom listener
26 | type CustomListener struct {
27 | acceptCh chan net.Conn
28 | closed bool
29 | mu sync.Mutex
30 | }
31 |
32 | func NewCustomListener() *CustomListener {
33 | return &CustomListener{
34 | acceptCh: make(chan net.Conn, 64),
35 | }
36 | }
37 |
38 | func (l *CustomListener) Accept() (net.Conn, error) {
39 | conn, ok := <-l.acceptCh
40 | if !ok {
41 | return nil, fmt.Errorf("listener closed")
42 | }
43 | return conn, nil
44 | }
45 |
46 | func (l *CustomListener) PutConn(conn net.Conn) error {
47 | err := errors.PanicToError(func() {
48 | select {
49 | case l.acceptCh <- conn:
50 | default:
51 | conn.Close()
52 | }
53 | })
54 | return err
55 | }
56 |
57 | func (l *CustomListener) Close() error {
58 | l.mu.Lock()
59 | defer l.mu.Unlock()
60 | if !l.closed {
61 | close(l.acceptCh)
62 | l.closed = true
63 | }
64 | return nil
65 | }
66 |
67 | func (l *CustomListener) Addr() net.Addr {
68 | return (*net.TCPAddr)(nil)
69 | }
70 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/net/tls.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package net
16 |
17 | import (
18 | "crypto/tls"
19 | "fmt"
20 | "net"
21 | "time"
22 |
23 | gnet "github.com/fatedier/golib/net"
24 | )
25 |
26 | var (
27 | FRPTLSHeadByte = 0x17
28 | )
29 |
30 | func WrapTLSClientConn(c net.Conn, tlsConfig *tls.Config) (out net.Conn) {
31 | c.Write([]byte{byte(FRPTLSHeadByte)})
32 | out = tls.Client(c, tlsConfig)
33 | return
34 | }
35 |
36 | func CheckAndEnableTLSServerConnWithTimeout(c net.Conn, tlsConfig *tls.Config, tlsOnly bool, timeout time.Duration) (out net.Conn, err error) {
37 | sc, r := gnet.NewSharedConnSize(c, 2)
38 | buf := make([]byte, 1)
39 | var n int
40 | c.SetReadDeadline(time.Now().Add(timeout))
41 | n, err = r.Read(buf)
42 | c.SetReadDeadline(time.Time{})
43 | if err != nil {
44 | return
45 | }
46 |
47 | if n == 1 && int(buf[0]) == FRPTLSHeadByte {
48 | out = tls.Server(c, tlsConfig)
49 | } else {
50 | if tlsOnly {
51 | err = fmt.Errorf("non-TLS connection received on a TlsOnly server")
52 | return
53 | }
54 | out = sc
55 | }
56 | return
57 | }
58 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/net/udp.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package net
16 |
17 | import (
18 | "fmt"
19 | "io"
20 | "net"
21 | "sync"
22 | "time"
23 |
24 | "github.com/fatedier/golib/pool"
25 | )
26 |
27 | type UDPPacket struct {
28 | Buf []byte
29 | LocalAddr net.Addr
30 | RemoteAddr net.Addr
31 | }
32 |
33 | type FakeUDPConn struct {
34 | l *UDPListener
35 |
36 | localAddr net.Addr
37 | remoteAddr net.Addr
38 | packets chan []byte
39 | closeFlag bool
40 |
41 | lastActive time.Time
42 | mu sync.RWMutex
43 | }
44 |
45 | func NewFakeUDPConn(l *UDPListener, laddr, raddr net.Addr) *FakeUDPConn {
46 | fc := &FakeUDPConn{
47 | l: l,
48 | localAddr: laddr,
49 | remoteAddr: raddr,
50 | packets: make(chan []byte, 20),
51 | }
52 |
53 | go func() {
54 | for {
55 | time.Sleep(5 * time.Second)
56 | fc.mu.RLock()
57 | if time.Now().Sub(fc.lastActive) > 10*time.Second {
58 | fc.mu.RUnlock()
59 | fc.Close()
60 | break
61 | }
62 | fc.mu.RUnlock()
63 | }
64 | }()
65 | return fc
66 | }
67 |
68 | func (c *FakeUDPConn) putPacket(content []byte) {
69 | defer func() {
70 | if err := recover(); err != nil {
71 | }
72 | }()
73 |
74 | select {
75 | case c.packets <- content:
76 | default:
77 | }
78 | }
79 |
80 | func (c *FakeUDPConn) Read(b []byte) (n int, err error) {
81 | content, ok := <-c.packets
82 | if !ok {
83 | return 0, io.EOF
84 | }
85 | c.mu.Lock()
86 | c.lastActive = time.Now()
87 | c.mu.Unlock()
88 |
89 | if len(b) < len(content) {
90 | n = len(b)
91 | } else {
92 | n = len(content)
93 | }
94 | copy(b, content)
95 | return n, nil
96 | }
97 |
98 | func (c *FakeUDPConn) Write(b []byte) (n int, err error) {
99 | c.mu.RLock()
100 | if c.closeFlag {
101 | c.mu.RUnlock()
102 | return 0, io.ErrClosedPipe
103 | }
104 | c.mu.RUnlock()
105 |
106 | packet := &UDPPacket{
107 | Buf: b,
108 | LocalAddr: c.localAddr,
109 | RemoteAddr: c.remoteAddr,
110 | }
111 | c.l.writeUDPPacket(packet)
112 |
113 | c.mu.Lock()
114 | c.lastActive = time.Now()
115 | c.mu.Unlock()
116 | return len(b), nil
117 | }
118 |
119 | func (c *FakeUDPConn) Close() error {
120 | c.mu.Lock()
121 | defer c.mu.Unlock()
122 | if !c.closeFlag {
123 | c.closeFlag = true
124 | close(c.packets)
125 | }
126 | return nil
127 | }
128 |
129 | func (c *FakeUDPConn) IsClosed() bool {
130 | c.mu.RLock()
131 | defer c.mu.RUnlock()
132 | return c.closeFlag
133 | }
134 |
135 | func (c *FakeUDPConn) LocalAddr() net.Addr {
136 | return c.localAddr
137 | }
138 |
139 | func (c *FakeUDPConn) RemoteAddr() net.Addr {
140 | return c.remoteAddr
141 | }
142 |
143 | func (c *FakeUDPConn) SetDeadline(t time.Time) error {
144 | return nil
145 | }
146 |
147 | func (c *FakeUDPConn) SetReadDeadline(t time.Time) error {
148 | return nil
149 | }
150 |
151 | func (c *FakeUDPConn) SetWriteDeadline(t time.Time) error {
152 | return nil
153 | }
154 |
155 | type UDPListener struct {
156 | addr net.Addr
157 | acceptCh chan net.Conn
158 | writeCh chan *UDPPacket
159 | readConn net.Conn
160 | closeFlag bool
161 |
162 | fakeConns map[string]*FakeUDPConn
163 | }
164 |
165 | func ListenUDP(bindAddr string, bindPort int) (l *UDPListener, err error) {
166 | udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", bindAddr, bindPort))
167 | if err != nil {
168 | return l, err
169 | }
170 | readConn, err := net.ListenUDP("udp", udpAddr)
171 |
172 | l = &UDPListener{
173 | addr: udpAddr,
174 | acceptCh: make(chan net.Conn),
175 | writeCh: make(chan *UDPPacket, 1000),
176 | fakeConns: make(map[string]*FakeUDPConn),
177 | }
178 |
179 | // for reading
180 | go func() {
181 | for {
182 | buf := pool.GetBuf(1450)
183 | n, remoteAddr, err := readConn.ReadFromUDP(buf)
184 | if err != nil {
185 | close(l.acceptCh)
186 | close(l.writeCh)
187 | return
188 | }
189 |
190 | fakeConn, exist := l.fakeConns[remoteAddr.String()]
191 | if !exist || fakeConn.IsClosed() {
192 | fakeConn = NewFakeUDPConn(l, l.Addr(), remoteAddr)
193 | l.fakeConns[remoteAddr.String()] = fakeConn
194 | }
195 | fakeConn.putPacket(buf[:n])
196 |
197 | l.acceptCh <- fakeConn
198 | }
199 | }()
200 |
201 | // for writing
202 | go func() {
203 | for {
204 | packet, ok := <-l.writeCh
205 | if !ok {
206 | return
207 | }
208 |
209 | if addr, ok := packet.RemoteAddr.(*net.UDPAddr); ok {
210 | readConn.WriteToUDP(packet.Buf, addr)
211 | }
212 | }
213 | }()
214 |
215 | return
216 | }
217 |
218 | func (l *UDPListener) writeUDPPacket(packet *UDPPacket) (err error) {
219 | defer func() {
220 | if errRet := recover(); errRet != nil {
221 | err = fmt.Errorf("udp write closed listener")
222 | }
223 | }()
224 | l.writeCh <- packet
225 | return
226 | }
227 |
228 | func (l *UDPListener) WriteMsg(buf []byte, remoteAddr *net.UDPAddr) (err error) {
229 | // only set remote addr here
230 | packet := &UDPPacket{
231 | Buf: buf,
232 | RemoteAddr: remoteAddr,
233 | }
234 | err = l.writeUDPPacket(packet)
235 | return
236 | }
237 |
238 | func (l *UDPListener) Accept() (net.Conn, error) {
239 | conn, ok := <-l.acceptCh
240 | if !ok {
241 | return conn, fmt.Errorf("channel for udp listener closed")
242 | }
243 | return conn, nil
244 | }
245 |
246 | func (l *UDPListener) Close() error {
247 | if !l.closeFlag {
248 | l.closeFlag = true
249 | if l.readConn != nil {
250 | l.readConn.Close()
251 | }
252 | }
253 | return nil
254 | }
255 |
256 | func (l *UDPListener) Addr() net.Addr {
257 | return l.addr
258 | }
259 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/net/websocket.go:
--------------------------------------------------------------------------------
1 | package net
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "net"
7 | "net/http"
8 | "net/url"
9 | "strings"
10 | "time"
11 |
12 | "golang.org/x/net/websocket"
13 | )
14 |
15 | var (
16 | ErrWebsocketListenerClosed = errors.New("websocket listener closed")
17 | )
18 |
19 | const (
20 | FrpWebsocketPath = "/~!auth"
21 | )
22 |
23 | type WebsocketListener struct {
24 | ln net.Listener
25 | acceptCh chan net.Conn
26 |
27 | server *http.Server
28 | httpMutex *http.ServeMux
29 | }
30 |
31 | // NewWebsocketListener to handle websocket connections
32 | // ln: tcp listener for websocket connections
33 | func NewWebsocketListener(ln net.Listener) (wl *WebsocketListener) {
34 | wl = &WebsocketListener{
35 | acceptCh: make(chan net.Conn),
36 | }
37 |
38 | muxer := http.NewServeMux()
39 | muxer.Handle(FrpWebsocketPath, websocket.Handler(func(c *websocket.Conn) {
40 | notifyCh := make(chan struct{})
41 | conn := WrapCloseNotifyConn(c, func() {
42 | close(notifyCh)
43 | })
44 | wl.acceptCh <- conn
45 | <-notifyCh
46 | }))
47 |
48 | wl.server = &http.Server{
49 | Addr: ln.Addr().String(),
50 | Handler: muxer,
51 | }
52 |
53 | go wl.server.Serve(ln)
54 | return
55 | }
56 |
57 | func ListenWebsocket(bindAddr string, bindPort int) (*WebsocketListener, error) {
58 | tcpLn, err := net.Listen("tcp", fmt.Sprintf("%s:%d", bindAddr, bindPort))
59 | if err != nil {
60 | return nil, err
61 | }
62 | l := NewWebsocketListener(tcpLn)
63 | return l, nil
64 | }
65 |
66 | func (p *WebsocketListener) Accept() (net.Conn, error) {
67 | c, ok := <-p.acceptCh
68 | if !ok {
69 | return nil, ErrWebsocketListenerClosed
70 | }
71 | return c, nil
72 | }
73 |
74 | func (p *WebsocketListener) Close() error {
75 | return p.server.Close()
76 | }
77 |
78 | func (p *WebsocketListener) Addr() net.Addr {
79 | return p.ln.Addr()
80 | }
81 |
82 | // addr: domain:port
83 | func ConnectWebsocketServer(addr string, websocket_domain string, isSecure bool) (net.Conn, error) {
84 | if isSecure {
85 | ho := strings.Split(addr, ":")
86 | ip, err := net.ResolveIPAddr("ip", ho[0])
87 | ip_addr := ip.String() + ":" + ho[1]
88 | if err != nil {
89 | return nil, err
90 | }
91 | addr = "wss://" + ip_addr + FrpWebsocketPath
92 | } else {
93 | addr = "ws://" + addr + FrpWebsocketPath
94 | }
95 | uri, err := url.Parse(addr)
96 | if err != nil {
97 | return nil, err
98 | }
99 |
100 | var origin string
101 | if isSecure {
102 | ho := strings.Split(uri.Host, ":")
103 | ip, err := net.ResolveIPAddr("ip", ho[0])
104 | ip_addr := ip.String() + ":" + ho[1]
105 | if err != nil {
106 | return nil, err
107 | }
108 | origin = "https://" + ip_addr
109 | } else {
110 | origin = "http://" + uri.Host
111 | }
112 |
113 | cfg, err := websocket.NewConfig(addr, origin)
114 | if err != nil {
115 | return nil, err
116 | }
117 | cfg.Dialer = &net.Dialer{
118 | Timeout: 10 * time.Second,
119 | }
120 |
121 | conn, err := websocket.DialConfig(cfg)
122 | if err != nil {
123 | return nil, err
124 | }
125 | return conn, nil
126 | }
127 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/tcpmux/httpconnect.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 guylewin, guy@lewin.co.il
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package tcpmux
16 |
17 | import (
18 | "bufio"
19 | "fmt"
20 | "io"
21 | "net"
22 | "net/http"
23 | "time"
24 |
25 | "github.com/fatedier/frp/pkg/util/util"
26 | "github.com/fatedier/frp/pkg/util/vhost"
27 | )
28 |
29 | type HTTPConnectTCPMuxer struct {
30 | *vhost.Muxer
31 | }
32 |
33 | func NewHTTPConnectTCPMuxer(listener net.Listener, timeout time.Duration) (*HTTPConnectTCPMuxer, error) {
34 | mux, err := vhost.NewMuxer(listener, getHostFromHTTPConnect, nil, sendHTTPOk, nil, timeout)
35 | return &HTTPConnectTCPMuxer{mux}, err
36 | }
37 |
38 | func readHTTPConnectRequest(rd io.Reader) (host string, err error) {
39 | bufioReader := bufio.NewReader(rd)
40 |
41 | req, err := http.ReadRequest(bufioReader)
42 | if err != nil {
43 | return
44 | }
45 |
46 | if req.Method != "CONNECT" {
47 | err = fmt.Errorf("connections to tcp vhost must be of method CONNECT")
48 | return
49 | }
50 |
51 | host = util.GetHostFromAddr(req.Host)
52 | return
53 | }
54 |
55 | func sendHTTPOk(c net.Conn) error {
56 | return util.OkResponse().Write(c)
57 | }
58 |
59 | func getHostFromHTTPConnect(c net.Conn) (_ net.Conn, _ map[string]string, err error) {
60 | reqInfoMap := make(map[string]string, 0)
61 | host, err := readHTTPConnectRequest(c)
62 | if err != nil {
63 | return nil, reqInfoMap, err
64 | }
65 | reqInfoMap["Host"] = host
66 | reqInfoMap["Scheme"] = "tcp"
67 | return c, reqInfoMap, nil
68 | }
69 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/util/http.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 guylewin, guy@lewin.co.il
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package util
16 |
17 | import (
18 | "net/http"
19 | "strings"
20 | )
21 |
22 | func OkResponse() *http.Response {
23 | header := make(http.Header)
24 |
25 | res := &http.Response{
26 | Status: "OK",
27 | StatusCode: 200,
28 | Proto: "HTTP/1.1",
29 | ProtoMajor: 1,
30 | ProtoMinor: 1,
31 | Header: header,
32 | }
33 | return res
34 | }
35 |
36 | func GetHostFromAddr(addr string) (host string) {
37 | strs := strings.Split(addr, ":")
38 | if len(strs) > 1 {
39 | host = strs[0]
40 | } else {
41 | host = addr
42 | }
43 | return
44 | }
45 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/util/util.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package util
16 |
17 | import (
18 | "crypto/md5"
19 | "crypto/rand"
20 | "encoding/hex"
21 | "fmt"
22 | "strconv"
23 | "strings"
24 | )
25 |
26 | // RandID return a rand string used in frp.
27 | func RandID() (id string, err error) {
28 | return RandIDWithLen(8)
29 | }
30 |
31 | // RandIDWithLen return a rand string with idLen length.
32 | func RandIDWithLen(idLen int) (id string, err error) {
33 | b := make([]byte, idLen)
34 | _, err = rand.Read(b)
35 | if err != nil {
36 | return
37 | }
38 |
39 | id = fmt.Sprintf("%x", b)
40 | return
41 | }
42 |
43 | func GetAuthKey(token string, timestamp int64) (key string) {
44 | token = token + fmt.Sprintf("%d", timestamp)
45 | md5Ctx := md5.New()
46 | md5Ctx.Write([]byte(token))
47 | data := md5Ctx.Sum(nil)
48 | return hex.EncodeToString(data)
49 | }
50 |
51 | func CanonicalAddr(host string, port int) (addr string) {
52 | if port == 80 || port == 443 {
53 | addr = host
54 | } else {
55 | addr = fmt.Sprintf("%s:%d", host, port)
56 | }
57 | return
58 | }
59 |
60 | func ParseRangeNumbers(rangeStr string) (numbers []int64, err error) {
61 | rangeStr = strings.TrimSpace(rangeStr)
62 | numbers = make([]int64, 0)
63 | // e.g. 1000-2000,2001,2002,3000-4000
64 | numRanges := strings.Split(rangeStr, ",")
65 | for _, numRangeStr := range numRanges {
66 | // 1000-2000 or 2001
67 | numArray := strings.Split(numRangeStr, "-")
68 | // length: only 1 or 2 is correct
69 | rangeType := len(numArray)
70 | if rangeType == 1 {
71 | // single number
72 | singleNum, errRet := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64)
73 | if errRet != nil {
74 | err = fmt.Errorf("range number is invalid, %v", errRet)
75 | return
76 | }
77 | numbers = append(numbers, singleNum)
78 | } else if rangeType == 2 {
79 | // range numbers
80 | min, errRet := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64)
81 | if errRet != nil {
82 | err = fmt.Errorf("range number is invalid, %v", errRet)
83 | return
84 | }
85 | max, errRet := strconv.ParseInt(strings.TrimSpace(numArray[1]), 10, 64)
86 | if errRet != nil {
87 | err = fmt.Errorf("range number is invalid, %v", errRet)
88 | return
89 | }
90 | if max < min {
91 | err = fmt.Errorf("range number is invalid")
92 | return
93 | }
94 | for i := min; i <= max; i++ {
95 | numbers = append(numbers, i)
96 | }
97 | } else {
98 | err = fmt.Errorf("range number is invalid")
99 | return
100 | }
101 | }
102 | return
103 | }
104 |
105 | func GenerateResponseErrorString(summary string, err error, detailed bool) string {
106 | if detailed {
107 | return err.Error()
108 | }
109 | return summary
110 | }
111 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/util/util_test.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/assert"
7 | )
8 |
9 | func TestRandId(t *testing.T) {
10 | assert := assert.New(t)
11 | id, err := RandID()
12 | assert.NoError(err)
13 | t.Log(id)
14 | assert.Equal(16, len(id))
15 | }
16 |
17 | func TestGetAuthKey(t *testing.T) {
18 | assert := assert.New(t)
19 | key := GetAuthKey("1234", 1488720000)
20 | t.Log(key)
21 | assert.Equal("6df41a43725f0c770fd56379e12acf8c", key)
22 | }
23 |
24 | func TestParseRangeNumbers(t *testing.T) {
25 | assert := assert.New(t)
26 | numbers, err := ParseRangeNumbers("2-5")
27 | if assert.NoError(err) {
28 | assert.Equal([]int64{2, 3, 4, 5}, numbers)
29 | }
30 |
31 | numbers, err = ParseRangeNumbers("1")
32 | if assert.NoError(err) {
33 | assert.Equal([]int64{1}, numbers)
34 | }
35 |
36 | numbers, err = ParseRangeNumbers("3-5,8")
37 | if assert.NoError(err) {
38 | assert.Equal([]int64{3, 4, 5, 8}, numbers)
39 | }
40 |
41 | numbers, err = ParseRangeNumbers(" 3-5,8, 10-12 ")
42 | if assert.NoError(err) {
43 | assert.Equal([]int64{3, 4, 5, 8, 10, 11, 12}, numbers)
44 | }
45 |
46 | _, err = ParseRangeNumbers("3-a")
47 | assert.Error(err)
48 | }
49 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/version/version.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package version
16 |
17 | import (
18 | "strconv"
19 | "strings"
20 | )
21 |
22 | var version string = "0.34.1"
23 |
24 | func Full() string {
25 | return version
26 | }
27 |
28 | func getSubVersion(v string, position int) int64 {
29 | arr := strings.Split(v, ".")
30 | if len(arr) < 3 {
31 | return 0
32 | }
33 | res, _ := strconv.ParseInt(arr[position], 10, 64)
34 | return res
35 | }
36 |
37 | func Proto(v string) int64 {
38 | return getSubVersion(v, 0)
39 | }
40 |
41 | func Major(v string) int64 {
42 | return getSubVersion(v, 1)
43 | }
44 |
45 | func Minor(v string) int64 {
46 | return getSubVersion(v, 2)
47 | }
48 |
49 | // add every case there if server will not accept client's protocol and return false
50 | func Compat(client string) (ok bool, msg string) {
51 | if LessThan(client, "0.18.0") {
52 | return false, "Please upgrade your frpc version to at least 0.18.0"
53 | }
54 | return true, ""
55 | }
56 |
57 | func LessThan(client string, server string) bool {
58 | vc := Proto(client)
59 | vs := Proto(server)
60 | if vc > vs {
61 | return false
62 | } else if vc < vs {
63 | return true
64 | }
65 |
66 | vc = Major(client)
67 | vs = Major(server)
68 | if vc > vs {
69 | return false
70 | } else if vc < vs {
71 | return true
72 | }
73 |
74 | vc = Minor(client)
75 | vs = Minor(server)
76 | if vc > vs {
77 | return false
78 | } else if vc < vs {
79 | return true
80 | }
81 | return false
82 | }
83 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/version/version_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package version
16 |
17 | import (
18 | "fmt"
19 | "strconv"
20 | "strings"
21 | "testing"
22 |
23 | "github.com/stretchr/testify/assert"
24 | )
25 |
26 | func TestFull(t *testing.T) {
27 | assert := assert.New(t)
28 | version := Full()
29 | arr := strings.Split(version, ".")
30 | assert.Equal(3, len(arr))
31 |
32 | proto, err := strconv.ParseInt(arr[0], 10, 64)
33 | assert.NoError(err)
34 | assert.True(proto >= 0)
35 |
36 | major, err := strconv.ParseInt(arr[1], 10, 64)
37 | assert.NoError(err)
38 | assert.True(major >= 0)
39 |
40 | minor, err := strconv.ParseInt(arr[2], 10, 64)
41 | assert.NoError(err)
42 | assert.True(minor >= 0)
43 | }
44 |
45 | func TestVersion(t *testing.T) {
46 | assert := assert.New(t)
47 | proto := Proto(Full())
48 | major := Major(Full())
49 | minor := Minor(Full())
50 | parseVerion := fmt.Sprintf("%d.%d.%d", proto, major, minor)
51 | version := Full()
52 | assert.Equal(parseVerion, version)
53 | }
54 |
55 | func TestCompact(t *testing.T) {
56 | assert := assert.New(t)
57 | ok, _ := Compat("0.9.0")
58 | assert.False(ok)
59 |
60 | ok, _ = Compat("10.0.0")
61 | assert.True(ok)
62 |
63 | ok, _ = Compat("0.10.0")
64 | assert.False(ok)
65 | }
66 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/vhost/https.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package vhost
16 |
17 | import (
18 | "fmt"
19 | "io"
20 | "net"
21 | "strings"
22 | "time"
23 |
24 | gnet "github.com/fatedier/golib/net"
25 | "github.com/fatedier/golib/pool"
26 | )
27 |
28 | const (
29 | typeClientHello uint8 = 1 // Type client hello
30 | )
31 |
32 | // TLS extension numbers
33 | const (
34 | extensionServerName uint16 = 0
35 | extensionStatusRequest uint16 = 5
36 | extensionSupportedCurves uint16 = 10
37 | extensionSupportedPoints uint16 = 11
38 | extensionSignatureAlgorithms uint16 = 13
39 | extensionALPN uint16 = 16
40 | extensionSCT uint16 = 18
41 | extensionSessionTicket uint16 = 35
42 | extensionNextProtoNeg uint16 = 13172 // not IANA assigned
43 | extensionRenegotiationInfo uint16 = 0xff01
44 | )
45 |
46 | type HTTPSMuxer struct {
47 | *Muxer
48 | }
49 |
50 | func NewHTTPSMuxer(listener net.Listener, timeout time.Duration) (*HTTPSMuxer, error) {
51 | mux, err := NewMuxer(listener, GetHTTPSHostname, nil, nil, nil, timeout)
52 | return &HTTPSMuxer{mux}, err
53 | }
54 |
55 | func readHandshake(rd io.Reader) (host string, err error) {
56 | data := pool.GetBuf(1024)
57 | origin := data
58 | defer pool.PutBuf(origin)
59 |
60 | _, err = io.ReadFull(rd, data[:47])
61 | if err != nil {
62 | return
63 | }
64 |
65 | length, err := rd.Read(data[47:])
66 | if err != nil {
67 | return
68 | }
69 | length += 47
70 | data = data[:length]
71 | if uint8(data[5]) != typeClientHello {
72 | err = fmt.Errorf("readHandshake: type[%d] is not clientHello", uint16(data[5]))
73 | return
74 | }
75 |
76 | // session
77 | sessionIDLen := int(data[43])
78 | if sessionIDLen > 32 || len(data) < 44+sessionIDLen {
79 | err = fmt.Errorf("readHandshake: sessionIdLen[%d] is long", sessionIDLen)
80 | return
81 | }
82 | data = data[44+sessionIDLen:]
83 | if len(data) < 2 {
84 | err = fmt.Errorf("readHandshake: dataLen[%d] after session is short", len(data))
85 | return
86 | }
87 |
88 | // cipher suite numbers
89 | cipherSuiteLen := int(data[0])<<8 | int(data[1])
90 | if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
91 | err = fmt.Errorf("readHandshake: dataLen[%d] after cipher suite is short", len(data))
92 | return
93 | }
94 | data = data[2+cipherSuiteLen:]
95 | if len(data) < 1 {
96 | err = fmt.Errorf("readHandshake: cipherSuiteLen[%d] is long", cipherSuiteLen)
97 | return
98 | }
99 |
100 | // compression method
101 | compressionMethodsLen := int(data[0])
102 | if len(data) < 1+compressionMethodsLen {
103 | err = fmt.Errorf("readHandshake: compressionMethodsLen[%d] is long", compressionMethodsLen)
104 | return
105 | }
106 |
107 | data = data[1+compressionMethodsLen:]
108 | if len(data) == 0 {
109 | // ClientHello is optionally followed by extension data
110 | err = fmt.Errorf("readHandshake: there is no extension data to get servername")
111 | return
112 | }
113 | if len(data) < 2 {
114 | err = fmt.Errorf("readHandshake: extension dataLen[%d] is too short", len(data))
115 | return
116 | }
117 |
118 | extensionsLength := int(data[0])<<8 | int(data[1])
119 | data = data[2:]
120 | if extensionsLength != len(data) {
121 | err = fmt.Errorf("readHandshake: extensionsLen[%d] is not equal to dataLen[%d]", extensionsLength, len(data))
122 | return
123 | }
124 | for len(data) != 0 {
125 | if len(data) < 4 {
126 | err = fmt.Errorf("readHandshake: extensionsDataLen[%d] is too short", len(data))
127 | return
128 | }
129 | extension := uint16(data[0])<<8 | uint16(data[1])
130 | length := int(data[2])<<8 | int(data[3])
131 | data = data[4:]
132 | if len(data) < length {
133 | err = fmt.Errorf("readHandshake: extensionLen[%d] is long", length)
134 | return
135 | }
136 |
137 | switch extension {
138 | case extensionRenegotiationInfo:
139 | if length != 1 || data[0] != 0 {
140 | err = fmt.Errorf("readHandshake: extension reNegotiationInfoLen[%d] is short", length)
141 | return
142 | }
143 | case extensionNextProtoNeg:
144 | case extensionStatusRequest:
145 | case extensionServerName:
146 | d := data[:length]
147 | if len(d) < 2 {
148 | err = fmt.Errorf("readHandshake: remiaining dataLen[%d] is short", len(d))
149 | return
150 | }
151 | namesLen := int(d[0])<<8 | int(d[1])
152 | d = d[2:]
153 | if len(d) != namesLen {
154 | err = fmt.Errorf("readHandshake: nameListLen[%d] is not equal to dataLen[%d]", namesLen, len(d))
155 | return
156 | }
157 | for len(d) > 0 {
158 | if len(d) < 3 {
159 | err = fmt.Errorf("readHandshake: extension serverNameLen[%d] is short", len(d))
160 | return
161 | }
162 | nameType := d[0]
163 | nameLen := int(d[1])<<8 | int(d[2])
164 | d = d[3:]
165 | if len(d) < nameLen {
166 | err = fmt.Errorf("readHandshake: nameLen[%d] is not equal to dataLen[%d]", nameLen, len(d))
167 | return
168 | }
169 | if nameType == 0 {
170 | serverName := string(d[:nameLen])
171 | host = strings.TrimSpace(serverName)
172 | return host, nil
173 | }
174 | d = d[nameLen:]
175 | }
176 | }
177 | data = data[length:]
178 | }
179 | err = fmt.Errorf("Unknown error")
180 | return
181 | }
182 |
183 | func GetHTTPSHostname(c net.Conn) (_ net.Conn, _ map[string]string, err error) {
184 | reqInfoMap := make(map[string]string, 0)
185 | sc, rd := gnet.NewSharedConn(c)
186 | host, err := readHandshake(rd)
187 | if err != nil {
188 | return nil, reqInfoMap, err
189 | }
190 | reqInfoMap["Host"] = host
191 | reqInfoMap["Scheme"] = "https"
192 | return sc, reqInfoMap, nil
193 | }
194 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/vhost/resource.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package vhost
16 |
17 | import (
18 | "bytes"
19 | "io/ioutil"
20 | "net/http"
21 |
22 | frpLog "github.com/fatedier/frp/pkg/util/log"
23 | //"github.com/fatedier/frp/pkg/util/version"
24 | )
25 |
26 | var (
27 | NotFoundPagePath = ""
28 | )
29 |
30 | const (
31 | NotFound = `
32 |
33 |
34 | Not Found
35 |
42 |
43 |
44 | The page you requested was not found.
45 | Sorry, the page you are looking for is currently unavailable.
46 | Please try again later.
47 | The server is powered by frp.
48 | Faithfully yours, frp.
49 |
50 |
51 | `
52 | )
53 |
54 | func getNotFoundPageContent() []byte {
55 | var (
56 | buf []byte
57 | err error
58 | )
59 | if NotFoundPagePath != "" {
60 | buf, err = ioutil.ReadFile(NotFoundPagePath)
61 | if err != nil {
62 | frpLog.Warn("read custom 404 page error: %v", err)
63 | buf = []byte(NotFound)
64 | }
65 | } else {
66 | buf = []byte(NotFound)
67 | }
68 | return buf
69 | }
70 |
71 | func notFoundResponse() *http.Response {
72 | header := make(http.Header)
73 | header.Set("server", "nginx")
74 | header.Set("Content-Type", "text/html")
75 |
76 | res := &http.Response{
77 | Status: "Not Found",
78 | StatusCode: 404,
79 | Proto: "HTTP/1.0",
80 | ProtoMajor: 1,
81 | ProtoMinor: 0,
82 | Header: header,
83 | Body: ioutil.NopCloser(bytes.NewReader(getNotFoundPageContent())),
84 | }
85 | return res
86 | }
87 |
88 | func noAuthResponse() *http.Response {
89 | header := make(map[string][]string)
90 | header["WWW-Authenticate"] = []string{`Basic realm="Restricted"`}
91 | res := &http.Response{
92 | Status: "401 Not authorized",
93 | StatusCode: 401,
94 | Proto: "HTTP/1.1",
95 | ProtoMajor: 1,
96 | ProtoMinor: 1,
97 | Header: header,
98 | }
99 | return res
100 | }
101 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/vhost/router.go:
--------------------------------------------------------------------------------
1 | package vhost
2 |
3 | import (
4 | "errors"
5 | "sort"
6 | "strings"
7 | "sync"
8 | )
9 |
10 | var (
11 | ErrRouterConfigConflict = errors.New("router config conflict")
12 | )
13 |
14 | type Routers struct {
15 | RouterByDomain map[string][]*Router
16 | mutex sync.RWMutex
17 | }
18 |
19 | type Router struct {
20 | domain string
21 | location string
22 |
23 | payload interface{}
24 | }
25 |
26 | func NewRouters() *Routers {
27 | return &Routers{
28 | RouterByDomain: make(map[string][]*Router),
29 | }
30 | }
31 |
32 | func (r *Routers) Add(domain, location string, payload interface{}) error {
33 | r.mutex.Lock()
34 | defer r.mutex.Unlock()
35 |
36 | if _, exist := r.exist(domain, location); exist {
37 | return ErrRouterConfigConflict
38 | }
39 |
40 | vrs, found := r.RouterByDomain[domain]
41 | if !found {
42 | vrs = make([]*Router, 0, 1)
43 | }
44 |
45 | vr := &Router{
46 | domain: domain,
47 | location: location,
48 | payload: payload,
49 | }
50 | vrs = append(vrs, vr)
51 |
52 | sort.Sort(sort.Reverse(ByLocation(vrs)))
53 | r.RouterByDomain[domain] = vrs
54 | return nil
55 | }
56 |
57 | func (r *Routers) Del(domain, location string) {
58 | r.mutex.Lock()
59 | defer r.mutex.Unlock()
60 |
61 | vrs, found := r.RouterByDomain[domain]
62 | if !found {
63 | return
64 | }
65 | newVrs := make([]*Router, 0)
66 | for _, vr := range vrs {
67 | if vr.location != location {
68 | newVrs = append(newVrs, vr)
69 | }
70 | }
71 | r.RouterByDomain[domain] = newVrs
72 | }
73 |
74 | func (r *Routers) Get(host, path string) (vr *Router, exist bool) {
75 | r.mutex.RLock()
76 | defer r.mutex.RUnlock()
77 |
78 | vrs, found := r.RouterByDomain[host]
79 | if !found {
80 | return
81 | }
82 |
83 | // can't support load balance, will to do
84 | for _, vr = range vrs {
85 | if strings.HasPrefix(path, vr.location) {
86 | return vr, true
87 | }
88 | }
89 |
90 | return
91 | }
92 |
93 | func (r *Routers) exist(host, path string) (vr *Router, exist bool) {
94 | vrs, found := r.RouterByDomain[host]
95 | if !found {
96 | return
97 | }
98 |
99 | for _, vr = range vrs {
100 | if path == vr.location {
101 | return vr, true
102 | }
103 | }
104 |
105 | return
106 | }
107 |
108 | // sort by location
109 | type ByLocation []*Router
110 |
111 | func (a ByLocation) Len() int {
112 | return len(a)
113 | }
114 | func (a ByLocation) Swap(i, j int) {
115 | a[i], a[j] = a[j], a[i]
116 | }
117 | func (a ByLocation) Less(i, j int) bool {
118 | return strings.Compare(a[i].location, a[j].location) < 0
119 | }
120 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/xlog/ctx.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package xlog
16 |
17 | import (
18 | "context"
19 | )
20 |
21 | type key int
22 |
23 | const (
24 | xlogKey key = 0
25 | )
26 |
27 | func NewContext(ctx context.Context, xl *Logger) context.Context {
28 | return context.WithValue(ctx, xlogKey, xl)
29 | }
30 |
31 | func FromContext(ctx context.Context) (xl *Logger, ok bool) {
32 | xl, ok = ctx.Value(xlogKey).(*Logger)
33 | return
34 | }
35 |
36 | func FromContextSafe(ctx context.Context) *Logger {
37 | xl, ok := ctx.Value(xlogKey).(*Logger)
38 | if !ok {
39 | xl = New()
40 | }
41 | return xl
42 | }
43 |
--------------------------------------------------------------------------------
/FRPsource/pkg/util/xlog/xlog.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package xlog
16 |
17 | import (
18 | "github.com/fatedier/frp/pkg/util/log"
19 | )
20 |
21 | // Logger is not thread safety for operations on prefix
22 | type Logger struct {
23 | prefixes []string
24 |
25 | prefixString string
26 | }
27 |
28 | func New() *Logger {
29 | return &Logger{
30 | prefixes: make([]string, 0),
31 | }
32 | }
33 |
34 | func (l *Logger) ResetPrefixes() (old []string) {
35 | old = l.prefixes
36 | l.prefixes = make([]string, 0)
37 | l.prefixString = ""
38 | return
39 | }
40 |
41 | func (l *Logger) AppendPrefix(prefix string) *Logger {
42 | l.prefixes = append(l.prefixes, prefix)
43 | l.prefixString += "[" + prefix + "] "
44 | return l
45 | }
46 |
47 | func (l *Logger) Spawn() *Logger {
48 | nl := New()
49 | for _, v := range l.prefixes {
50 | nl.AppendPrefix(v)
51 | }
52 | return nl
53 | }
54 |
55 | func (l *Logger) Error(format string, v ...interface{}) {
56 | log.Log.Error(l.prefixString+format, v...)
57 | }
58 |
59 | func (l *Logger) Warn(format string, v ...interface{}) {
60 | log.Log.Warn(l.prefixString+format, v...)
61 | }
62 |
63 | func (l *Logger) Info(format string, v ...interface{}) {
64 | log.Log.Info(l.prefixString+format, v...)
65 | }
66 |
67 | func (l *Logger) Debug(format string, v ...interface{}) {
68 | log.Log.Debug(l.prefixString+format, v...)
69 | }
70 |
71 | func (l *Logger) Trace(format string, v ...interface{}) {
72 | log.Log.Trace(l.prefixString+format, v...)
73 | }
74 |
--------------------------------------------------------------------------------
/FRPsource/server/controller/resource.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package controller
16 |
17 | import (
18 | "github.com/fatedier/frp/pkg/nathole"
19 | plugin "github.com/fatedier/frp/pkg/plugin/server"
20 | "github.com/fatedier/frp/pkg/util/tcpmux"
21 | "github.com/fatedier/frp/pkg/util/vhost"
22 |
23 | //"github.com/fatedier/frp/pkg/util/tcpmux"
24 | //"github.com/fatedier/frp/pkg/util/vhost"
25 | "github.com/fatedier/frp/server/group"
26 | "github.com/fatedier/frp/server/ports"
27 | "github.com/fatedier/frp/server/visitor"
28 | )
29 |
30 | // All resource managers and controllers
31 | type ResourceController struct {
32 | // Manage all visitor listeners
33 | VisitorManager *visitor.Manager
34 |
35 | // TCP Group Controller
36 | TCPGroupCtl *group.TCPGroupCtl
37 |
38 | // HTTP Group Controller
39 | HTTPGroupCtl *group.HTTPGroupController
40 |
41 | // TCP Mux Group Controller
42 | TCPMuxGroupCtl *group.TCPMuxGroupCtl
43 |
44 | // Manage all TCP ports
45 | TCPPortManager *ports.Manager
46 |
47 | // Manage all UDP ports
48 | UDPPortManager *ports.Manager
49 |
50 | // For HTTP proxies, forwarding HTTP requests
51 | HTTPReverseProxy *vhost.HTTPReverseProxy
52 |
53 | // For HTTPS proxies, route requests to different clients by hostname and other information
54 | VhostHTTPSMuxer *vhost.HTTPSMuxer
55 |
56 | // Controller for nat hole connections
57 | NatHoleController *nathole.Controller
58 |
59 | // TCPMux HTTP CONNECT multiplexer
60 | TCPMuxHTTPConnectMuxer *tcpmux.HTTPConnectTCPMuxer
61 |
62 | // All server manager plugin
63 | PluginManager *plugin.Manager
64 | }
65 |
--------------------------------------------------------------------------------
/FRPsource/server/dashboard.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package server
16 |
17 | import (
18 | "fmt"
19 | "net"
20 | "net/http"
21 | "time"
22 |
23 | "github.com/fatedier/frp/assets"
24 | frpNet "github.com/fatedier/frp/pkg/util/net"
25 |
26 | "github.com/gorilla/mux"
27 | "github.com/prometheus/client_golang/prometheus/promhttp"
28 | )
29 |
30 | var (
31 | httpServerReadTimeout = 10 * time.Second
32 | httpServerWriteTimeout = 10 * time.Second
33 | )
34 |
35 | func (svr *Service) RunDashboardServer(addr string, port int) (err error) {
36 | // url router
37 | router := mux.NewRouter()
38 |
39 | user, passwd := svr.cfg.DashboardUser, svr.cfg.DashboardPwd
40 | router.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware)
41 |
42 | // metrics
43 | if svr.cfg.EnablePrometheus {
44 | router.Handle("/metrics", promhttp.Handler())
45 | }
46 |
47 | // api, see dashboard_api.go
48 | router.HandleFunc("/api/serverinfo", svr.APIServerInfo).Methods("GET")
49 | router.HandleFunc("/api/proxy/{type}", svr.APIProxyByType).Methods("GET")
50 | router.HandleFunc("/api/proxy/{type}/{name}", svr.APIProxyByTypeAndName).Methods("GET")
51 | router.HandleFunc("/api/traffic/{name}", svr.APIProxyTraffic).Methods("GET")
52 |
53 | // view
54 | router.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET")
55 | router.PathPrefix("/static/").Handler(frpNet.MakeHTTPGzipHandler(http.StripPrefix("/static/", http.FileServer(assets.FileSystem)))).Methods("GET")
56 |
57 | router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
58 | http.Redirect(w, r, "/static/", http.StatusMovedPermanently)
59 | })
60 |
61 | address := fmt.Sprintf("%s:%d", addr, port)
62 | server := &http.Server{
63 | Addr: address,
64 | Handler: router,
65 | ReadTimeout: httpServerReadTimeout,
66 | WriteTimeout: httpServerWriteTimeout,
67 | }
68 | if address == "" {
69 | address = ":http"
70 | }
71 | ln, err := net.Listen("tcp", address)
72 | if err != nil {
73 | return err
74 | }
75 |
76 | go server.Serve(ln)
77 | return
78 | }
79 |
--------------------------------------------------------------------------------
/FRPsource/server/group/group.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package group
16 |
17 | import (
18 | "errors"
19 | )
20 |
21 | var (
22 | ErrGroupAuthFailed = errors.New("group auth failed")
23 | ErrGroupParamsInvalid = errors.New("group params invalid")
24 | ErrListenerClosed = errors.New("group listener closed")
25 | ErrGroupDifferentPort = errors.New("group should have same remote port")
26 | ErrProxyRepeated = errors.New("group proxy repeated")
27 | )
28 |
--------------------------------------------------------------------------------
/FRPsource/server/group/http.go:
--------------------------------------------------------------------------------
1 | package group
2 |
3 | import (
4 | "fmt"
5 | "github.com/fatedier/frp/pkg/util/vhost"
6 | "net"
7 | "sync"
8 | "sync/atomic"
9 | //"github.com/fatedier/frp/pkg/util/vhost"
10 | )
11 |
12 | type HTTPGroupController struct {
13 | groups map[string]*HTTPGroup
14 |
15 | vhostRouter *vhost.Routers
16 |
17 | mu sync.Mutex
18 | }
19 |
20 | func NewHTTPGroupController(vhostRouter *vhost.Routers) *HTTPGroupController {
21 | return &HTTPGroupController{
22 | groups: make(map[string]*HTTPGroup),
23 | vhostRouter: vhostRouter,
24 | }
25 | }
26 |
27 | func (ctl *HTTPGroupController) Register(proxyName, group, groupKey string,
28 | routeConfig vhost.RouteConfig) (err error) {
29 |
30 | indexKey := httpGroupIndex(group, routeConfig.Domain, routeConfig.Location)
31 | ctl.mu.Lock()
32 | g, ok := ctl.groups[indexKey]
33 | if !ok {
34 | g = NewHTTPGroup(ctl)
35 | ctl.groups[indexKey] = g
36 | }
37 | ctl.mu.Unlock()
38 |
39 | return g.Register(proxyName, group, groupKey, routeConfig)
40 | }
41 |
42 | func (ctl *HTTPGroupController) UnRegister(proxyName, group, domain, location string) {
43 | indexKey := httpGroupIndex(group, domain, location)
44 | ctl.mu.Lock()
45 | defer ctl.mu.Unlock()
46 | g, ok := ctl.groups[indexKey]
47 | if !ok {
48 | return
49 | }
50 |
51 | isEmpty := g.UnRegister(proxyName)
52 | if isEmpty {
53 | delete(ctl.groups, indexKey)
54 | }
55 | }
56 |
57 | type HTTPGroup struct {
58 | group string
59 | groupKey string
60 | domain string
61 | location string
62 |
63 | createFuncs map[string]vhost.CreateConnFunc
64 | pxyNames []string
65 | index uint64
66 | ctl *HTTPGroupController
67 | mu sync.RWMutex
68 | }
69 |
70 | func NewHTTPGroup(ctl *HTTPGroupController) *HTTPGroup {
71 | return &HTTPGroup{
72 | createFuncs: make(map[string]vhost.CreateConnFunc),
73 | pxyNames: make([]string, 0),
74 | ctl: ctl,
75 | }
76 | }
77 |
78 | func (g *HTTPGroup) Register(proxyName, group, groupKey string,
79 | routeConfig vhost.RouteConfig) (err error) {
80 |
81 | g.mu.Lock()
82 | defer g.mu.Unlock()
83 | if len(g.createFuncs) == 0 {
84 | // the first proxy in this group
85 | tmp := routeConfig // copy object
86 | tmp.CreateConnFn = g.createConn
87 | err = g.ctl.vhostRouter.Add(routeConfig.Domain, routeConfig.Location, &tmp)
88 | if err != nil {
89 | return
90 | }
91 |
92 | g.group = group
93 | g.groupKey = groupKey
94 | g.domain = routeConfig.Domain
95 | g.location = routeConfig.Location
96 | } else {
97 | if g.group != group || g.domain != routeConfig.Domain || g.location != routeConfig.Location {
98 | err = ErrGroupParamsInvalid
99 | return
100 | }
101 | if g.groupKey != groupKey {
102 | err = ErrGroupAuthFailed
103 | return
104 | }
105 | }
106 | if _, ok := g.createFuncs[proxyName]; ok {
107 | err = ErrProxyRepeated
108 | return
109 | }
110 | g.createFuncs[proxyName] = routeConfig.CreateConnFn
111 | g.pxyNames = append(g.pxyNames, proxyName)
112 | return nil
113 | }
114 |
115 | func (g *HTTPGroup) UnRegister(proxyName string) (isEmpty bool) {
116 | g.mu.Lock()
117 | defer g.mu.Unlock()
118 | delete(g.createFuncs, proxyName)
119 | for i, name := range g.pxyNames {
120 | if name == proxyName {
121 | g.pxyNames = append(g.pxyNames[:i], g.pxyNames[i+1:]...)
122 | break
123 | }
124 | }
125 |
126 | if len(g.createFuncs) == 0 {
127 | isEmpty = true
128 | g.ctl.vhostRouter.Del(g.domain, g.location)
129 | }
130 | return
131 | }
132 |
133 | func (g *HTTPGroup) createConn(remoteAddr string) (net.Conn, error) {
134 | var f vhost.CreateConnFunc
135 | newIndex := atomic.AddUint64(&g.index, 1)
136 |
137 | g.mu.RLock()
138 | group := g.group
139 | domain := g.domain
140 | location := g.location
141 | if len(g.pxyNames) > 0 {
142 | name := g.pxyNames[int(newIndex)%len(g.pxyNames)]
143 | f, _ = g.createFuncs[name]
144 | }
145 | g.mu.RUnlock()
146 |
147 | if f == nil {
148 | return nil, fmt.Errorf("no CreateConnFunc for http group [%s], domain [%s], location [%s]", group, domain, location)
149 | }
150 |
151 | return f(remoteAddr)
152 | }
153 |
154 | func httpGroupIndex(group, domain, location string) string {
155 | return fmt.Sprintf("%s_%s_%s", group, domain, location)
156 | }
157 |
--------------------------------------------------------------------------------
/FRPsource/server/group/tcp.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package group
16 |
17 | import (
18 | "fmt"
19 | "net"
20 | "sync"
21 |
22 | "github.com/fatedier/frp/server/ports"
23 |
24 | gerr "github.com/fatedier/golib/errors"
25 | )
26 |
27 | // TCPGroupCtl manage all TCPGroups
28 | type TCPGroupCtl struct {
29 | groups map[string]*TCPGroup
30 |
31 | // portManager is used to manage port
32 | portManager *ports.Manager
33 | mu sync.Mutex
34 | }
35 |
36 | // NewTCPGroupCtl return a new TcpGroupCtl
37 | func NewTCPGroupCtl(portManager *ports.Manager) *TCPGroupCtl {
38 | return &TCPGroupCtl{
39 | groups: make(map[string]*TCPGroup),
40 | portManager: portManager,
41 | }
42 | }
43 |
44 | // Listen is the wrapper for TCPGroup's Listen
45 | // If there are no group, we will create one here
46 | func (tgc *TCPGroupCtl) Listen(proxyName string, group string, groupKey string,
47 | addr string, port int) (l net.Listener, realPort int, err error) {
48 |
49 | tgc.mu.Lock()
50 | tcpGroup, ok := tgc.groups[group]
51 | if !ok {
52 | tcpGroup = NewTCPGroup(tgc)
53 | tgc.groups[group] = tcpGroup
54 | }
55 | tgc.mu.Unlock()
56 |
57 | return tcpGroup.Listen(proxyName, group, groupKey, addr, port)
58 | }
59 |
60 | // RemoveGroup remove TCPGroup from controller
61 | func (tgc *TCPGroupCtl) RemoveGroup(group string) {
62 | tgc.mu.Lock()
63 | defer tgc.mu.Unlock()
64 | delete(tgc.groups, group)
65 | }
66 |
67 | // TCPGroup route connections to different proxies
68 | type TCPGroup struct {
69 | group string
70 | groupKey string
71 | addr string
72 | port int
73 | realPort int
74 |
75 | acceptCh chan net.Conn
76 | index uint64
77 | tcpLn net.Listener
78 | lns []*TCPGroupListener
79 | ctl *TCPGroupCtl
80 | mu sync.Mutex
81 | }
82 |
83 | // NewTCPGroup return a new TCPGroup
84 | func NewTCPGroup(ctl *TCPGroupCtl) *TCPGroup {
85 | return &TCPGroup{
86 | lns: make([]*TCPGroupListener, 0),
87 | ctl: ctl,
88 | acceptCh: make(chan net.Conn),
89 | }
90 | }
91 |
92 | // Listen will return a new TCPGroupListener
93 | // if TCPGroup already has a listener, just add a new TCPGroupListener to the queues
94 | // otherwise, listen on the real address
95 | func (tg *TCPGroup) Listen(proxyName string, group string, groupKey string, addr string, port int) (ln *TCPGroupListener, realPort int, err error) {
96 | tg.mu.Lock()
97 | defer tg.mu.Unlock()
98 | if len(tg.lns) == 0 {
99 | // the first listener, listen on the real address
100 | realPort, err = tg.ctl.portManager.Acquire(proxyName, port)
101 | if err != nil {
102 | return
103 | }
104 | tcpLn, errRet := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port))
105 | if errRet != nil {
106 | err = errRet
107 | return
108 | }
109 | ln = newTCPGroupListener(group, tg, tcpLn.Addr())
110 |
111 | tg.group = group
112 | tg.groupKey = groupKey
113 | tg.addr = addr
114 | tg.port = port
115 | tg.realPort = realPort
116 | tg.tcpLn = tcpLn
117 | tg.lns = append(tg.lns, ln)
118 | if tg.acceptCh == nil {
119 | tg.acceptCh = make(chan net.Conn)
120 | }
121 | go tg.worker()
122 | } else {
123 | // address and port in the same group must be equal
124 | if tg.group != group || tg.addr != addr {
125 | err = ErrGroupParamsInvalid
126 | return
127 | }
128 | if tg.port != port {
129 | err = ErrGroupDifferentPort
130 | return
131 | }
132 | if tg.groupKey != groupKey {
133 | err = ErrGroupAuthFailed
134 | return
135 | }
136 | ln = newTCPGroupListener(group, tg, tg.lns[0].Addr())
137 | realPort = tg.realPort
138 | tg.lns = append(tg.lns, ln)
139 | }
140 | return
141 | }
142 |
143 | // worker is called when the real tcp listener has been created
144 | func (tg *TCPGroup) worker() {
145 | for {
146 | c, err := tg.tcpLn.Accept()
147 | if err != nil {
148 | return
149 | }
150 | err = gerr.PanicToError(func() {
151 | tg.acceptCh <- c
152 | })
153 | if err != nil {
154 | return
155 | }
156 | }
157 | }
158 |
159 | func (tg *TCPGroup) Accept() <-chan net.Conn {
160 | return tg.acceptCh
161 | }
162 |
163 | // CloseListener remove the TCPGroupListener from the TCPGroup
164 | func (tg *TCPGroup) CloseListener(ln *TCPGroupListener) {
165 | tg.mu.Lock()
166 | defer tg.mu.Unlock()
167 | for i, tmpLn := range tg.lns {
168 | if tmpLn == ln {
169 | tg.lns = append(tg.lns[:i], tg.lns[i+1:]...)
170 | break
171 | }
172 | }
173 | if len(tg.lns) == 0 {
174 | close(tg.acceptCh)
175 | tg.tcpLn.Close()
176 | tg.ctl.portManager.Release(tg.realPort)
177 | tg.ctl.RemoveGroup(tg.group)
178 | }
179 | }
180 |
181 | // TCPGroupListener
182 | type TCPGroupListener struct {
183 | groupName string
184 | group *TCPGroup
185 |
186 | addr net.Addr
187 | closeCh chan struct{}
188 | }
189 |
190 | func newTCPGroupListener(name string, group *TCPGroup, addr net.Addr) *TCPGroupListener {
191 | return &TCPGroupListener{
192 | groupName: name,
193 | group: group,
194 | addr: addr,
195 | closeCh: make(chan struct{}),
196 | }
197 | }
198 |
199 | // Accept will accept connections from TCPGroup
200 | func (ln *TCPGroupListener) Accept() (c net.Conn, err error) {
201 | var ok bool
202 | select {
203 | case <-ln.closeCh:
204 | return nil, ErrListenerClosed
205 | case c, ok = <-ln.group.Accept():
206 | if !ok {
207 | return nil, ErrListenerClosed
208 | }
209 | return c, nil
210 | }
211 | }
212 |
213 | func (ln *TCPGroupListener) Addr() net.Addr {
214 | return ln.addr
215 | }
216 |
217 | // Close close the listener
218 | func (ln *TCPGroupListener) Close() (err error) {
219 | close(ln.closeCh)
220 |
221 | // remove self from TcpGroup
222 | ln.group.CloseListener(ln)
223 | return
224 | }
225 |
--------------------------------------------------------------------------------
/FRPsource/server/group/tcpmux.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 guylewin, guy@lewin.co.il
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package group
16 |
17 | import (
18 | "context"
19 | "fmt"
20 | "net"
21 | "sync"
22 |
23 | "github.com/fatedier/frp/pkg/consts"
24 | "github.com/fatedier/frp/pkg/util/tcpmux"
25 | "github.com/fatedier/frp/pkg/util/vhost"
26 |
27 | gerr "github.com/fatedier/golib/errors"
28 | )
29 |
30 | // TCPMuxGroupCtl manage all TCPMuxGroups
31 | type TCPMuxGroupCtl struct {
32 | groups map[string]*TCPMuxGroup
33 |
34 | // portManager is used to manage port
35 | tcpMuxHTTPConnectMuxer *tcpmux.HTTPConnectTCPMuxer
36 | mu sync.Mutex
37 | }
38 |
39 | // NewTCPMuxGroupCtl return a new TCPMuxGroupCtl
40 | func NewTCPMuxGroupCtl(tcpMuxHTTPConnectMuxer *tcpmux.HTTPConnectTCPMuxer) *TCPMuxGroupCtl {
41 | return &TCPMuxGroupCtl{
42 | groups: make(map[string]*TCPMuxGroup),
43 | tcpMuxHTTPConnectMuxer: tcpMuxHTTPConnectMuxer,
44 | }
45 | }
46 |
47 | // Listen is the wrapper for TCPMuxGroup's Listen
48 | // If there are no group, we will create one here
49 | func (tmgc *TCPMuxGroupCtl) Listen(ctx context.Context, multiplexer string, group string, groupKey string,
50 | domain string) (l net.Listener, err error) {
51 |
52 | tmgc.mu.Lock()
53 | tcpMuxGroup, ok := tmgc.groups[group]
54 | if !ok {
55 | tcpMuxGroup = NewTCPMuxGroup(tmgc)
56 | tmgc.groups[group] = tcpMuxGroup
57 | }
58 | tmgc.mu.Unlock()
59 |
60 | switch multiplexer {
61 | case consts.HTTPConnectTCPMultiplexer:
62 | return tcpMuxGroup.HTTPConnectListen(ctx, group, groupKey, domain)
63 | default:
64 | err = fmt.Errorf("unknown multiplexer [%s]", multiplexer)
65 | return
66 | }
67 | }
68 |
69 | // RemoveGroup remove TCPMuxGroup from controller
70 | func (tmgc *TCPMuxGroupCtl) RemoveGroup(group string) {
71 | tmgc.mu.Lock()
72 | defer tmgc.mu.Unlock()
73 | delete(tmgc.groups, group)
74 | }
75 |
76 | // TCPMuxGroup route connections to different proxies
77 | type TCPMuxGroup struct {
78 | group string
79 | groupKey string
80 | domain string
81 |
82 | acceptCh chan net.Conn
83 | index uint64
84 | tcpMuxLn net.Listener
85 | lns []*TCPMuxGroupListener
86 | ctl *TCPMuxGroupCtl
87 | mu sync.Mutex
88 | }
89 |
90 | // NewTCPMuxGroup return a new TCPMuxGroup
91 | func NewTCPMuxGroup(ctl *TCPMuxGroupCtl) *TCPMuxGroup {
92 | return &TCPMuxGroup{
93 | lns: make([]*TCPMuxGroupListener, 0),
94 | ctl: ctl,
95 | acceptCh: make(chan net.Conn),
96 | }
97 | }
98 |
99 | // Listen will return a new TCPMuxGroupListener
100 | // if TCPMuxGroup already has a listener, just add a new TCPMuxGroupListener to the queues
101 | // otherwise, listen on the real address
102 | func (tmg *TCPMuxGroup) HTTPConnectListen(ctx context.Context, group string, groupKey string, domain string) (ln *TCPMuxGroupListener, err error) {
103 | tmg.mu.Lock()
104 | defer tmg.mu.Unlock()
105 | if len(tmg.lns) == 0 {
106 | // the first listener, listen on the real address
107 | routeConfig := &vhost.RouteConfig{
108 | Domain: domain,
109 | }
110 | tcpMuxLn, errRet := tmg.ctl.tcpMuxHTTPConnectMuxer.Listen(ctx, routeConfig)
111 | if errRet != nil {
112 | return nil, errRet
113 | }
114 | ln = newTCPMuxGroupListener(group, tmg, tcpMuxLn.Addr())
115 |
116 | tmg.group = group
117 | tmg.groupKey = groupKey
118 | tmg.domain = domain
119 | tmg.tcpMuxLn = tcpMuxLn
120 | tmg.lns = append(tmg.lns, ln)
121 | if tmg.acceptCh == nil {
122 | tmg.acceptCh = make(chan net.Conn)
123 | }
124 | go tmg.worker()
125 | } else {
126 | // domain in the same group must be equal
127 | if tmg.group != group || tmg.domain != domain {
128 | return nil, ErrGroupParamsInvalid
129 | }
130 | if tmg.groupKey != groupKey {
131 | return nil, ErrGroupAuthFailed
132 | }
133 | ln = newTCPMuxGroupListener(group, tmg, tmg.lns[0].Addr())
134 | tmg.lns = append(tmg.lns, ln)
135 | }
136 | return
137 | }
138 |
139 | // worker is called when the real TCP listener has been created
140 | func (tmg *TCPMuxGroup) worker() {
141 | for {
142 | c, err := tmg.tcpMuxLn.Accept()
143 | if err != nil {
144 | return
145 | }
146 | err = gerr.PanicToError(func() {
147 | tmg.acceptCh <- c
148 | })
149 | if err != nil {
150 | return
151 | }
152 | }
153 | }
154 |
155 | func (tmg *TCPMuxGroup) Accept() <-chan net.Conn {
156 | return tmg.acceptCh
157 | }
158 |
159 | // CloseListener remove the TCPMuxGroupListener from the TCPMuxGroup
160 | func (tmg *TCPMuxGroup) CloseListener(ln *TCPMuxGroupListener) {
161 | tmg.mu.Lock()
162 | defer tmg.mu.Unlock()
163 | for i, tmpLn := range tmg.lns {
164 | if tmpLn == ln {
165 | tmg.lns = append(tmg.lns[:i], tmg.lns[i+1:]...)
166 | break
167 | }
168 | }
169 | if len(tmg.lns) == 0 {
170 | close(tmg.acceptCh)
171 | tmg.tcpMuxLn.Close()
172 | tmg.ctl.RemoveGroup(tmg.group)
173 | }
174 | }
175 |
176 | // TCPMuxGroupListener
177 | type TCPMuxGroupListener struct {
178 | groupName string
179 | group *TCPMuxGroup
180 |
181 | addr net.Addr
182 | closeCh chan struct{}
183 | }
184 |
185 | func newTCPMuxGroupListener(name string, group *TCPMuxGroup, addr net.Addr) *TCPMuxGroupListener {
186 | return &TCPMuxGroupListener{
187 | groupName: name,
188 | group: group,
189 | addr: addr,
190 | closeCh: make(chan struct{}),
191 | }
192 | }
193 |
194 | // Accept will accept connections from TCPMuxGroup
195 | func (ln *TCPMuxGroupListener) Accept() (c net.Conn, err error) {
196 | var ok bool
197 | select {
198 | case <-ln.closeCh:
199 | return nil, ErrListenerClosed
200 | case c, ok = <-ln.group.Accept():
201 | if !ok {
202 | return nil, ErrListenerClosed
203 | }
204 | return c, nil
205 | }
206 | }
207 |
208 | func (ln *TCPMuxGroupListener) Addr() net.Addr {
209 | return ln.addr
210 | }
211 |
212 | // Close close the listener
213 | func (ln *TCPMuxGroupListener) Close() (err error) {
214 | close(ln.closeCh)
215 |
216 | // remove self from TcpMuxGroup
217 | ln.group.CloseListener(ln)
218 | return
219 | }
220 |
--------------------------------------------------------------------------------
/FRPsource/server/metrics/metrics.go:
--------------------------------------------------------------------------------
1 | package metrics
2 |
3 | import (
4 | "sync"
5 | )
6 |
7 | type ServerMetrics interface {
8 | NewClient()
9 | CloseClient()
10 | NewProxy(name string, proxyType string)
11 | CloseProxy(name string, proxyType string)
12 | OpenConnection(name string, proxyType string)
13 | CloseConnection(name string, proxyType string)
14 | AddTrafficIn(name string, proxyType string, trafficBytes int64)
15 | AddTrafficOut(name string, proxyType string, trafficBytes int64)
16 | }
17 |
18 | var Server ServerMetrics = noopServerMetrics{}
19 |
20 | var registerMetrics sync.Once
21 |
22 | func Register(m ServerMetrics) {
23 | registerMetrics.Do(func() {
24 | Server = m
25 | })
26 | }
27 |
28 | type noopServerMetrics struct{}
29 |
30 | func (noopServerMetrics) NewClient() {}
31 | func (noopServerMetrics) CloseClient() {}
32 | func (noopServerMetrics) NewProxy(name string, proxyType string) {}
33 | func (noopServerMetrics) CloseProxy(name string, proxyType string) {}
34 | func (noopServerMetrics) OpenConnection(name string, proxyType string) {}
35 | func (noopServerMetrics) CloseConnection(name string, proxyType string) {}
36 | func (noopServerMetrics) AddTrafficIn(name string, proxyType string, trafficBytes int64) {}
37 | func (noopServerMetrics) AddTrafficOut(name string, proxyType string, trafficBytes int64) {}
38 |
--------------------------------------------------------------------------------
/FRPsource/server/ports/ports.go:
--------------------------------------------------------------------------------
1 | package ports
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "net"
7 | "sync"
8 | "time"
9 | )
10 |
11 | const (
12 | MinPort = 1
13 | MaxPort = 65535
14 | MaxPortReservedDuration = time.Duration(24) * time.Hour
15 | CleanReservedPortsInterval = time.Hour
16 | )
17 |
18 | var (
19 | ErrPortAlreadyUsed = errors.New("port already used")
20 | ErrPortNotAllowed = errors.New("port not allowed")
21 | ErrPortUnAvailable = errors.New("port unavailable")
22 | ErrNoAvailablePort = errors.New("no available port")
23 | )
24 |
25 | type PortCtx struct {
26 | ProxyName string
27 | Port int
28 | Closed bool
29 | UpdateTime time.Time
30 | }
31 |
32 | type Manager struct {
33 | reservedPorts map[string]*PortCtx
34 | usedPorts map[int]*PortCtx
35 | freePorts map[int]struct{}
36 |
37 | bindAddr string
38 | netType string
39 | mu sync.Mutex
40 | }
41 |
42 | func NewManager(netType string, bindAddr string, allowPorts map[int]struct{}) *Manager {
43 | pm := &Manager{
44 | reservedPorts: make(map[string]*PortCtx),
45 | usedPorts: make(map[int]*PortCtx),
46 | freePorts: make(map[int]struct{}),
47 | bindAddr: bindAddr,
48 | netType: netType,
49 | }
50 | if len(allowPorts) > 0 {
51 | for port := range allowPorts {
52 | pm.freePorts[port] = struct{}{}
53 | }
54 | } else {
55 | for i := MinPort; i <= MaxPort; i++ {
56 | pm.freePorts[i] = struct{}{}
57 | }
58 | }
59 | go pm.cleanReservedPortsWorker()
60 | return pm
61 | }
62 |
63 | func (pm *Manager) Acquire(name string, port int) (realPort int, err error) {
64 | portCtx := &PortCtx{
65 | ProxyName: name,
66 | Closed: false,
67 | UpdateTime: time.Now(),
68 | }
69 |
70 | var ok bool
71 |
72 | pm.mu.Lock()
73 | defer func() {
74 | if err == nil {
75 | portCtx.Port = realPort
76 | }
77 | pm.mu.Unlock()
78 | }()
79 |
80 | // check reserved ports first
81 | if port == 0 {
82 | if ctx, ok := pm.reservedPorts[name]; ok {
83 | if pm.isPortAvailable(ctx.Port) {
84 | realPort = ctx.Port
85 | pm.usedPorts[realPort] = portCtx
86 | pm.reservedPorts[name] = portCtx
87 | delete(pm.freePorts, realPort)
88 | return
89 | }
90 | }
91 | }
92 |
93 | if port == 0 {
94 | // get random port
95 | count := 0
96 | maxTryTimes := 5
97 | for k := range pm.freePorts {
98 | count++
99 | if count > maxTryTimes {
100 | break
101 | }
102 | if pm.isPortAvailable(k) {
103 | realPort = k
104 | pm.usedPorts[realPort] = portCtx
105 | pm.reservedPorts[name] = portCtx
106 | delete(pm.freePorts, realPort)
107 | break
108 | }
109 | }
110 | if realPort == 0 {
111 | err = ErrNoAvailablePort
112 | }
113 | } else {
114 | // specified port
115 | if _, ok = pm.freePorts[port]; ok {
116 | if pm.isPortAvailable(port) {
117 | realPort = port
118 | pm.usedPorts[realPort] = portCtx
119 | pm.reservedPorts[name] = portCtx
120 | delete(pm.freePorts, realPort)
121 | } else {
122 | err = ErrPortUnAvailable
123 | }
124 | } else {
125 | if _, ok = pm.usedPorts[port]; ok {
126 | err = ErrPortAlreadyUsed
127 | } else {
128 | err = ErrPortNotAllowed
129 | }
130 | }
131 | }
132 | return
133 | }
134 |
135 | func (pm *Manager) isPortAvailable(port int) bool {
136 | if pm.netType == "udp" {
137 | addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pm.bindAddr, port))
138 | if err != nil {
139 | return false
140 | }
141 | l, err := net.ListenUDP("udp", addr)
142 | if err != nil {
143 | return false
144 | }
145 | l.Close()
146 | return true
147 | }
148 |
149 | l, err := net.Listen(pm.netType, fmt.Sprintf("%s:%d", pm.bindAddr, port))
150 | if err != nil {
151 | return false
152 | }
153 | l.Close()
154 | return true
155 | }
156 |
157 | func (pm *Manager) Release(port int) {
158 | pm.mu.Lock()
159 | defer pm.mu.Unlock()
160 | if ctx, ok := pm.usedPorts[port]; ok {
161 | pm.freePorts[port] = struct{}{}
162 | delete(pm.usedPorts, port)
163 | ctx.Closed = true
164 | ctx.UpdateTime = time.Now()
165 | }
166 | }
167 |
168 | // Release reserved port if it isn't used in last 24 hours.
169 | func (pm *Manager) cleanReservedPortsWorker() {
170 | for {
171 | time.Sleep(CleanReservedPortsInterval)
172 | pm.mu.Lock()
173 | for name, ctx := range pm.reservedPorts {
174 | if ctx.Closed && time.Since(ctx.UpdateTime) > MaxPortReservedDuration {
175 | delete(pm.reservedPorts, name)
176 | }
177 | }
178 | pm.mu.Unlock()
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/FRPsource/server/proxy/http.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package proxy
16 |
17 | import (
18 | "github.com/fatedier/frp/pkg/util/vhost"
19 | "io"
20 | "net"
21 | "strings"
22 |
23 | "github.com/fatedier/frp/pkg/config"
24 | frpNet "github.com/fatedier/frp/pkg/util/net"
25 | "github.com/fatedier/frp/pkg/util/util"
26 | //"github.com/fatedier/frp/pkg/util/vhost"
27 | "github.com/fatedier/frp/server/metrics"
28 |
29 | frpIo "github.com/fatedier/golib/io"
30 | )
31 |
32 | type HTTPProxy struct {
33 | *BaseProxy
34 | cfg *config.HTTPProxyConf
35 |
36 | closeFuncs []func()
37 | }
38 |
39 | func (pxy *HTTPProxy) Run() (remoteAddr string, err error) {
40 | xl := pxy.xl
41 | routeConfig := vhost.RouteConfig{
42 | RewriteHost: pxy.cfg.HostHeaderRewrite,
43 | Headers: pxy.cfg.Headers,
44 | Username: pxy.cfg.HTTPUser,
45 | Password: pxy.cfg.HTTPPwd,
46 | CreateConnFn: pxy.GetRealConn,
47 | }
48 |
49 | locations := pxy.cfg.Locations
50 | if len(locations) == 0 {
51 | locations = []string{""}
52 | }
53 |
54 | defer func() {
55 | if err != nil {
56 | pxy.Close()
57 | }
58 | }()
59 |
60 | addrs := make([]string, 0)
61 | for _, domain := range pxy.cfg.CustomDomains {
62 | if domain == "" {
63 | continue
64 | }
65 |
66 | routeConfig.Domain = domain
67 | for _, location := range locations {
68 | routeConfig.Location = location
69 | tmpDomain := routeConfig.Domain
70 | tmpLocation := routeConfig.Location
71 |
72 | // handle group
73 | if pxy.cfg.Group != "" {
74 | err = pxy.rc.HTTPGroupCtl.Register(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, routeConfig)
75 | if err != nil {
76 | return
77 | }
78 |
79 | pxy.closeFuncs = append(pxy.closeFuncs, func() {
80 | pxy.rc.HTTPGroupCtl.UnRegister(pxy.name, pxy.cfg.Group, tmpDomain, tmpLocation)
81 | })
82 | } else {
83 | // no group
84 | err = pxy.rc.HTTPReverseProxy.Register(routeConfig)
85 | if err != nil {
86 | return
87 | }
88 | pxy.closeFuncs = append(pxy.closeFuncs, func() {
89 | pxy.rc.HTTPReverseProxy.UnRegister(tmpDomain, tmpLocation)
90 | })
91 | }
92 | addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(pxy.serverCfg.VhostHTTPPort)))
93 | xl.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group)
94 | }
95 | }
96 |
97 | if pxy.cfg.SubDomain != "" {
98 | routeConfig.Domain = pxy.cfg.SubDomain + "." + pxy.serverCfg.SubDomainHost
99 | for _, location := range locations {
100 | routeConfig.Location = location
101 | tmpDomain := routeConfig.Domain
102 | tmpLocation := routeConfig.Location
103 |
104 | // handle group
105 | if pxy.cfg.Group != "" {
106 | err = pxy.rc.HTTPGroupCtl.Register(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, routeConfig)
107 | if err != nil {
108 | return
109 | }
110 |
111 | pxy.closeFuncs = append(pxy.closeFuncs, func() {
112 | pxy.rc.HTTPGroupCtl.UnRegister(pxy.name, pxy.cfg.Group, tmpDomain, tmpLocation)
113 | })
114 | } else {
115 | err = pxy.rc.HTTPReverseProxy.Register(routeConfig)
116 | if err != nil {
117 | return
118 | }
119 | pxy.closeFuncs = append(pxy.closeFuncs, func() {
120 | pxy.rc.HTTPReverseProxy.UnRegister(tmpDomain, tmpLocation)
121 | })
122 | }
123 | addrs = append(addrs, util.CanonicalAddr(tmpDomain, pxy.serverCfg.VhostHTTPPort))
124 |
125 | xl.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group)
126 | }
127 | }
128 | remoteAddr = strings.Join(addrs, ",")
129 | return
130 | }
131 |
132 | func (pxy *HTTPProxy) GetConf() config.ProxyConf {
133 | return pxy.cfg
134 | }
135 |
136 | func (pxy *HTTPProxy) GetRealConn(remoteAddr string) (workConn net.Conn, err error) {
137 | xl := pxy.xl
138 | rAddr, errRet := net.ResolveTCPAddr("tcp", remoteAddr)
139 | if errRet != nil {
140 | xl.Warn("resolve TCP addr [%s] error: %v", remoteAddr, errRet)
141 | // we do not return error here since remoteAddr is not necessary for proxies without proxy protocol enabled
142 | }
143 |
144 | tmpConn, errRet := pxy.GetWorkConnFromPool(rAddr, nil)
145 | if errRet != nil {
146 | err = errRet
147 | return
148 | }
149 |
150 | var rwc io.ReadWriteCloser = tmpConn
151 | if pxy.cfg.UseEncryption {
152 | rwc, err = frpIo.WithEncryption(rwc, []byte(pxy.serverCfg.Token))
153 | if err != nil {
154 | xl.Error("create encryption stream error: %v", err)
155 | return
156 | }
157 | }
158 | if pxy.cfg.UseCompression {
159 | rwc = frpIo.WithCompression(rwc)
160 | }
161 | workConn = frpNet.WrapReadWriteCloserToConn(rwc, tmpConn)
162 | workConn = frpNet.WrapStatsConn(workConn, pxy.updateStatsAfterClosedConn)
163 | metrics.Server.OpenConnection(pxy.GetName(), pxy.GetConf().GetBaseInfo().ProxyType)
164 | return
165 | }
166 |
167 | func (pxy *HTTPProxy) updateStatsAfterClosedConn(totalRead, totalWrite int64) {
168 | name := pxy.GetName()
169 | proxyType := pxy.GetConf().GetBaseInfo().ProxyType
170 | metrics.Server.CloseConnection(name, proxyType)
171 | metrics.Server.AddTrafficIn(name, proxyType, totalWrite)
172 | metrics.Server.AddTrafficOut(name, proxyType, totalRead)
173 | }
174 |
175 | func (pxy *HTTPProxy) Close() {
176 | pxy.BaseProxy.Close()
177 | for _, closeFn := range pxy.closeFuncs {
178 | closeFn()
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/FRPsource/server/proxy/https.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package proxy
16 |
17 | import (
18 | "github.com/fatedier/frp/pkg/util/vhost"
19 | "strings"
20 |
21 | "github.com/fatedier/frp/pkg/config"
22 | "github.com/fatedier/frp/pkg/util/util"
23 | //"github.com/fatedier/frp/pkg/util/vhost"
24 | )
25 |
26 | type HTTPSProxy struct {
27 | *BaseProxy
28 | cfg *config.HTTPSProxyConf
29 | }
30 |
31 | func (pxy *HTTPSProxy) Run() (remoteAddr string, err error) {
32 | xl := pxy.xl
33 | routeConfig := &vhost.RouteConfig{}
34 |
35 | defer func() {
36 | if err != nil {
37 | pxy.Close()
38 | }
39 | }()
40 | addrs := make([]string, 0)
41 | for _, domain := range pxy.cfg.CustomDomains {
42 | if domain == "" {
43 | continue
44 | }
45 |
46 | routeConfig.Domain = domain
47 | l, errRet := pxy.rc.VhostHTTPSMuxer.Listen(pxy.ctx, routeConfig)
48 | if errRet != nil {
49 | err = errRet
50 | return
51 | }
52 | xl.Info("https proxy listen for host [%s]", routeConfig.Domain)
53 | pxy.listeners = append(pxy.listeners, l)
54 | addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.VhostHTTPSPort))
55 | }
56 |
57 | if pxy.cfg.SubDomain != "" {
58 | routeConfig.Domain = pxy.cfg.SubDomain + "." + pxy.serverCfg.SubDomainHost
59 | l, errRet := pxy.rc.VhostHTTPSMuxer.Listen(pxy.ctx, routeConfig)
60 | if errRet != nil {
61 | err = errRet
62 | return
63 | }
64 | xl.Info("https proxy listen for host [%s]", routeConfig.Domain)
65 | pxy.listeners = append(pxy.listeners, l)
66 | addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(pxy.serverCfg.VhostHTTPSPort)))
67 | }
68 |
69 | pxy.startListenHandler(pxy, HandleUserTCPConnection)
70 | remoteAddr = strings.Join(addrs, ",")
71 | return
72 | }
73 |
74 | func (pxy *HTTPSProxy) GetConf() config.ProxyConf {
75 | return pxy.cfg
76 | }
77 |
78 | func (pxy *HTTPSProxy) Close() {
79 | pxy.BaseProxy.Close()
80 | }
81 |
--------------------------------------------------------------------------------
/FRPsource/server/proxy/stcp.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package proxy
16 |
17 | import (
18 | "github.com/fatedier/frp/pkg/config"
19 | )
20 |
21 | type STCPProxy struct {
22 | *BaseProxy
23 | cfg *config.STCPProxyConf
24 | }
25 |
26 | func (pxy *STCPProxy) Run() (remoteAddr string, err error) {
27 | xl := pxy.xl
28 | listener, errRet := pxy.rc.VisitorManager.Listen(pxy.GetName(), pxy.cfg.Sk)
29 | if errRet != nil {
30 | err = errRet
31 | return
32 | }
33 | pxy.listeners = append(pxy.listeners, listener)
34 | xl.Info("stcp proxy custom listen success")
35 |
36 | pxy.startListenHandler(pxy, HandleUserTCPConnection)
37 | return
38 | }
39 |
40 | func (pxy *STCPProxy) GetConf() config.ProxyConf {
41 | return pxy.cfg
42 | }
43 |
44 | func (pxy *STCPProxy) Close() {
45 | pxy.BaseProxy.Close()
46 | pxy.rc.VisitorManager.CloseListener(pxy.GetName())
47 | }
48 |
--------------------------------------------------------------------------------
/FRPsource/server/proxy/sudp.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package proxy
16 |
17 | import (
18 | "github.com/fatedier/frp/pkg/config"
19 | )
20 |
21 | type SUDPProxy struct {
22 | *BaseProxy
23 | cfg *config.SUDPProxyConf
24 | }
25 |
26 | func (pxy *SUDPProxy) Run() (remoteAddr string, err error) {
27 | xl := pxy.xl
28 |
29 | listener, errRet := pxy.rc.VisitorManager.Listen(pxy.GetName(), pxy.cfg.Sk)
30 | if errRet != nil {
31 | err = errRet
32 | return
33 | }
34 | pxy.listeners = append(pxy.listeners, listener)
35 | xl.Info("sudp proxy custom listen success")
36 |
37 | pxy.startListenHandler(pxy, HandleUserTCPConnection)
38 | return
39 | }
40 |
41 | func (pxy *SUDPProxy) GetConf() config.ProxyConf {
42 | return pxy.cfg
43 | }
44 |
45 | func (pxy *SUDPProxy) Close() {
46 | pxy.BaseProxy.Close()
47 | pxy.rc.VisitorManager.CloseListener(pxy.GetName())
48 | }
49 |
--------------------------------------------------------------------------------
/FRPsource/server/proxy/tcp.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package proxy
16 |
17 | import (
18 | "fmt"
19 | "net"
20 |
21 | "github.com/fatedier/frp/pkg/config"
22 | )
23 |
24 | type TCPProxy struct {
25 | *BaseProxy
26 | cfg *config.TCPProxyConf
27 |
28 | realPort int
29 | }
30 |
31 | func (pxy *TCPProxy) Run() (remoteAddr string, err error) {
32 | xl := pxy.xl
33 | if pxy.cfg.Group != "" {
34 | l, realPort, errRet := pxy.rc.TCPGroupCtl.Listen(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, pxy.serverCfg.ProxyBindAddr, pxy.cfg.RemotePort)
35 | if errRet != nil {
36 | err = errRet
37 | return
38 | }
39 | defer func() {
40 | if err != nil {
41 | l.Close()
42 | }
43 | }()
44 | pxy.realPort = realPort
45 | pxy.listeners = append(pxy.listeners, l)
46 | xl.Info("tcp proxy listen port [%d] in group [%s]", pxy.cfg.RemotePort, pxy.cfg.Group)
47 | } else {
48 | pxy.realPort, err = pxy.rc.TCPPortManager.Acquire(pxy.name, pxy.cfg.RemotePort)
49 | if err != nil {
50 | return
51 | }
52 | defer func() {
53 | if err != nil {
54 | pxy.rc.TCPPortManager.Release(pxy.realPort)
55 | }
56 | }()
57 | listener, errRet := net.Listen("tcp", fmt.Sprintf("%s:%d", pxy.serverCfg.ProxyBindAddr, pxy.realPort))
58 | if errRet != nil {
59 | err = errRet
60 | return
61 | }
62 | pxy.listeners = append(pxy.listeners, listener)
63 | xl.Info("tcp proxy listen port [%d]", pxy.cfg.RemotePort)
64 | }
65 |
66 | pxy.cfg.RemotePort = pxy.realPort
67 | remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
68 | pxy.startListenHandler(pxy, HandleUserTCPConnection)
69 | return
70 | }
71 |
72 | func (pxy *TCPProxy) GetConf() config.ProxyConf {
73 | return pxy.cfg
74 | }
75 |
76 | func (pxy *TCPProxy) Close() {
77 | pxy.BaseProxy.Close()
78 | if pxy.cfg.Group == "" {
79 | pxy.rc.TCPPortManager.Release(pxy.realPort)
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/FRPsource/server/proxy/tcpmux.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 guylewin, guy@lewin.co.il
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package proxy
16 |
17 | import (
18 | "fmt"
19 | "github.com/fatedier/frp/pkg/util/vhost"
20 | "net"
21 | "strings"
22 |
23 | "github.com/fatedier/frp/pkg/config"
24 | "github.com/fatedier/frp/pkg/consts"
25 | "github.com/fatedier/frp/pkg/util/util"
26 | //"github.com/fatedier/frp/pkg/util/vhost"
27 | )
28 |
29 | type TCPMuxProxy struct {
30 | *BaseProxy
31 | cfg *config.TCPMuxProxyConf
32 | }
33 |
34 | func (pxy *TCPMuxProxy) httpConnectListen(domain string, addrs []string) (_ []string, err error) {
35 | var l net.Listener
36 | if pxy.cfg.Group != "" {
37 | l, err = pxy.rc.TCPMuxGroupCtl.Listen(pxy.ctx, pxy.cfg.Multiplexer, pxy.cfg.Group, pxy.cfg.GroupKey, domain)
38 | } else {
39 | routeConfig := &vhost.RouteConfig{
40 | Domain: domain,
41 | }
42 | l, err = pxy.rc.TCPMuxHTTPConnectMuxer.Listen(pxy.ctx, routeConfig)
43 | }
44 | if err != nil {
45 | return nil, err
46 | }
47 | pxy.xl.Info("tcpmux httpconnect multiplexer listens for host [%s]", domain)
48 | pxy.listeners = append(pxy.listeners, l)
49 | return append(addrs, util.CanonicalAddr(domain, pxy.serverCfg.TCPMuxHTTPConnectPort)), nil
50 | }
51 |
52 | func (pxy *TCPMuxProxy) httpConnectRun() (remoteAddr string, err error) {
53 | addrs := make([]string, 0)
54 | for _, domain := range pxy.cfg.CustomDomains {
55 | if domain == "" {
56 | continue
57 | }
58 |
59 | addrs, err = pxy.httpConnectListen(domain, addrs)
60 | if err != nil {
61 | return "", err
62 | }
63 | }
64 |
65 | if pxy.cfg.SubDomain != "" {
66 | addrs, err = pxy.httpConnectListen(pxy.cfg.SubDomain+"."+pxy.serverCfg.SubDomainHost, addrs)
67 | if err != nil {
68 | return "", err
69 | }
70 | }
71 |
72 | pxy.startListenHandler(pxy, HandleUserTCPConnection)
73 | remoteAddr = strings.Join(addrs, ",")
74 | return remoteAddr, err
75 | }
76 |
77 | func (pxy *TCPMuxProxy) Run() (remoteAddr string, err error) {
78 | switch pxy.cfg.Multiplexer {
79 | case consts.HTTPConnectTCPMultiplexer:
80 | remoteAddr, err = pxy.httpConnectRun()
81 | default:
82 | err = fmt.Errorf("unknown multiplexer [%s]", pxy.cfg.Multiplexer)
83 | }
84 |
85 | if err != nil {
86 | pxy.Close()
87 | }
88 | return remoteAddr, err
89 | }
90 |
91 | func (pxy *TCPMuxProxy) GetConf() config.ProxyConf {
92 | return pxy.cfg
93 | }
94 |
95 | func (pxy *TCPMuxProxy) Close() {
96 | pxy.BaseProxy.Close()
97 | }
98 |
--------------------------------------------------------------------------------
/FRPsource/server/proxy/xtcp.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package proxy
16 |
17 | import (
18 | "fmt"
19 |
20 | "github.com/fatedier/frp/pkg/config"
21 | "github.com/fatedier/frp/pkg/msg"
22 |
23 | "github.com/fatedier/golib/errors"
24 | )
25 |
26 | type XTCPProxy struct {
27 | *BaseProxy
28 | cfg *config.XTCPProxyConf
29 |
30 | closeCh chan struct{}
31 | }
32 |
33 | func (pxy *XTCPProxy) Run() (remoteAddr string, err error) {
34 | xl := pxy.xl
35 |
36 | if pxy.rc.NatHoleController == nil {
37 | xl.Error("udp port for xtcp is not specified.")
38 | err = fmt.Errorf("xtcp is not supported in frps")
39 | return
40 | }
41 | sidCh := pxy.rc.NatHoleController.ListenClient(pxy.GetName(), pxy.cfg.Sk)
42 | go func() {
43 | for {
44 | select {
45 | case <-pxy.closeCh:
46 | break
47 | case sidRequest := <-sidCh:
48 | sr := sidRequest
49 | workConn, errRet := pxy.GetWorkConnFromPool(nil, nil)
50 | if errRet != nil {
51 | continue
52 | }
53 | m := &msg.NatHoleSid{
54 | Sid: sr.Sid,
55 | }
56 | errRet = msg.WriteMsg(workConn, m)
57 | if errRet != nil {
58 | xl.Warn("write nat hole sid package error, %v", errRet)
59 | workConn.Close()
60 | break
61 | }
62 |
63 | go func() {
64 | raw, errRet := msg.ReadMsg(workConn)
65 | if errRet != nil {
66 | xl.Warn("read nat hole client ok package error: %v", errRet)
67 | workConn.Close()
68 | return
69 | }
70 | if _, ok := raw.(*msg.NatHoleClientDetectOK); !ok {
71 | xl.Warn("read nat hole client ok package format error")
72 | workConn.Close()
73 | return
74 | }
75 |
76 | select {
77 | case sr.NotifyCh <- struct{}{}:
78 | default:
79 | }
80 | }()
81 | }
82 | }
83 | }()
84 | return
85 | }
86 |
87 | func (pxy *XTCPProxy) GetConf() config.ProxyConf {
88 | return pxy.cfg
89 | }
90 |
91 | func (pxy *XTCPProxy) Close() {
92 | pxy.BaseProxy.Close()
93 | pxy.rc.NatHoleController.CloseClient(pxy.GetName())
94 | errors.PanicToError(func() {
95 | close(pxy.closeCh)
96 | })
97 | }
98 |
--------------------------------------------------------------------------------
/FRPsource/server/visitor/visitor.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 fatedier, fatedier@gmail.com
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package visitor
16 |
17 | import (
18 | "fmt"
19 | "io"
20 | "net"
21 | "sync"
22 |
23 | frpNet "github.com/fatedier/frp/pkg/util/net"
24 | "github.com/fatedier/frp/pkg/util/util"
25 |
26 | frpIo "github.com/fatedier/golib/io"
27 | )
28 |
29 | // Manager for visitor listeners.
30 | type Manager struct {
31 | visitorListeners map[string]*frpNet.CustomListener
32 | skMap map[string]string
33 |
34 | mu sync.RWMutex
35 | }
36 |
37 | func NewManager() *Manager {
38 | return &Manager{
39 | visitorListeners: make(map[string]*frpNet.CustomListener),
40 | skMap: make(map[string]string),
41 | }
42 | }
43 |
44 | func (vm *Manager) Listen(name string, sk string) (l *frpNet.CustomListener, err error) {
45 | vm.mu.Lock()
46 | defer vm.mu.Unlock()
47 |
48 | if _, ok := vm.visitorListeners[name]; ok {
49 | err = fmt.Errorf("custom listener for [%s] is repeated", name)
50 | return
51 | }
52 |
53 | l = frpNet.NewCustomListener()
54 | vm.visitorListeners[name] = l
55 | vm.skMap[name] = sk
56 | return
57 | }
58 |
59 | func (vm *Manager) NewConn(name string, conn net.Conn, timestamp int64, signKey string,
60 | useEncryption bool, useCompression bool) (err error) {
61 |
62 | vm.mu.RLock()
63 | defer vm.mu.RUnlock()
64 |
65 | if l, ok := vm.visitorListeners[name]; ok {
66 | var sk string
67 | if sk = vm.skMap[name]; util.GetAuthKey(sk, timestamp) != signKey {
68 | err = fmt.Errorf("visitor connection of [%s] auth failed", name)
69 | return
70 | }
71 |
72 | var rwc io.ReadWriteCloser = conn
73 | if useEncryption {
74 | if rwc, err = frpIo.WithEncryption(rwc, []byte(sk)); err != nil {
75 | err = fmt.Errorf("create encryption connection failed: %v", err)
76 | return
77 | }
78 | }
79 | if useCompression {
80 | rwc = frpIo.WithCompression(rwc)
81 | }
82 | err = l.PutConn(frpNet.WrapReadWriteCloserToConn(rwc, conn))
83 | } else {
84 | err = fmt.Errorf("custom listener for [%s] doesn't exist", name)
85 | return
86 | }
87 | return
88 | }
89 |
90 | func (vm *Manager) CloseListener(name string) {
91 | vm.mu.Lock()
92 | defer vm.mu.Unlock()
93 |
94 | delete(vm.visitorListeners, name)
95 | delete(vm.skMap, name)
96 | }
97 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # frpBuilder
2 | To Make frp with no arguments ,which Conveniently in red teaming
3 |
4 |
5 |
6 | **I will give a simple modified source code of frp and Builder(MFC C++) in the project**
7 |
8 |
9 | **All it does is write the configuration into the root.go and compile it, so first make sure that you can run go build in that directory**
10 |
11 | ### Usage:
12 |
13 | open the Builder `FRPBuider\cmd\frpc\builder.exe`
14 |
15 | 
16 |
17 | ### Did:
18 |
19 | 1. It will generate a frp without arguments
20 |
21 | in other words, when building a tunnel, you only need to execute this PE file directly
22 |
23 | 2. The tls encryption is enabled by default
24 | 3. Simply remove some not often used packages
25 | 4. But UPX I used compression is the default. If you want to reduce the size, please notice that could be Noticed by Anti-virus
26 | 5. Finally, the source code is here, you can further optimize, such as remove the features of tls
27 |
28 |
--------------------------------------------------------------------------------
/buildersource/builder.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30611.23
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "builder", "builder\builder.vcxproj", "{D5B2210E-AED7-48AE-B924-52AC5B2A854F}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|x64 = Debug|x64
11 | Debug|x86 = Debug|x86
12 | Release|x64 = Release|x64
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {D5B2210E-AED7-48AE-B924-52AC5B2A854F}.Debug|x64.ActiveCfg = Debug|x64
17 | {D5B2210E-AED7-48AE-B924-52AC5B2A854F}.Debug|x64.Build.0 = Debug|x64
18 | {D5B2210E-AED7-48AE-B924-52AC5B2A854F}.Debug|x86.ActiveCfg = Debug|Win32
19 | {D5B2210E-AED7-48AE-B924-52AC5B2A854F}.Debug|x86.Build.0 = Debug|Win32
20 | {D5B2210E-AED7-48AE-B924-52AC5B2A854F}.Release|x64.ActiveCfg = Release|x64
21 | {D5B2210E-AED7-48AE-B924-52AC5B2A854F}.Release|x64.Build.0 = Release|x64
22 | {D5B2210E-AED7-48AE-B924-52AC5B2A854F}.Release|x86.ActiveCfg = Release|Win32
23 | {D5B2210E-AED7-48AE-B924-52AC5B2A854F}.Release|x86.Build.0 = Release|Win32
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {0D9F707D-5FAD-4995-B633-4F851BD613E5}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/buildersource/builder/builder.aps:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evilashz/frpBuilder/a1b0c0397739efd707ea784b23a6018f7bb38ac3/buildersource/builder/builder.aps
--------------------------------------------------------------------------------
/buildersource/builder/builder.cpp:
--------------------------------------------------------------------------------
1 |
2 | // builder.cpp: 定义应用程序的类行为。
3 | //
4 |
5 | #include "pch.h"
6 | #include "framework.h"
7 | #include "builder.h"
8 | #include "builderDlg.h"
9 |
10 | #ifdef _DEBUG
11 | #define new DEBUG_NEW
12 | #endif
13 |
14 |
15 | // CbuilderApp
16 |
17 | BEGIN_MESSAGE_MAP(CbuilderApp, CWinApp)
18 | ON_COMMAND(ID_HELP, &CWinApp::OnHelp)
19 | END_MESSAGE_MAP()
20 |
21 |
22 | // CbuilderApp 构造
23 |
24 | CbuilderApp::CbuilderApp()
25 | {
26 | // 支持重新启动管理器
27 | m_dwRestartManagerSupportFlags = AFX_RESTART_MANAGER_SUPPORT_RESTART;
28 |
29 | // TODO: 在此处添加构造代码,
30 | // 将所有重要的初始化放置在 InitInstance 中
31 | }
32 |
33 |
34 | // 唯一的 CbuilderApp 对象
35 |
36 | CbuilderApp theApp;
37 |
38 |
39 | // CbuilderApp 初始化
40 |
41 | BOOL CbuilderApp::InitInstance()
42 | {
43 | // 如果一个运行在 Windows XP 上的应用程序清单指定要
44 | // 使用 ComCtl32.dll 版本 6 或更高版本来启用可视化方式,
45 | //则需要 InitCommonControlsEx()。 否则,将无法创建窗口。
46 | INITCOMMONCONTROLSEX InitCtrls;
47 | InitCtrls.dwSize = sizeof(InitCtrls);
48 | // 将它设置为包括所有要在应用程序中使用的
49 | // 公共控件类。
50 | InitCtrls.dwICC = ICC_WIN95_CLASSES;
51 | InitCommonControlsEx(&InitCtrls);
52 |
53 | CWinApp::InitInstance();
54 |
55 |
56 | AfxEnableControlContainer();
57 |
58 | // 创建 shell 管理器,以防对话框包含
59 | // 任何 shell 树视图控件或 shell 列表视图控件。
60 | CShellManager *pShellManager = new CShellManager;
61 |
62 | // 激活“Windows Native”视觉管理器,以便在 MFC 控件中启用主题
63 | CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerWindows));
64 |
65 | // 标准初始化
66 | // 如果未使用这些功能并希望减小
67 | // 最终可执行文件的大小,则应移除下列
68 | // 不需要的特定初始化例程
69 | // 更改用于存储设置的注册表项
70 | // TODO: 应适当修改该字符串,
71 | // 例如修改为公司或组织名
72 | SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
73 |
74 | CbuilderDlg dlg;
75 | m_pMainWnd = &dlg;
76 | INT_PTR nResponse = dlg.DoModal();
77 | if (nResponse == IDOK)
78 | {
79 | // TODO: 在此放置处理何时用
80 | // “确定”来关闭对话框的代码
81 | }
82 | else if (nResponse == IDCANCEL)
83 | {
84 | // TODO: 在此放置处理何时用
85 | // “取消”来关闭对话框的代码
86 | }
87 | else if (nResponse == -1)
88 | {
89 | TRACE(traceAppMsg, 0, "警告: 对话框创建失败,应用程序将意外终止。\n");
90 | TRACE(traceAppMsg, 0, "警告: 如果您在对话框上使用 MFC 控件,则无法 #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS。\n");
91 | }
92 |
93 | // 删除上面创建的 shell 管理器。
94 | if (pShellManager != nullptr)
95 | {
96 | delete pShellManager;
97 | }
98 |
99 | #if !defined(_AFXDLL) && !defined(_AFX_NO_MFC_CONTROLS_IN_DIALOGS)
100 | ControlBarCleanUp();
101 | #endif
102 |
103 | // 由于对话框已关闭,所以将返回 FALSE 以便退出应用程序,
104 | // 而不是启动应用程序的消息泵。
105 | return FALSE;
106 | }
107 |
108 |
--------------------------------------------------------------------------------
/buildersource/builder/builder.h:
--------------------------------------------------------------------------------
1 |
2 | // builder.h: PROJECT_NAME 应用程序的主头文件
3 | //
4 |
5 | #pragma once
6 |
7 | #ifndef __AFXWIN_H__
8 | #error "在包含此文件之前包含 'pch.h' 以生成 PCH"
9 | #endif
10 |
11 | #include "resource.h" // 主符号
12 |
13 |
14 | // CbuilderApp:
15 | // 有关此类的实现,请参阅 builder.cpp
16 | //
17 |
18 | class CbuilderApp : public CWinApp
19 | {
20 | public:
21 | CbuilderApp();
22 |
23 | // 重写
24 | public:
25 | virtual BOOL InitInstance();
26 |
27 | // 实现
28 |
29 | DECLARE_MESSAGE_MAP()
30 | };
31 |
32 | extern CbuilderApp theApp;
33 |
--------------------------------------------------------------------------------
/buildersource/builder/builder.rc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evilashz/frpBuilder/a1b0c0397739efd707ea784b23a6018f7bb38ac3/buildersource/builder/builder.rc
--------------------------------------------------------------------------------
/buildersource/builder/builder.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | 头文件
20 |
21 |
22 | 头文件
23 |
24 |
25 | 头文件
26 |
27 |
28 | 头文件
29 |
30 |
31 | 头文件
32 |
33 |
34 | 头文件
35 |
36 |
37 |
38 |
39 | 源文件
40 |
41 |
42 | 源文件
43 |
44 |
45 | 源文件
46 |
47 |
48 |
49 |
50 | 资源文件
51 |
52 |
53 |
54 |
55 | 资源文件
56 |
57 |
58 |
59 |
60 | 资源文件
61 |
62 |
63 |
--------------------------------------------------------------------------------
/buildersource/builder/builder.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | builder.rc
5 |
6 |
--------------------------------------------------------------------------------
/buildersource/builder/builderDlg.h:
--------------------------------------------------------------------------------
1 |
2 | // builderDlg.h: 头文件
3 | //
4 |
5 | #pragma once
6 |
7 |
8 | // CbuilderDlg 对话框
9 | class CbuilderDlg : public CDialogEx
10 | {
11 | // 构造
12 | public:
13 | CbuilderDlg(CWnd* pParent = nullptr); // 标准构造函数
14 |
15 | // 对话框数据
16 | #ifdef AFX_DESIGN_TIME
17 | enum { IDD = IDD_BUILDER_DIALOG };
18 | #endif
19 |
20 | protected:
21 | virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
22 |
23 |
24 | // 实现
25 | protected:
26 | HICON m_hIcon;
27 |
28 | // 生成的消息映射函数
29 | virtual BOOL OnInitDialog();
30 | afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
31 | afx_msg void OnPaint();
32 | afx_msg HCURSOR OnQueryDragIcon();
33 | DECLARE_MESSAGE_MAP()
34 |
35 | public:
36 | CString ServerAddr;
37 | CString ServerPort;
38 | CString ForwardPort;
39 |
40 | afx_msg void OnBnClickedGenerate();
41 |
42 | BOOL isx64;
43 | BOOL isx86;
44 | BOOL islinux;
45 | BOOL iswindows;
46 | BOOL isupx;
47 | };
48 |
--------------------------------------------------------------------------------
/buildersource/builder/framework.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifndef VC_EXTRALEAN
4 | #define VC_EXTRALEAN // 从 Windows 头中排除极少使用的资料
5 | #endif
6 |
7 | #include "targetver.h"
8 |
9 | #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // 某些 CString 构造函数将是显式的
10 |
11 | // 关闭 MFC 的一些常见且经常可放心忽略的隐藏警告消息
12 | #define _AFX_ALL_WARNINGS
13 |
14 | #include // MFC 核心组件和标准组件
15 | #include // MFC 扩展
16 |
17 |
18 | #include // MFC 自动化类
19 |
20 |
21 |
22 | #ifndef _AFX_NO_OLE_SUPPORT
23 | #include // MFC 对 Internet Explorer 4 公共控件的支持
24 | #endif
25 | #ifndef _AFX_NO_AFXCMN_SUPPORT
26 | #include // MFC 对 Windows 公共控件的支持
27 | #endif // _AFX_NO_AFXCMN_SUPPORT
28 |
29 | #include // MFC 支持功能区和控制条
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | #ifdef _UNICODE
40 | #if defined _M_IX86
41 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
42 | #elif defined _M_X64
43 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"")
44 | #else
45 | #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
46 | #endif
47 | #endif
48 |
49 |
50 |
--------------------------------------------------------------------------------
/buildersource/builder/pch.cpp:
--------------------------------------------------------------------------------
1 | // pch.cpp: 与预编译标头对应的源文件
2 |
3 | #include "pch.h"
4 |
5 | // 当使用预编译的头时,需要使用此源文件,编译才能成功。
6 |
--------------------------------------------------------------------------------
/buildersource/builder/pch.h:
--------------------------------------------------------------------------------
1 | // pch.h: 这是预编译标头文件。
2 | // 下方列出的文件仅编译一次,提高了将来生成的生成性能。
3 | // 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
4 | // 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
5 | // 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
6 |
7 | #ifndef PCH_H
8 | #define PCH_H
9 |
10 | #define _CRT_SECURE_NO_WARNINGS
11 | // 添加要在此处预编译的标头
12 | #include "framework.h"
13 |
14 | #endif //PCH_H
15 |
--------------------------------------------------------------------------------
/buildersource/builder/res/builder.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evilashz/frpBuilder/a1b0c0397739efd707ea784b23a6018f7bb38ac3/buildersource/builder/res/builder.ico
--------------------------------------------------------------------------------
/buildersource/builder/res/builder.rc2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/evilashz/frpBuilder/a1b0c0397739efd707ea784b23a6018f7bb38ac3/buildersource/builder/res/builder.rc2
--------------------------------------------------------------------------------
/buildersource/builder/resource.h:
--------------------------------------------------------------------------------
1 | //{{NO_DEPENDENCIES}}
2 | // Microsoft Visual C++ 生成的包含文件。
3 | // 供 builder.rc 使用
4 | //
5 | #define IDM_ABOUTBOX 0x0010
6 | #define IDD_ABOUTBOX 100
7 | #define IDS_ABOUTBOX 101
8 | #define IDD_BUILDER_DIALOG 102
9 | #define IDR_MAINFRAME 128
10 | #define SERVERADDR_EDIT1 1000
11 | #define SERVERPORT_EDIT2 1001
12 | #define FORWARDPORT_EDIT3 1002
13 | #define X64_CHECK1 1004
14 | #define IDC_X64 1004
15 | #define X86_CHECK2 1005
16 | #define Windows_CHECK2 1006
17 | #define Windows_CHECK3 1007
18 | #define Linux_CHECK3 1007
19 | #define Windows_CHECK4 1008
20 |
21 | // Next default values for new objects
22 | //
23 | #ifdef APSTUDIO_INVOKED
24 | #ifndef APSTUDIO_READONLY_SYMBOLS
25 | #define _APS_NEXT_RESOURCE_VALUE 131
26 | #define _APS_NEXT_COMMAND_VALUE 32771
27 | #define _APS_NEXT_CONTROL_VALUE 1005
28 | #define _APS_NEXT_SYMED_VALUE 101
29 | #endif
30 | #endif
31 |
--------------------------------------------------------------------------------
/buildersource/builder/targetver.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | // 包括 SDKDDKVer.h 将定义可用的最高版本的 Windows 平台。
4 |
5 | // 如果要为以前的 Windows 平台生成应用程序,请包括 WinSDKVer.h,并将
6 | // 将 _WIN32_WINNT 宏设置为要支持的平台,然后再包括 SDKDDKVer.h。
7 |
8 | #include
9 |
--------------------------------------------------------------------------------