From a57679f8375986abc970d22bad52644ba62a4969 Mon Sep 17 00:00:00 2001 From: fatedier Date: Sun, 8 Dec 2019 21:01:58 +0800 Subject: [PATCH 1/3] support meta info for client and proxy --- conf/frpc_full.ini | 7 +++++++ models/config/client_common.go | 8 ++++++++ models/config/proxy.go | 14 +++++++++++++- models/msg/msg.go | 30 ++++++++++++++++-------------- models/plugin/http2https.go | 3 +-- models/plugin/https2http.go | 2 +- 6 files changed, 46 insertions(+), 18 deletions(-) diff --git a/conf/frpc_full.ini b/conf/frpc_full.ini index 14ca6ed3..8c86acba 100644 --- a/conf/frpc_full.ini +++ b/conf/frpc_full.ini @@ -64,6 +64,10 @@ tls_enable = true # heartbeat_interval = 30 # heartbeat_timeout = 90 +# additional meta info for client +meta_var1 = 123 +meta_var2 = 234 + # 'ssh' is the unique proxy name # if user in [common] section is not empty, it will be changed to {user}.{proxy} such as 'your_name.ssh' [ssh] @@ -92,6 +96,9 @@ health_check_timeout_s = 3 health_check_max_failed = 3 # every 10 seconds will do a health check health_check_interval_s = 10 +# additional meta info for each proxy +meta_var1 = 123 +meta_var2 = 234 [ssh_random] type = tcp diff --git a/models/config/client_common.go b/models/config/client_common.go index fe87e08d..2b5006b4 100644 --- a/models/config/client_common.go +++ b/models/config/client_common.go @@ -115,6 +115,8 @@ type ClientCommonConf struct { // before the connection is terminated, in seconds. It is not recommended // to change this value. By default, this value is 90. HeartBeatTimeout int64 `json:"heartbeat_timeout"` + // Client meta info + Metas map[string]string `json:"metas"` } // GetDefaultClientConf returns a client configuration with default values. @@ -144,6 +146,7 @@ func GetDefaultClientConf() ClientCommonConf { TLSEnable: false, HeartBeatInterval: 30, HeartBeatTimeout: 90, + Metas: make(map[string]string), } } @@ -294,6 +297,11 @@ func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error cfg.HeartBeatInterval = v } } + for k, v := range conf.Section("common") { + if strings.HasPrefix(k, "meta_") { + cfg.Metas[strings.TrimPrefix(k, "meta_")] = v + } + } return } diff --git a/models/config/proxy.go b/models/config/proxy.go index 85c353ed..1efaf38a 100644 --- a/models/config/proxy.go +++ b/models/config/proxy.go @@ -130,6 +130,9 @@ type BaseProxyConf struct { // 0 means no limit BandwidthLimit BandwidthQuantity `json:"bandwidth_limit"` + // meta info for each proxy + Metas map[string]string `json:"metas"` + LocalSvrConf HealthCheckConf } @@ -146,7 +149,8 @@ func (cfg *BaseProxyConf) compare(cmp *BaseProxyConf) bool { cfg.Group != cmp.Group || cfg.GroupKey != cmp.GroupKey || cfg.ProxyProtocolVersion != cmp.ProxyProtocolVersion || - cfg.BandwidthLimit.Equal(&cmp.BandwidthLimit) { + cfg.BandwidthLimit.Equal(&cmp.BandwidthLimit) || + !reflect.DeepEqual(cfg.Metas, cmp.Metas) { return false } if !cfg.LocalSvrConf.compare(&cmp.LocalSvrConf) { @@ -165,6 +169,7 @@ func (cfg *BaseProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { cfg.UseCompression = pMsg.UseCompression cfg.Group = pMsg.Group cfg.GroupKey = pMsg.GroupKey + cfg.Metas = pMsg.Metas } func (cfg *BaseProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) error { @@ -212,6 +217,12 @@ func (cfg *BaseProxyConf) UnmarshalFromIni(prefix string, name string, section i } cfg.HealthCheckUrl = s + cfg.HealthCheckUrl } + + for k, v := range section { + if strings.HasPrefix(k, "meta_") { + cfg.Metas[strings.TrimPrefix(k, "meta_")] = v + } + } return nil } @@ -222,6 +233,7 @@ func (cfg *BaseProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { pMsg.UseCompression = cfg.UseCompression pMsg.Group = cfg.Group pMsg.GroupKey = cfg.GroupKey + pMsg.Metas = cfg.Metas } func (cfg *BaseProxyConf) checkForCli() (err error) { diff --git a/models/msg/msg.go b/models/msg/msg.go index 11d2542f..ce41c9ec 100644 --- a/models/msg/msg.go +++ b/models/msg/msg.go @@ -62,14 +62,15 @@ var ( // When frpc start, client send this message to login to server. type Login struct { - Version string `json:"version"` - Hostname string `json:"hostname"` - Os string `json:"os"` - Arch string `json:"arch"` - User string `json:"user"` - PrivilegeKey string `json:"privilege_key"` - Timestamp int64 `json:"timestamp"` - RunId string `json:"run_id"` + Version string `json:"version"` + Hostname string `json:"hostname"` + Os string `json:"os"` + Arch string `json:"arch"` + User string `json:"user"` + PrivilegeKey string `json:"privilege_key"` + Timestamp int64 `json:"timestamp"` + RunId string `json:"run_id"` + Metas map[string]string `json:"metas"` // Some global configures. PoolCount int `json:"pool_count"` @@ -84,12 +85,13 @@ type LoginResp struct { // When frpc login success, send this message to frps for running a new proxy. type NewProxy struct { - ProxyName string `json:"proxy_name"` - ProxyType string `json:"proxy_type"` - UseEncryption bool `json:"use_encryption"` - UseCompression bool `json:"use_compression"` - Group string `json:"group"` - GroupKey string `json:"group_key"` + ProxyName string `json:"proxy_name"` + ProxyType string `json:"proxy_type"` + UseEncryption bool `json:"use_encryption"` + UseCompression bool `json:"use_compression"` + Group string `json:"group"` + GroupKey string `json:"group_key"` + Metas map[string]string `json:"metas"` // tcp and udp only RemotePort int `json:"remote_port"` diff --git a/models/plugin/http2https.go b/models/plugin/http2https.go index 6f5965e6..570b3f9c 100644 --- a/models/plugin/http2https.go +++ b/models/plugin/http2https.go @@ -94,7 +94,6 @@ func NewHTTP2HTTPSPlugin(params map[string]string) (Plugin, error) { return p, nil } - func (p *HTTP2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, extraBufToLocal []byte) { wrapConn := frpNet.WrapReadWriteCloserToConn(conn, realConn) p.l.PutConn(wrapConn) @@ -105,7 +104,7 @@ func (p *HTTP2HTTPSPlugin) Name() string { } func (p *HTTP2HTTPSPlugin) Close() error { - if err := p.s.Close();err != nil { + if err := p.s.Close(); err != nil { return err } return nil diff --git a/models/plugin/https2http.go b/models/plugin/https2http.go index af5b6af9..093a74f4 100644 --- a/models/plugin/https2http.go +++ b/models/plugin/https2http.go @@ -126,7 +126,7 @@ func (p *HTTPS2HTTPPlugin) Name() string { } func (p *HTTPS2HTTPPlugin) Close() error { - if err := p.s.Close();err != nil { + if err := p.s.Close(); err != nil { return err } return nil From 91e46a2c5318b66088b169d9ea91b5bd261e5bf5 Mon Sep 17 00:00:00 2001 From: fatedier Date: Fri, 20 Dec 2019 20:28:28 +0800 Subject: [PATCH 2/3] support server plugin feature --- client/proxy/proxy.go | 2 +- client/service.go | 1 + conf/frps_full.ini | 10 ++ models/config/server_common.go | 24 ++++ models/plugin/{ => client}/http2https.go | 0 models/plugin/{ => client}/http_proxy.go | 0 models/plugin/{ => client}/https2http.go | 0 models/plugin/{ => client}/plugin.go | 0 models/plugin/{ => client}/socks5.go | 0 models/plugin/{ => client}/static_file.go | 0 .../plugin/{ => client}/unix_domain_socket.go | 0 models/plugin/server/http.go | 104 +++++++++++++++++ models/plugin/server/manager.go | 105 ++++++++++++++++++ models/plugin/server/plugin.go | 32 ++++++ models/plugin/server/tracer.go | 34 ++++++ models/plugin/server/types.go | 46 ++++++++ server/control.go | 33 +++++- server/service.go | 28 ++++- 18 files changed, 410 insertions(+), 9 deletions(-) rename models/plugin/{ => client}/http2https.go (100%) rename models/plugin/{ => client}/http_proxy.go (100%) rename models/plugin/{ => client}/https2http.go (100%) rename models/plugin/{ => client}/plugin.go (100%) rename models/plugin/{ => client}/socks5.go (100%) rename models/plugin/{ => client}/static_file.go (100%) rename models/plugin/{ => client}/unix_domain_socket.go (100%) create mode 100644 models/plugin/server/http.go create mode 100644 models/plugin/server/manager.go create mode 100644 models/plugin/server/plugin.go create mode 100644 models/plugin/server/tracer.go create mode 100644 models/plugin/server/types.go diff --git a/client/proxy/proxy.go b/client/proxy/proxy.go index 268b317d..c51364f8 100644 --- a/client/proxy/proxy.go +++ b/client/proxy/proxy.go @@ -28,7 +28,7 @@ import ( "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/msg" - "github.com/fatedier/frp/models/plugin" + plugin "github.com/fatedier/frp/models/plugin/client" "github.com/fatedier/frp/models/proto/udp" "github.com/fatedier/frp/utils/limit" frpNet "github.com/fatedier/frp/utils/net" diff --git a/client/service.go b/client/service.go index 095df0aa..5ad08855 100644 --- a/client/service.go +++ b/client/service.go @@ -222,6 +222,7 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) { PrivilegeKey: util.GetAuthKey(svr.cfg.Token, now), Timestamp: now, RunId: svr.runId, + Metas: svr.cfg.Metas, } if err = msg.WriteMsg(conn, loginMsg); err != nil { diff --git a/conf/frps_full.ini b/conf/frps_full.ini index ed507cef..030a3b3a 100644 --- a/conf/frps_full.ini +++ b/conf/frps_full.ini @@ -71,3 +71,13 @@ tcp_mux = true # custom 404 page for HTTP requests # custom_404_page = /path/to/404.html + +[plugin.user-manager] +addr = 127.0.0.1:9000 +path = /handler +ops = Login + +[plugin.port-manager] +addr = 127.0.0.1:9001 +path = /handler +ops = NewProxy diff --git a/models/config/server_common.go b/models/config/server_common.go index a190a61a..df6b7a10 100644 --- a/models/config/server_common.go +++ b/models/config/server_common.go @@ -21,6 +21,7 @@ import ( ini "github.com/vaughan0/go-ini" + plugin "github.com/fatedier/frp/models/plugin/server" "github.com/fatedier/frp/utils/util" ) @@ -134,6 +135,8 @@ type ServerCommonConf struct { // UserConnTimeout specifies the maximum time to wait for a work // connection. By default, this value is 10. UserConnTimeout int64 `json:"user_conn_timeout"` + // HTTPPlugins specify the server plugins support HTTP protocol. + HTTPPlugins map[string]plugin.HTTPPluginOptions `json:"http_plugins"` } // GetDefaultServerConf returns a server configuration with reasonable @@ -167,6 +170,7 @@ func GetDefaultServerConf() ServerCommonConf { HeartBeatTimeout: 90, UserConnTimeout: 10, Custom404Page: "", + HTTPPlugins: make(map[string]plugin.HTTPPluginOptions), } } @@ -181,6 +185,8 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error return ServerCommonConf{}, err } + UnmarshalPluginsFromIni(conf, &cfg) + var ( tmpStr string ok bool @@ -375,6 +381,24 @@ func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error return } +func UnmarshalPluginsFromIni(sections ini.File, cfg *ServerCommonConf) { + for name, section := range sections { + if strings.HasPrefix(name, "plugin.") { + name = strings.TrimSpace(strings.TrimPrefix(name, "plugin.")) + options := plugin.HTTPPluginOptions{ + Name: name, + Addr: section["addr"], + Path: section["path"], + Ops: strings.Split(section["ops"], ","), + } + for i, _ := range options.Ops { + options.Ops[i] = strings.TrimSpace(options.Ops[i]) + } + cfg.HTTPPlugins[name] = options + } + } +} + func (cfg *ServerCommonConf) Check() (err error) { return } diff --git a/models/plugin/http2https.go b/models/plugin/client/http2https.go similarity index 100% rename from models/plugin/http2https.go rename to models/plugin/client/http2https.go diff --git a/models/plugin/http_proxy.go b/models/plugin/client/http_proxy.go similarity index 100% rename from models/plugin/http_proxy.go rename to models/plugin/client/http_proxy.go diff --git a/models/plugin/https2http.go b/models/plugin/client/https2http.go similarity index 100% rename from models/plugin/https2http.go rename to models/plugin/client/https2http.go diff --git a/models/plugin/plugin.go b/models/plugin/client/plugin.go similarity index 100% rename from models/plugin/plugin.go rename to models/plugin/client/plugin.go diff --git a/models/plugin/socks5.go b/models/plugin/client/socks5.go similarity index 100% rename from models/plugin/socks5.go rename to models/plugin/client/socks5.go diff --git a/models/plugin/static_file.go b/models/plugin/client/static_file.go similarity index 100% rename from models/plugin/static_file.go rename to models/plugin/client/static_file.go diff --git a/models/plugin/unix_domain_socket.go b/models/plugin/client/unix_domain_socket.go similarity index 100% rename from models/plugin/unix_domain_socket.go rename to models/plugin/client/unix_domain_socket.go diff --git a/models/plugin/server/http.go b/models/plugin/server/http.go new file mode 100644 index 00000000..155c470a --- /dev/null +++ b/models/plugin/server/http.go @@ -0,0 +1,104 @@ +// Copyright 2019 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "reflect" +) + +type HTTPPluginOptions struct { + Name string + Addr string + Path string + Ops []string +} + +type httpPlugin struct { + options HTTPPluginOptions + + url string + client *http.Client +} + +func NewHTTPPluginOptions(options HTTPPluginOptions) Plugin { + return &httpPlugin{ + options: options, + url: fmt.Sprintf("http://%s%s", options.Addr, options.Path), + client: &http.Client{}, + } +} + +func (p *httpPlugin) Name() string { + return p.options.Name +} + +func (p *httpPlugin) IsSupport(op string) bool { + for _, v := range p.options.Ops { + if v == op { + return true + } + } + return false +} + +func (p *httpPlugin) Handle(ctx context.Context, op string, content interface{}) (*Response, interface{}, error) { + r := &Request{ + Version: APIVersion, + Op: op, + Content: content, + } + var res Response + res.Content = reflect.New(reflect.TypeOf(content)).Interface() + if err := p.do(ctx, r, &res); err != nil { + return nil, nil, err + } + return &res, res.Content, nil +} + +func (p *httpPlugin) do(ctx context.Context, r *Request, res *Response) error { + buf, err := json.Marshal(r) + if err != nil { + return err + } + req, err := http.NewRequest("POST", p.url, bytes.NewReader(buf)) + if err != nil { + return err + } + req = req.WithContext(ctx) + req.Header.Set("X-Frp-Reqid", GetReqidFromContext(ctx)) + resp, err := p.client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("do http request error code: %d", resp.StatusCode) + } + buf, err = ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + if err = json.Unmarshal(buf, res); err != nil { + return err + } + return nil +} diff --git a/models/plugin/server/manager.go b/models/plugin/server/manager.go new file mode 100644 index 00000000..94642932 --- /dev/null +++ b/models/plugin/server/manager.go @@ -0,0 +1,105 @@ +// Copyright 2019 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "context" + "errors" + "fmt" + + "github.com/fatedier/frp/utils/util" + "github.com/fatedier/frp/utils/xlog" +) + +type Manager struct { + loginPlugins []Plugin + newProxyPlugins []Plugin +} + +func NewManager() *Manager { + return &Manager{ + loginPlugins: make([]Plugin, 0), + newProxyPlugins: make([]Plugin, 0), + } +} + +func (m *Manager) Register(p Plugin) { + if p.IsSupport(OpLogin) { + m.loginPlugins = append(m.loginPlugins, p) + } + if p.IsSupport(OpNewProxy) { + m.newProxyPlugins = append(m.newProxyPlugins, p) + } +} + +func (m *Manager) Login(content *LoginContent) (*LoginContent, error) { + var ( + res = &Response{ + Reject: false, + Unchange: true, + } + retContent interface{} + err error + ) + reqid, _ := util.RandId() + xl := xlog.New().AppendPrefix("reqid: " + reqid) + ctx := xlog.NewContext(context.Background(), xl) + ctx = NewReqidContext(ctx, reqid) + + for _, p := range m.loginPlugins { + res, retContent, err = p.Handle(ctx, OpLogin, *content) + if err != nil { + xl.Warn("send Login request to plugin [%s] error: %v", p.Name(), err) + return nil, errors.New("send Login request to plugin error") + } + if res.Reject { + return nil, fmt.Errorf("%s", res.RejectReason) + } + if !res.Unchange { + content = retContent.(*LoginContent) + } + } + return content, nil +} + +func (m *Manager) NewProxy(content *NewProxyContent) (*NewProxyContent, error) { + var ( + res = &Response{ + Reject: false, + Unchange: true, + } + retContent interface{} + err error + ) + reqid, _ := util.RandId() + xl := xlog.New().AppendPrefix("reqid: " + reqid) + ctx := xlog.NewContext(context.Background(), xl) + ctx = NewReqidContext(ctx, reqid) + + for _, p := range m.newProxyPlugins { + res, retContent, err = p.Handle(ctx, OpNewProxy, *content) + if err != nil { + xl.Warn("send NewProxy request to plugin [%s] error: %v", p.Name(), err) + return nil, errors.New("send NewProxy request to plugin error") + } + if res.Reject { + return nil, fmt.Errorf("%s", res.RejectReason) + } + if !res.Unchange { + content = retContent.(*NewProxyContent) + } + } + return content, nil +} diff --git a/models/plugin/server/plugin.go b/models/plugin/server/plugin.go new file mode 100644 index 00000000..fd16b145 --- /dev/null +++ b/models/plugin/server/plugin.go @@ -0,0 +1,32 @@ +// Copyright 2019 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "context" +) + +const ( + APIVersion = "0.1.0" + + OpLogin = "Login" + OpNewProxy = "NewProxy" +) + +type Plugin interface { + Name() string + IsSupport(op string) bool + Handle(ctx context.Context, op string, content interface{}) (res *Response, retContent interface{}, err error) +} diff --git a/models/plugin/server/tracer.go b/models/plugin/server/tracer.go new file mode 100644 index 00000000..2f4f2ccc --- /dev/null +++ b/models/plugin/server/tracer.go @@ -0,0 +1,34 @@ +// Copyright 2019 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "context" +) + +type key int + +const ( + reqidKey key = 0 +) + +func NewReqidContext(ctx context.Context, reqid string) context.Context { + return context.WithValue(ctx, reqidKey, reqid) +} + +func GetReqidFromContext(ctx context.Context) string { + ret, _ := ctx.Value(reqidKey).(string) + return ret +} diff --git a/models/plugin/server/types.go b/models/plugin/server/types.go new file mode 100644 index 00000000..4e392b71 --- /dev/null +++ b/models/plugin/server/types.go @@ -0,0 +1,46 @@ +// Copyright 2019 fatedier, fatedier@gmail.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package plugin + +import ( + "github.com/fatedier/frp/models/msg" +) + +type Request struct { + Version string `json:"version"` + Op string `json:"op"` + Content interface{} `json:"content"` +} + +type Response struct { + Reject bool `json:"reject"` + RejectReason string `json:"reject_reason"` + Unchange bool `json:"unchange"` + Content interface{} `json:"content"` +} + +type LoginContent struct { + msg.Login +} + +type UserInfo struct { + User string `json:"user"` + Metas map[string]string `json:"metas"` +} + +type NewProxyContent struct { + User UserInfo `json:"user"` + msg.NewProxy +} diff --git a/server/control.go b/server/control.go index 0db61987..e5e4901c 100644 --- a/server/control.go +++ b/server/control.go @@ -27,6 +27,7 @@ import ( "github.com/fatedier/frp/models/consts" frpErr "github.com/fatedier/frp/models/errors" "github.com/fatedier/frp/models/msg" + plugin "github.com/fatedier/frp/models/plugin/server" "github.com/fatedier/frp/server/controller" "github.com/fatedier/frp/server/proxy" "github.com/fatedier/frp/server/stats" @@ -86,6 +87,9 @@ type Control struct { // proxy manager pxyManager *proxy.ProxyManager + // plugin manager + pluginManager *plugin.Manager + // stats collector to store stats info of clients and proxies statsCollector stats.Collector @@ -138,9 +142,16 @@ type Control struct { ctx context.Context } -func NewControl(ctx context.Context, rc *controller.ResourceController, pxyManager *proxy.ProxyManager, - statsCollector stats.Collector, ctlConn net.Conn, loginMsg *msg.Login, - serverCfg config.ServerCommonConf) *Control { +func NewControl( + ctx context.Context, + rc *controller.ResourceController, + pxyManager *proxy.ProxyManager, + pluginManager *plugin.Manager, + statsCollector stats.Collector, + ctlConn net.Conn, + loginMsg *msg.Login, + serverCfg config.ServerCommonConf, +) *Control { poolCount := loginMsg.PoolCount if poolCount > int(serverCfg.MaxPoolCount) { @@ -149,6 +160,7 @@ func NewControl(ctx context.Context, rc *controller.ResourceController, pxyManag return &Control{ rc: rc, pxyManager: pxyManager, + pluginManager: pluginManager, statsCollector: statsCollector, conn: ctlConn, loginMsg: loginMsg, @@ -407,8 +419,21 @@ func (ctl *Control) manager() { switch m := rawMsg.(type) { case *msg.NewProxy: + content := &plugin.NewProxyContent{ + User: plugin.UserInfo{ + User: ctl.loginMsg.User, + Metas: ctl.loginMsg.Metas, + }, + NewProxy: *m, + } + var remoteAddr string + retContent, err := ctl.pluginManager.NewProxy(content) + if err == nil { + m = &retContent.NewProxy + remoteAddr, err = ctl.RegisterProxy(m) + } + // register proxy in this control - remoteAddr, err := ctl.RegisterProxy(m) resp := &msg.NewProxyResp{ ProxyName: m.ProxyName, } diff --git a/server/service.go b/server/service.go index a4d2e9df..122555af 100644 --- a/server/service.go +++ b/server/service.go @@ -33,6 +33,7 @@ import ( "github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/models/nathole" + plugin "github.com/fatedier/frp/models/plugin/server" "github.com/fatedier/frp/server/controller" "github.com/fatedier/frp/server/group" "github.com/fatedier/frp/server/ports" @@ -76,6 +77,9 @@ type Service struct { // Manage all proxies pxyManager *proxy.ProxyManager + // Manage all plugins + pluginManager *plugin.Manager + // HTTP vhost router httpVhostRouter *vhost.VhostRouters @@ -92,8 +96,9 @@ type Service struct { func NewService(cfg config.ServerCommonConf) (svr *Service, err error) { svr = &Service{ - ctlManager: NewControlManager(), - pxyManager: proxy.NewProxyManager(), + ctlManager: NewControlManager(), + pxyManager: proxy.NewProxyManager(), + pluginManager: plugin.NewManager(), rc: &controller.ResourceController{ VisitorManager: controller.NewVisitorManager(), TcpPortManager: ports.NewPortManager("tcp", cfg.ProxyBindAddr, cfg.AllowPorts), @@ -104,6 +109,12 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) { cfg: cfg, } + // Init all plugins + for name, options := range cfg.HTTPPlugins { + svr.pluginManager.Register(plugin.NewHTTPPluginOptions(options)) + log.Info("plugin [%s] has been registered", name) + } + // Init group controller svr.rc.TcpGroupCtl = group.NewTcpGroupCtl(svr.rc.TcpPortManager) @@ -295,7 +306,16 @@ func (svr *Service) HandleListener(l net.Listener) { switch m := rawMsg.(type) { case *msg.Login: - err = svr.RegisterControl(conn, m) + // server plugin hook + content := &plugin.LoginContent{ + Login: *m, + } + retContent, err := svr.pluginManager.Login(content) + if err == nil { + m = &retContent.Login + err = svr.RegisterControl(conn, m) + } + // If login failed, send error message there. // Otherwise send success message in control's work goroutine. if err != nil { @@ -384,7 +404,7 @@ func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login) (err return } - ctl := NewControl(ctx, svr.rc, svr.pxyManager, svr.statsCollector, ctlConn, loginMsg, svr.cfg) + ctl := NewControl(ctx, svr.rc, svr.pxyManager, svr.pluginManager, svr.statsCollector, ctlConn, loginMsg, svr.cfg) if oldCtl := svr.ctlManager.Add(loginMsg.RunId, ctl); oldCtl != nil { oldCtl.allShutdown.WaitDone() From 31e2cb76bb3a420b114851064ad9bc84f993bb75 Mon Sep 17 00:00:00 2001 From: fatedier Date: Mon, 23 Dec 2019 20:00:59 +0800 Subject: [PATCH 3/3] bump version --- utils/version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/version/version.go b/utils/version/version.go index dde6de96..27eb88a4 100644 --- a/utils/version/version.go +++ b/utils/version/version.go @@ -19,7 +19,7 @@ import ( "strings" ) -var version string = "0.30.0" +var version string = "0.31.0" func Full() string { return version