mirror of
https://github.com/fatedier/frp.git
synced 2025-07-09 07:19:54 +07:00
First available commit
This commit is contained in:
70
pkg/models/client.go
Normal file
70
pkg/models/client.go
Normal file
@ -0,0 +1,70 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"frp/pkg/utils/conn"
|
||||
"frp/pkg/utils/log"
|
||||
)
|
||||
|
||||
type ProxyClient struct {
|
||||
Name string
|
||||
Passwd string
|
||||
LocalPort int64
|
||||
}
|
||||
|
||||
func (p *ProxyClient) GetLocalConn() (c *conn.Conn, err error) {
|
||||
c = &conn.Conn{}
|
||||
err = c.ConnectServer("127.0.0.1", p.LocalPort)
|
||||
if err != nil {
|
||||
log.Error("ProxyName [%s], connect to local port error, %v", p.Name, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (p *ProxyClient) GetRemoteConn(addr string, port int64) (c *conn.Conn, err error) {
|
||||
c = &conn.Conn{}
|
||||
defer func(){
|
||||
if err != nil {
|
||||
c.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
err = c.ConnectServer(addr, port)
|
||||
if err != nil {
|
||||
log.Error("ProxyName [%s], connect to server [%s:%d] error, %v", p.Name, addr, port, err)
|
||||
return
|
||||
}
|
||||
|
||||
req := &ClientCtlReq{
|
||||
Type: WorkConn,
|
||||
ProxyName: p.Name,
|
||||
Passwd: p.Passwd,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (p *ProxyClient) StartTunnel(serverAddr string, serverPort int64) (err error) {
|
||||
localConn, err := p.GetLocalConn()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
remoteConn, err := p.GetRemoteConn(serverAddr, serverPort)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("Join two conns, (l[%s] r[%s]) (l[%s] r[%s])", localConn.GetLocalAddr(), localConn.GetRemoteAddr(),
|
||||
remoteConn.GetLocalAddr(), remoteConn.GetRemoteAddr())
|
||||
go conn.Join(localConn, remoteConn)
|
||||
return nil
|
||||
}
|
27
pkg/models/msg.go
Normal file
27
pkg/models/msg.go
Normal file
@ -0,0 +1,27 @@
|
||||
package models
|
||||
|
||||
type GeneralRes struct {
|
||||
Code int64 `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
// type
|
||||
const (
|
||||
ControlConn = iota
|
||||
WorkConn
|
||||
)
|
||||
|
||||
type ClientCtlReq struct {
|
||||
Type int64 `json:"type"`
|
||||
ProxyName string `json:"proxy_name"`
|
||||
Passwd string `json:"passwd"`
|
||||
}
|
||||
|
||||
type ClientCtlRes struct {
|
||||
GeneralRes
|
||||
}
|
||||
|
||||
|
||||
type ServerCtlReq struct {
|
||||
Type int64 `json:"type"`
|
||||
}
|
116
pkg/models/server.go
Normal file
116
pkg/models/server.go
Normal file
@ -0,0 +1,116 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"container/list"
|
||||
|
||||
"frp/pkg/utils/conn"
|
||||
"frp/pkg/utils/log"
|
||||
)
|
||||
|
||||
const (
|
||||
Idle = iota
|
||||
Working
|
||||
)
|
||||
|
||||
type ProxyServer struct {
|
||||
Name string
|
||||
Passwd string
|
||||
BindAddr string
|
||||
ListenPort int64
|
||||
|
||||
Status int64
|
||||
Listener *conn.Listener // accept new connection from remote users
|
||||
CtlMsgChan chan int64 // every time accept a new user conn, put "1" to the channel
|
||||
CliConnChan chan *conn.Conn // get client conns from control goroutine
|
||||
UserConnList *list.List // store user conns
|
||||
Mutex sync.Mutex
|
||||
}
|
||||
|
||||
func (p *ProxyServer) Init() {
|
||||
p.Status = Idle
|
||||
p.CtlMsgChan = make(chan int64)
|
||||
p.CliConnChan = make(chan *conn.Conn)
|
||||
p.UserConnList = list.New()
|
||||
}
|
||||
|
||||
func (p *ProxyServer) Lock() {
|
||||
p.Mutex.Lock()
|
||||
}
|
||||
|
||||
func (p *ProxyServer) Unlock() {
|
||||
p.Mutex.Unlock()
|
||||
}
|
||||
|
||||
// start listening for user conns
|
||||
func (p *ProxyServer) Start() (err error) {
|
||||
p.Listener, err = conn.Listen(p.BindAddr, p.ListenPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Status = Working
|
||||
|
||||
// start a goroutine for listener
|
||||
go func() {
|
||||
for {
|
||||
// block
|
||||
c := p.Listener.GetConn()
|
||||
log.Debug("ProxyName [%s], get one new user conn [%s]", p.Name, c.GetRemoteAddr())
|
||||
|
||||
// put to list
|
||||
p.Lock()
|
||||
if p.Status != Working {
|
||||
log.Debug("ProxyName [%s] is not working, new user conn close", p.Name)
|
||||
c.Close()
|
||||
p.Unlock()
|
||||
return
|
||||
}
|
||||
p.UserConnList.PushBack(c)
|
||||
p.Unlock()
|
||||
|
||||
// put msg to control conn
|
||||
p.CtlMsgChan <- 1
|
||||
}
|
||||
}()
|
||||
|
||||
// start another goroutine for join two conns from client and user
|
||||
go func() {
|
||||
for {
|
||||
cliConn := <-p.CliConnChan
|
||||
p.Lock()
|
||||
element := p.UserConnList.Front()
|
||||
|
||||
var userConn *conn.Conn
|
||||
if element != nil {
|
||||
userConn = element.Value.(*conn.Conn)
|
||||
p.UserConnList.Remove(element)
|
||||
} else {
|
||||
cliConn.Close()
|
||||
continue
|
||||
}
|
||||
p.Unlock()
|
||||
|
||||
// msg will transfer to another without modifying
|
||||
log.Debug("Join two conns, (l[%s] r[%s]) (l[%s] r[%s])", cliConn.GetLocalAddr(), cliConn.GetRemoteAddr(),
|
||||
userConn.GetLocalAddr(), userConn.GetRemoteAddr())
|
||||
go conn.Join(cliConn, userConn)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *ProxyServer) Close() {
|
||||
p.Lock()
|
||||
p.Status = Idle
|
||||
p.CtlMsgChan = make(chan int64)
|
||||
p.CliConnChan = make(chan *conn.Conn)
|
||||
p.UserConnList = list.New()
|
||||
p.Unlock()
|
||||
}
|
||||
|
||||
func (p *ProxyServer) WaitUserConn() (res int64) {
|
||||
res = <-p.CtlMsgChan
|
||||
return
|
||||
}
|
115
pkg/utils/conn/conn.go
Normal file
115
pkg/utils/conn/conn.go
Normal file
@ -0,0 +1,115 @@
|
||||
package conn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"bufio"
|
||||
"sync"
|
||||
"io"
|
||||
|
||||
"frp/pkg/utils/log"
|
||||
)
|
||||
|
||||
type Listener struct {
|
||||
Addr net.Addr
|
||||
Conns chan *Conn
|
||||
}
|
||||
|
||||
// wait util get one
|
||||
func (l *Listener) GetConn() (conn *Conn) {
|
||||
conn = <-l.Conns
|
||||
return conn
|
||||
}
|
||||
|
||||
type Conn struct {
|
||||
TcpConn *net.TCPConn
|
||||
Reader *bufio.Reader
|
||||
}
|
||||
|
||||
func (c *Conn) ConnectServer(host string, port int64) (err error) {
|
||||
servertAddr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", host, port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn, err := net.DialTCP("tcp", nil, servertAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.TcpConn = conn
|
||||
c.Reader = bufio.NewReader(c.TcpConn)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) GetRemoteAddr() (addr string) {
|
||||
return c.TcpConn.RemoteAddr().String()
|
||||
}
|
||||
|
||||
func (c *Conn) GetLocalAddr() (addr string) {
|
||||
return c.TcpConn.LocalAddr().String()
|
||||
}
|
||||
|
||||
func (c *Conn) ReadLine() (buff string, err error) {
|
||||
buff, err = c.Reader.ReadString('\n')
|
||||
return buff, err
|
||||
}
|
||||
|
||||
func (c *Conn) Write(content string) (err error) {
|
||||
_, err = c.TcpConn.Write([]byte(content))
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Conn) Close() {
|
||||
c.TcpConn.Close()
|
||||
}
|
||||
|
||||
func Listen(bindAddr string, bindPort int64) (l *Listener, err error) {
|
||||
tcpAddr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", bindAddr, bindPort))
|
||||
listener, err := net.ListenTCP("tcp", tcpAddr)
|
||||
if err != nil {
|
||||
return l, err
|
||||
}
|
||||
|
||||
l = &Listener{
|
||||
Addr: listener.Addr(),
|
||||
Conns: make(chan *Conn),
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
conn, err := listener.AcceptTCP()
|
||||
if err != nil {
|
||||
log.Error("Accept new tcp connection error, %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
c := &Conn{
|
||||
TcpConn: conn,
|
||||
}
|
||||
c.Reader = bufio.NewReader(c.TcpConn)
|
||||
l.Conns <- c
|
||||
}
|
||||
}()
|
||||
return l, err
|
||||
}
|
||||
|
||||
// will block until conn close
|
||||
func Join(c1 *Conn, c2 *Conn) {
|
||||
var wait sync.WaitGroup
|
||||
pipe := func(to *Conn, from *Conn) {
|
||||
defer to.Close()
|
||||
defer from.Close()
|
||||
defer wait.Done()
|
||||
|
||||
var err error
|
||||
_, err = io.Copy(to.TcpConn, from.TcpConn)
|
||||
if err != nil {
|
||||
log.Warn("join conns error, %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
wait.Add(2)
|
||||
go pipe(c1, c2)
|
||||
go pipe(c2, c1)
|
||||
wait.Wait()
|
||||
return
|
||||
}
|
64
pkg/utils/log/log.go
Normal file
64
pkg/utils/log/log.go
Normal file
@ -0,0 +1,64 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego/logs"
|
||||
)
|
||||
|
||||
var Log *logs.BeeLogger
|
||||
|
||||
func init() {
|
||||
Log = logs.NewLogger(1000)
|
||||
Log.EnableFuncCallDepth(true)
|
||||
Log.SetLogFuncCallDepth(Log.GetLogFuncCallDepth() + 1)
|
||||
}
|
||||
|
||||
func InitLog(logWay string, logFile string, logLevel string) {
|
||||
SetLogFile(logWay, logFile)
|
||||
SetLogLevel(logLevel)
|
||||
}
|
||||
|
||||
// logWay: such as file or console
|
||||
func SetLogFile(logWay string, logFile string) {
|
||||
if logWay == "console" {
|
||||
Log.SetLogger("console", "")
|
||||
} else {
|
||||
Log.SetLogger("file", `{"filename": "` + logFile + `"}`)
|
||||
}
|
||||
}
|
||||
|
||||
// value: error, warning, info, debug
|
||||
func SetLogLevel(logLevel string) {
|
||||
level := 4 // warning
|
||||
|
||||
switch logLevel {
|
||||
case "error":
|
||||
level = 3
|
||||
case "warn":
|
||||
level = 4
|
||||
case "info":
|
||||
level = 6
|
||||
case "debug":
|
||||
level = 7
|
||||
default:
|
||||
level = 4
|
||||
}
|
||||
|
||||
Log.SetLevel(level)
|
||||
}
|
||||
|
||||
// wrap log
|
||||
func Error(format string, v ...interface{}) {
|
||||
Log.Error(format, v...)
|
||||
}
|
||||
|
||||
func Warn(format string, v ...interface{}) {
|
||||
Log.Warn(format, v...)
|
||||
}
|
||||
|
||||
func Info(format string, v ...interface{}) {
|
||||
Log.Info(format, v...)
|
||||
}
|
||||
|
||||
func Debug(format string, v ...interface{}) {
|
||||
Log.Debug(format, v...)
|
||||
}
|
Reference in New Issue
Block a user