//启动逻辑
runClient(cfg)→startService(解析配置文件)→client.NewService(封装业务逻辑备用)→svr.Run(正式启动frpc)→
//全部注册逻辑
svr.login(检测服务端连通性并向服务端注册)→cm.OpenConnection(udp或者tcp或者tcp多路复用建立服务器连接)→conn, err = cm.Connect(流conn或者tcp conn)→cm.realConnect(增加dialOptions[启用tls加密,webSocketHock等])→[err = svr.authSetter.SetLogin(loginMsg)设置身份认证loginMsg err = msg.WriteMsg(conn, loginMsg)发送loginMsg 写注册信息]
//注册成功开启业务逻辑
ctl := NewControl(初始化代理控制器)→ctl.Run(解析代理配置,运行监听逻辑,运行visitor逻辑)→go ctl.worker(msgHandler,reader,writer)
→go ctl.msgHandler()
case <-hbSendCh: ctl.sendCh <- pingMsg // 将心跳消息发送到控制器的发送通道
case <-hbCheckCh: //检测心跳超时
case rawMsg, ok := <-ctl.readCh:
switch m := rawMsg.(type):
case *msg.ReqWorkConn:→go ctl.HandleReqWorkConn(m)// 处理请求工作连接消息→
m := &msg.NewWorkConn{
RunID: ctl.runID,
}
err = msg.WriteMsg(workConn, m)//发送NewWorkconn
err = msg.ReadMsgInto(workConn, &startMsg)//接收服务端的Startworkconn
ctl.pm.HandleWorkConn(startMsg.ProxyName, workConn, &startMsg)// 将工作连接分发给相关的代理进行处理
case *msg.NewProxyResp:// 处理新代理响应消息
err := ctl.pm.StartProxy(inMsg.ProxyName, inMsg.RemoteAddr, inMsg.Error)
case *msg.Pong:// 处理Pong消息,这是心跳的响应消息
ctl.lastPong = time.Now()// 更新上次接收到心跳消息的时间
→go ctl.reader()
encReader := crypto.NewReader(ctl.conn, []byte(ctl.clientCfg.Token))// 创建一个加密的读取器,用于解密从连接中读取的消息
for{m, err := msg.ReadMsg(encReader) , ctl.readCh <- m}
→go ctl.writer()
encWriter, err := crypto.NewWriter(ctl.conn, []byte(ctl.clientCfg.Token))
for{m, ok := <-ctl.sendCh , err := msg.WriteMsg(encWriter, m)}
开启代理逻辑
ctl.pm.Reload(ctl.pxyCfgs)→pxy.Start()→go pw.checkWorker()→var newProxyMsg msg.NewProxy→
_ = pw.handler(&event.StartProxyPayload{ →
NewProxyMsg: &newProxyMsg,
}
关闭代理逻辑
ctl.HandleNewProxyResp(m)→ctl.pm.StartProxy()→pxy.SetRunningStatus()→pw.pxy.Run():err→ pw.close()
pw *Wrapper.stop()→pw.close()
pm.Reload()→pxy.Start()→pw.checkWorker():else→pw.close()
closedDoneCh //frpc端关闭进程信号
Control:
sendCh: make(chan msg.Message, 100), // 发送消息的通道
readCh: make(chan msg.Message, 100), // 接收消息的通道
closedCh: make(chan struct{}), // 关闭通道
closedDoneCh: make(chan struct{}), // 关闭完成通道
var hbSendCh <-chan time.Time client心跳发送通道
var hbCheckCh <-chan time.Time client心跳接收通道
svr,errRet := client.NewService(cfg, pxyCfgs, visitorCfgs, cfgFile)//服务最大的管理器
xl := xlog.FromContextSafe(svr.ctx) //svr的全局日志
ctl := NewControl() //业务逻辑控制器
cm = NewConnectionManager(svr.ctx, &svr.cfg) // 创建连接管理器
pm.proxys[name]{pm.proxys[name]也是pw} frpc端全部proxy 管理pm 其中pm.proxys[name].cfg{举例config.TCPProxyConf}.PluginParams包含相应的plug选项 隶属于ctl
var msgCtl *jsonMsg.MsgCtl 消息管理器 实际就是封装让conn的io去读或者写
tlsConfig, err = transport.NewClientTLSConfig()//配置证书逻辑
conn, err := quic.DialAddr()&cm.quicConn = conn 与服务器建立的udp协议(当然你可以不使用udp)
Control:
readerShutdown: shutdown.New(), // 读取器关闭控制
writerShutdown: shutdown.New(), // 写入器关闭控制
msgHandlerShutdown: shutdown.New(), // 消息处理器关闭控制
ctl.pm = proxy.NewManager(ctl.ctx, ctl.sendCh, clientCfg, serverUDPPort)// 创建代理管理器,用于管理代理配置和状态
ctl.vm = NewVisitorManager(ctl.ctx, ctl)// 创建访客管理器,用于管理访客配置和状态,并加载访客配置
runServer(cfg)→server.NewService(cfg 初始化svr)→svr.Run(开启监听Listener逻辑)→svr.HandleListener(各种listener)→svr.handleConnection(ctx, frpConn)开启msg监听→
switch m := rawMsg.(type)
case *msg.Login:
RegisterControl(将客户端注册)→ctl := NewControl(注册login信息)→svr.ctlManager.Add(loginMsg.RunID, ctl)添加到svr管理器→
ctl.Start(_ = msg.WriteMsg(ctl.conn, loginRespMsg)发送resp)
for i := 0; i < ctl.poolCount; i++ {
ctl.sendCh <- &msg.ReqWorkConn{} //发送连接池数量的msg.ReqWorkConn{}
}
go ctl.writer() //从sendCh取写msg
go ctl.reader() //从readCh取读msg
go ctl.stoper() //关闭各种ch逻辑
go ctl.manager() //代理msg处理逻辑
case <-heartbeatCh: conn连接心跳超时检测
case rawMsg, ok := <-ctl.readCh:
case *msg.NewProxy:
ctl.RegisterProxy(m)→ctl.pxyManager.Add()→ctl.sendCh <- resp
case *msg.CloseProxy:
_ = ctl.CloseProxy(m)
case *msg.Ping:
ctl.sendCh <- &msg.Pong{}
case *msg.NewWorkConn:
svr.RegisterWorkConn()→ctl.RegisterWorkConn(workConn)→ctl.workConnCh <- conn加入到工作通道中→[GetWorkConnFromPool]:msg.WriteMsg(workConn, &msg.StartWorkConn{}
case *msg.NewVisitorConn:
msg.WriteMsg(conn, &msg.NewVisitorConnResp{}
Control
sendCh: make(chan msg.Message, 10),//发送ch
readCh: make(chan msg.Message, 10),//读写ch
workConnCh: make(chan net.Conn, poolCount+10),//Workconn ch
proxies: make(map[string]proxy.Proxy),//代理ch
svr, err := server.NewService(cfg)//服务最大的管理器
ctlManager: NewControlManager()//业务逻辑控制器
pxyManager: proxy.NewManager()// 代理管理器
pluginManager: plugin.NewManager()//插件管理器
rc: &controller.ResourceController{VisitorManager TCPPortManager UDPPortManager等}//资源控制器,管理资源如访问者、TCP和UDP端口等
httpVhostRouter: vhost.NewRouters()//虚拟主机路由器,用于路由HTTP请求到不同的虚拟主机
tlsConfig.NextProtos = []string{"frp"}//协议名并不会影响 TLS 的安全性,而只是标识应用层协议的一种方式(udp协议下)
encWriter, err := crypto.NewWriter(ctl.conn, []byte(ctl.clientCfg.Token)) //加密方法或者盐可能被破解
crypto.DefaultSalt = "frp"默认盐值
var rootCmd = &cobra.Command{
Use: "frps",
Short: "frps is the server of frp (https://github.com/fatedier/frp)", cmd命令输出可能导致被检测 最好能修改输出 或禁止输出
......
(
rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frps")
&showVersion: 这是一个指向 bool 类型变量的指针,用于存储命令行参数的值。
"version": 这是参数的名称,用户在命令行中通过 --version 标志来设置该参数的值。
"v": 这是参数的简短标志,用户可以使用 -v 来设置该参数的值。
false: 这是参数的默认值,如果用户没有在命令行中显式提供该参数,则使用默认值。
"version of frps": 这是参数的简要描述,用于生成帮助文档时显示给用户。
)
}
log.Info("frps started successfully") //所有的log.Info 应该被限制
quicTLSCfg.NextProtos = []string{"frp"}//协议名并不会影响 TLS 的安全性,而只是标识应用层协议的一种方式(udp协议下)
websocketPrefix := []byte("GET " + frpNet.FrpWebsocketPath) //明显的流量特征"/~!frp"
return int(data[0]) == frpNet.FRPTLSHeadByte || int(data[0]) == 0x16 //tls协议头特征 !属于pkg/util/net包!
frp(c)自带加密与压缩: frpc.ini use_encryption = true use_compression = true
自带Tls握手:
frpc:
tls_enable = true
tls_cert_file = certificate.crt
tls_key_file = certificate.key
tls_trusted_ca_file = ca.crt
frps:
tls_only = true
tls_enable = true
tls_cert_file = certificate.crt
tls_key_file = certificate.key
tls_trusted_ca_file = ca.crt
修改tls 握手协议头 pkg/util/net/dial.go[DialHookCustomTLSHeadByte] pkg/util/net/tls.go
修改/pkg/msg/msg.go json值
pkg/parse 解析ini
pxy.cfg.Plugin, pxy.cfg.PluginParams 相关plugins解析变量
client.proxy.proxy_manager HandleEvent 是一切frpc端 proxy代理信息的发送 包含checkWorker()调用的event.StartProxyPayload
client.proxy.proxy_wrapper.go var newProxyMsg msg.NewProxy 新建proxyMsg
pw.Cfg.MarshalToMsg(&newProxyMsg)为msg解析调用处
server.control RegisterProxy() pxyConf frps注册代理
client.proxy.proxy_manager ctl.pm.StartProxy frpc代理请求 由ctl.HandleNewProxyResp()发起
plugin解析链路
client.control.go ctl.HandleNewProxyResp(m)→client.control.go (ctl *Control) HandleNewProxyResp ctl.pm.StartProxy→client.proxy.proxy_wrapper.go (pm *Manager) StartProxy pxy.SetRunningStatus()→client.proxy.proxy_wrapper.go (pw *Wrapper) SetRunningStatus pw.pxy.Run()→client.proxy.proxy.go (pxy *TCPProxy) Run()
pkg.config.proxy.go LocalSvrConf struct中 包含 Plugin类型以及PluginParams相关选项