func (p *ProxyClient) GetRemoteConn(addr string, port int64) (c *conn.Conn, err error) { defer func() { if err != nil { c.Close() } }() c, err = conn.ConnectServer(addr, port) if err != nil { log.Error("ProxyName [%s], connect to server [%s:%d] error, %v", p.Name, addr, port, err) return } nowTime := time.Now().Unix() authKey := pcrypto.GetAuthKey(p.Name + p.AuthToken + fmt.Sprintf("%d", nowTime)) req := &msg.ControlReq{ Type: consts.NewWorkConn, ProxyName: p.Name, AuthKey: authKey, Timestamp: nowTime, } buf, _ := json.Marshal(req) err = c.Write(string(buf) + "\n") if err != nil { log.Error("ProxyName [%s], write to server error, %v", p.Name, err) return } err = nil return }
// recvive msg from reader, then encrypt msg into write func PipeEncrypt(r net.Conn, w net.Conn, key string) error { laes := new(pcrypto.Pcrypto) if err := laes.Init([]byte(key)); err != nil { log.Error("Pcrypto Init error: %v", err) return fmt.Errorf("Pcrypto Init error: %v", err) } nreader := bufio.NewReader(r) buf := make([]byte, 10*1024) for { n, err := nreader.Read(buf) if err != nil { return err } res, err := laes.Encrypt(buf[:n]) if err != nil { log.Error("Encrypt error: %v", err) return fmt.Errorf("Encrypt error: %v", err) } res = append(res, '\n') _, err = w.Write(res) if err != nil { return err } } return nil }
// decrypt msg from reader, then write into writer func PipeDecrypt(r net.Conn, w net.Conn, key string) error { laes := new(pcrypto.Pcrypto) if err := laes.Init([]byte(key)); err != nil { log.Error("Pcrypto Init error: %v", err) return fmt.Errorf("Pcrypto Init error: %v", err) } nreader := bufio.NewReader(r) for { buf, err := nreader.ReadBytes('\n') if err != nil { return err } res, err := laes.Decrypt(buf) if err != nil { log.Error("Decrypt [%s] error, %v", string(buf), err) return fmt.Errorf("Decrypt [%s] error: %v", string(buf), err) } _, err = w.Write(res) if err != nil { return err } } return nil }
func loginToServer(cli *client.ProxyClient) (c *conn.Conn, err error) { c, err = conn.ConnectServer(client.ServerAddr, client.ServerPort) if err != nil { log.Error("ProxyName [%s], connect to server [%s:%d] error, %v", cli.Name, client.ServerAddr, client.ServerPort, err) return } nowTime := time.Now().Unix() req := &msg.ControlReq{ Type: consts.NewCtlConn, ProxyName: cli.Name, UseEncryption: cli.UseEncryption, UseGzip: cli.UseGzip, PrivilegeMode: cli.PrivilegeMode, ProxyType: cli.Type, Timestamp: nowTime, } if cli.PrivilegeMode { privilegeKey := pcrypto.GetAuthKey(cli.Name + client.PrivilegeToken + fmt.Sprintf("%d", nowTime)) req.RemotePort = cli.RemotePort req.CustomDomains = cli.CustomDomains req.PrivilegeKey = privilegeKey } else { authKey := pcrypto.GetAuthKey(cli.Name + cli.AuthToken + fmt.Sprintf("%d", nowTime)) req.AuthKey = authKey } buf, _ := json.Marshal(req) err = c.Write(string(buf) + "\n") if err != nil { log.Error("ProxyName [%s], write to server error, %v", cli.Name, err) return } res, err := c.ReadLine() if err != nil { log.Error("ProxyName [%s], read from server error, %v", cli.Name, err) return } log.Debug("ProxyName [%s], read [%s]", cli.Name, res) ctlRes := &msg.ControlRes{} if err = json.Unmarshal([]byte(res), &ctlRes); err != nil { log.Error("ProxyName [%s], format server response error, %v", cli.Name, err) return } if ctlRes.Code != 0 { log.Error("ProxyName [%s], start proxy error, %s", cli.Name, ctlRes.Msg) return c, fmt.Errorf("%s", ctlRes.Msg) } log.Debug("ProxyName [%s], connect to server [%s:%d] success!", cli.Name, client.ServerAddr, client.ServerPort) return }
// loop for reading messages from frpc after control connection is established func msgReader(cli *client.ProxyClient, c *conn.Conn, msgSendChan chan interface{}) error { // for heartbeat var heartbeatTimeout bool = false timer := time.AfterFunc(time.Duration(client.HeartBeatTimeout)*time.Second, func() { heartbeatTimeout = true c.Close() log.Error("ProxyName [%s], heartbeatRes from frps timeout", cli.Name) }) defer timer.Stop() for { buf, err := c.ReadLine() if err == io.EOF || c == nil || c.IsClosed() { c.Close() log.Warn("ProxyName [%s], frps close this control conn!", cli.Name) var delayTime time.Duration = 1 // loop until reconnect to frps for { log.Info("ProxyName [%s], try to reconnect to frps [%s:%d]...", cli.Name, client.ServerAddr, client.ServerPort) c, err = loginToServer(cli) if err == nil { close(msgSendChan) msgSendChan = make(chan interface{}, 1024) go heartbeatSender(c, msgSendChan) go msgSender(cli, c, msgSendChan) break } if delayTime < 60 { delayTime = delayTime * 2 } time.Sleep(delayTime * time.Second) } continue } else if err != nil { log.Warn("ProxyName [%s], read from frps error: %v", cli.Name, err) continue } ctlRes := &msg.ControlRes{} if err := json.Unmarshal([]byte(buf), &ctlRes); err != nil { log.Warn("ProxyName [%s], parse msg from frps error: %v : %s", cli.Name, err, buf) continue } switch ctlRes.Type { case consts.HeartbeatRes: log.Debug("ProxyName [%s], receive heartbeat response", cli.Name) timer.Reset(time.Duration(client.HeartBeatTimeout) * time.Second) case consts.NoticeUserConn: log.Debug("ProxyName [%s], new user connection", cli.Name) // join local and remote connections, async go cli.StartTunnel(client.ServerAddr, client.ServerPort) default: log.Warn("ProxyName [%s}, unsupport msgType [%d]", cli.Name, ctlRes.Type) } } return nil }
func (p *ProxyClient) GetLocalConn() (c *conn.Conn, err error) { c, err = conn.ConnectServer(p.LocalIp, p.LocalPort) if err != nil { log.Error("ProxyName [%s], connect to local port error, %v", p.Name, err) } return }
func apiReload(c *gin.Context) { res := &GeneralResponse{} defer func() { buf, _ := json.Marshal(res) log.Info("Http response [/api/reload]: %s", string(buf)) }() log.Info("Http request: [/api/reload]") err := ReloadConf(ConfigFile) if err != nil { res.Code = 2 res.Msg = fmt.Sprintf("%v", err) log.Error("frps reload error: %v", err) } c.JSON(200, res) }
// loop for reading messages from frpc after control connection is established func msgReader(s *server.ProxyServer, c *conn.Conn, msgSendChan chan interface{}) error { // for heartbeat var heartbeatTimeout bool = false timer := time.AfterFunc(time.Duration(server.HeartBeatTimeout)*time.Second, func() { heartbeatTimeout = true s.Close() log.Error("ProxyName [%s], client heartbeat timeout", s.Name) }) defer timer.Stop() for { buf, err := c.ReadLine() if err != nil { if err == io.EOF { log.Warn("ProxyName [%s], client is dead!", s.Name) return err } else if c == nil || c.IsClosed() { log.Warn("ProxyName [%s], client connection is closed", s.Name) return err } log.Warn("ProxyName [%s], read error: %v", s.Name, err) continue } cliReq := &msg.ControlReq{} if err := json.Unmarshal([]byte(buf), &cliReq); err != nil { log.Warn("ProxyName [%s], parse msg from frpc error: %v : %s", s.Name, err, buf) continue } switch cliReq.Type { case consts.HeartbeatReq: log.Debug("ProxyName [%s], get heartbeat", s.Name) timer.Reset(time.Duration(server.HeartBeatTimeout) * time.Second) heartbeatRes := &msg.ControlRes{ Type: consts.HeartbeatRes, } msgSendChan <- heartbeatRes default: log.Warn("ProxyName [%s}, unsupport msgType [%d]", s.Name, cliReq.Type) } } return nil }
func ControlProcess(cli *client.ProxyClient, wait *sync.WaitGroup) { defer wait.Done() msgSendChan := make(chan interface{}, 1024) c, err := loginToServer(cli) if err != nil { log.Error("ProxyName [%s], connect to server failed!", cli.Name) return } defer c.Close() go heartbeatSender(c, msgSendChan) go msgSender(cli, c, msgSendChan) msgReader(cli, c, msgSendChan) close(msgSendChan) }
func main() { // the configures parsed from file will be replaced by those from command line if exist args, err := docopt.Parse(usage, nil, true, version.Full(), false) if args["-c"] != nil { server.ConfigFile = args["-c"].(string) } err = server.LoadConf(server.ConfigFile) if err != nil { fmt.Println(err) os.Exit(-1) } // reload check if args["--reload"] != nil { if args["--reload"].(bool) { resp, err := http.Get("http://" + server.BindAddr + ":" + fmt.Sprintf("%d", server.DashboardPort) + "/api/reload") if err != nil { fmt.Printf("frps reload error: %v\n", err) os.Exit(1) } else { defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Printf("frps reload error: %v\n", err) os.Exit(1) } res := &server.GeneralResponse{} err = json.Unmarshal(body, &res) if err != nil { fmt.Printf("http response error: %v\n", err) os.Exit(1) } else if res.Code != 0 { fmt.Printf("reload error: %s\n", res.Msg) os.Exit(1) } fmt.Printf("reload success\n") os.Exit(0) } } } if args["-L"] != nil { if args["-L"].(string) == "console" { server.LogWay = "console" } else { server.LogWay = "file" server.LogFile = args["-L"].(string) } } if args["--log-level"] != nil { server.LogLevel = args["--log-level"].(string) } if args["--addr"] != nil { addr := strings.Split(args["--addr"].(string), ":") if len(addr) != 2 { fmt.Println("--addr format error: example 0.0.0.0:7000") os.Exit(1) } bindPort, err := strconv.ParseInt(addr[1], 10, 64) if err != nil { fmt.Println("--addr format error, example 0.0.0.0:7000") os.Exit(1) } server.BindAddr = addr[0] server.BindPort = bindPort } if args["-v"] != nil { if args["-v"].(bool) { fmt.Println(version.Full()) os.Exit(0) } } log.InitLog(server.LogWay, server.LogFile, server.LogLevel, server.LogMaxDays) l, err := conn.Listen(server.BindAddr, server.BindPort) if err != nil { log.Error("Create server listener error, %v", err) os.Exit(1) } // create vhost if VhostHttpPort != 0 if server.VhostHttpPort != 0 { vhostListener, err := conn.Listen(server.BindAddr, server.VhostHttpPort) if err != nil { log.Error("Create vhost http listener error, %v", err) os.Exit(1) } server.VhostMuxer, err = vhost.NewHttpMuxer(vhostListener, 30*time.Second) if err != nil { log.Error("Create vhost httpMuxer error, %v", err) } } // create dashboard web server if DashboardPort is set, so it won't be 0 if server.DashboardPort != 0 { err := server.RunDashboardServer(server.BindAddr, server.DashboardPort) if err != nil { log.Error("Create dashboard web server error, %v", err) os.Exit(1) } } log.Info("Start frps success") ProcessControlConn(l) }