├── 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 | ![image-20210810092734550](https://images-1258433570.cos.ap-beijing.myqcloud.com/images/20210810092735.png) 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 | --------------------------------------------------------------------------------