├── .gitignore
├── .vscode
├── launch.json
└── settings.json
├── LICENSE
├── README.md
├── forward-agent
├── AgentClient.go
├── AgentService.go
├── AgentServiceV1.go
├── build_win.bat
├── conf
│ └── app.conf
├── go.mod
├── go.sum
└── main.go
├── forward-core
├── Common
│ └── KcpPipe.go
├── Constant
│ ├── MagicCmd.go
│ └── RunStatus.go
├── Models
│ ├── ForwardConfig.go
│ ├── ForwardInfo.go
│ ├── FuncResult.go
│ ├── LoginUser.go
│ ├── PageData.go
│ ├── PageParam.go
│ ├── PortForward.go
│ ├── PortInfo.go
│ └── SysUser.go
├── NetUtils
│ ├── BytesUtil.go
│ ├── HttpUtil.go
│ └── NetTool.go
├── Utils
│ ├── CollectionUtil.go
│ ├── CryptoUtil.go
│ ├── DateUtil.go
│ ├── JsonUtil.go
│ ├── MathUtil.go
│ ├── StringUtil.go
│ └── SysUtil.go
├── go.mod
├── go.sum
└── ikcp
│ ├── ikcp.go
│ ├── ikcp_h.go
│ ├── ikcp_test.go
│ └── ikcp_test_h.go
├── forward-server
├── Controllers
│ ├── BaseCtrl
│ │ ├── ApiCtrl.go
│ │ ├── ConsoleCtrl.go
│ │ └── WebCtrl.go
│ ├── DefaultCtrl.go
│ ├── ForwardCtrl.go
│ ├── LoginCtrl.go
│ ├── RestApiCtrl.go
│ └── UCenterCtrl.go
├── Service
│ ├── ConsoleServer.go
│ ├── ForWardJob.go
│ ├── ForwardClient.go
│ ├── ForwardServer.go
│ ├── InitServices.go
│ ├── MagicClient.go
│ ├── MagicServer.go
│ ├── MagicServiceV1.go
│ ├── SysDataService.go
│ └── UdpForward.go
├── build_linux.bat
├── build_linux.sh
├── build_mac.bat
├── build_win.bat
├── conf
│ ├── app.conf
│ └── data.conf
├── data
│ └── data.db
├── go.mod
├── go.sum
├── main.go
├── routers
│ ├── commentsRouter_.go
│ ├── commentsRouter_Controllers.go
│ └── router.go
├── static
│ ├── js
│ │ ├── jquery.min.js
│ │ ├── moment.min.js
│ │ └── timer.min.js
│ └── layui
│ │ ├── css
│ │ ├── layui.css
│ │ ├── layui.mobile.css
│ │ └── modules
│ │ │ ├── code.css
│ │ │ ├── laydate
│ │ │ ├── default
│ │ │ │ └── laydate.css
│ │ │ ├── icon.png
│ │ │ └── laydate.css
│ │ │ └── layer
│ │ │ └── default
│ │ │ ├── icon-ext.png
│ │ │ ├── icon.png
│ │ │ ├── layer.css
│ │ │ ├── loading-0.gif
│ │ │ ├── loading-1.gif
│ │ │ └── loading-2.gif
│ │ ├── font
│ │ ├── iconfont.eot
│ │ ├── iconfont.svg
│ │ ├── iconfont.ttf
│ │ ├── iconfont.woff
│ │ └── iconfont.woff2
│ │ ├── images
│ │ └── face
│ │ │ ├── 0.gif
│ │ │ ├── 1.gif
│ │ │ ├── 10.gif
│ │ │ ├── 11.gif
│ │ │ ├── 12.gif
│ │ │ ├── 13.gif
│ │ │ ├── 14.gif
│ │ │ ├── 15.gif
│ │ │ ├── 16.gif
│ │ │ ├── 17.gif
│ │ │ ├── 18.gif
│ │ │ ├── 19.gif
│ │ │ ├── 2.gif
│ │ │ ├── 20.gif
│ │ │ ├── 21.gif
│ │ │ ├── 22.gif
│ │ │ ├── 23.gif
│ │ │ ├── 24.gif
│ │ │ ├── 25.gif
│ │ │ ├── 26.gif
│ │ │ ├── 27.gif
│ │ │ ├── 28.gif
│ │ │ ├── 29.gif
│ │ │ ├── 3.gif
│ │ │ ├── 30.gif
│ │ │ ├── 31.gif
│ │ │ ├── 32.gif
│ │ │ ├── 33.gif
│ │ │ ├── 34.gif
│ │ │ ├── 35.gif
│ │ │ ├── 36.gif
│ │ │ ├── 37.gif
│ │ │ ├── 38.gif
│ │ │ ├── 39.gif
│ │ │ ├── 4.gif
│ │ │ ├── 40.gif
│ │ │ ├── 41.gif
│ │ │ ├── 42.gif
│ │ │ ├── 43.gif
│ │ │ ├── 44.gif
│ │ │ ├── 45.gif
│ │ │ ├── 46.gif
│ │ │ ├── 47.gif
│ │ │ ├── 48.gif
│ │ │ ├── 49.gif
│ │ │ ├── 5.gif
│ │ │ ├── 50.gif
│ │ │ ├── 51.gif
│ │ │ ├── 52.gif
│ │ │ ├── 53.gif
│ │ │ ├── 54.gif
│ │ │ ├── 55.gif
│ │ │ ├── 56.gif
│ │ │ ├── 57.gif
│ │ │ ├── 58.gif
│ │ │ ├── 59.gif
│ │ │ ├── 6.gif
│ │ │ ├── 60.gif
│ │ │ ├── 61.gif
│ │ │ ├── 62.gif
│ │ │ ├── 63.gif
│ │ │ ├── 64.gif
│ │ │ ├── 65.gif
│ │ │ ├── 66.gif
│ │ │ ├── 67.gif
│ │ │ ├── 68.gif
│ │ │ ├── 69.gif
│ │ │ ├── 7.gif
│ │ │ ├── 70.gif
│ │ │ ├── 71.gif
│ │ │ ├── 8.gif
│ │ │ └── 9.gif
│ │ ├── lay
│ │ ├── dest
│ │ │ └── layui.all.js
│ │ └── modules
│ │ │ ├── carousel.js
│ │ │ ├── code.js
│ │ │ ├── colorpicker.js
│ │ │ ├── element.js
│ │ │ ├── flow.js
│ │ │ ├── form.js
│ │ │ ├── jquery.js
│ │ │ ├── laydate.js
│ │ │ ├── layedit.js
│ │ │ ├── layer.js
│ │ │ ├── laypage.js
│ │ │ ├── laytpl.js
│ │ │ ├── mobile.js
│ │ │ ├── rate.js
│ │ │ ├── slider.js
│ │ │ ├── table.js
│ │ │ ├── transfer.js
│ │ │ ├── tree.js
│ │ │ ├── upload.js
│ │ │ └── util.js
│ │ ├── layui.all.js
│ │ └── layui.js
└── views
│ ├── index.html
│ ├── login.html
│ └── ucenter
│ ├── addBatchForward.html
│ ├── apiDoc.html
│ ├── changePwd.html
│ ├── footer.html
│ ├── forwardForm.html
│ ├── forwardList.1.html
│ ├── forwardList.html
│ ├── header.html
│ ├── importForward.html
│ ├── index.html
│ ├── layout.html
│ ├── main.html
│ └── netAgent.html
├── go.mod
├── go.sum
└── screenshot
├── ApiDoc.png
├── List.png
├── Login.png
├── Tras_1.png
├── Tras_2.png
└── edit.png
/.gitignore:
--------------------------------------------------------------------------------
1 | logs
2 | *.log
3 |
4 | .idea
5 | .temp
6 |
7 | *.log
8 | *.iml
9 | *.sum
10 |
11 | /*.sum
12 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "forward-server",
9 | "type": "go",
10 | "request": "launch",
11 | "mode": "auto",
12 | "program": "${workspaceFolder}/forward-server",
13 | "env": {},
14 | "args": []
15 | },
16 | {
17 | "name": "forward-agent",
18 | "type": "go",
19 | "request": "launch",
20 | "mode": "auto",
21 | "program": "${workspaceFolder}/forward-agent",
22 | "env": {},
23 | "args": []
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "go.formatTool": "goimports",
3 | "go.alternateTools": {
4 |
5 | },
6 | "[go]": {
7 | "editor.formatOnSave": true,
8 | "editor.codeActionsOnSave": {
9 | "source.organizeImports": true,
10 | },
11 | // Optional: Disable snippets, as they conflict with completion ranking.
12 | "editor.snippetSuggestions": "none",
13 | },
14 | "[go.mod]": {
15 | "editor.formatOnSave": true,
16 | "editor.codeActionsOnSave": {
17 | "source.organizeImports": true,
18 | },
19 | },
20 | "go.gotoSymbol.includeImports": true,
21 | "go.gotoSymbol.includeGoroot": true,
22 | "go.languageServerExperimentalFeatures": {
23 |
24 | "diagnostics": true,
25 | "documentLink": true
26 | },
27 | "go.trace.server": "verbose",
28 | "go.overwriteGoplsMiddleware": {
29 |
30 | },
31 | "go.useLanguageServer": true
32 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # port-forward
3 | Go语言开发的端口转发工具 for port data forward
4 |
5 | 官网地址:
6 | https://gitee.com/tavenli/port-forward
7 |
8 | ```
9 | 开发语言:GO
10 | 控制台框架:beego
11 | 数据库:sqlite3
12 | ```
13 |
14 | # 最近更新
15 | ```
16 | v1.3.6 发布,重新增加 TCP端口数据分发功能
17 | v1.3.5 发布,增加批量导入、批量添加规则
18 | v1.3.3 发布,增加 一键开启所有转发 和 一键关闭所有转发
19 | v1.3.2 发布,服务稳定性已经过长时间的验证
20 | v1.3.1 发布,增加程序启动自动开启转发
21 | v1.2.9_beta 发布,完善点对点转发的稳定性,支持TCP和UDP协议转发
22 | ```
23 |
24 | # 最新编译好的版本下载:
25 | [https://gitee.com/tavenli/port-forward/releases](https://gitee.com/tavenli/port-forward/releases)
26 |
27 |
28 |
29 | # 功能介绍
30 |
31 | > 支持Web控制台添加端口映射
32 |
33 | > 支持对每条端口映射进行开启和关闭控制
34 |
35 | > 支持 RestfulAPI 接口,方便被其它系统集成
36 |
37 | > 支持每条端口转发的同时,再分发给多个端口,满足某些测试场景
38 |
39 | > 类似企业交换机的功能,即软交换机,主要是方便企业网络维护人员或开发人员
40 |
41 |
42 | # 使用交流群
43 |
44 | > 使用问题或个性化需求可加QQ号:17020415 (申请时请备注:端口转发)
45 |
46 | # 快速安装说明
47 | 1. 下载编译好的程序包,并解压程序包
48 | 2. 执行 start.sh (Linux)或 start.bat (Win)命令
49 | 3. 打开浏览器,进入控制台,打开 http://127.0.0.1:8080/login
50 | 4. 输入用户 admin 密码 123456 进入控制台
51 |
52 |
53 | # 控制台UI
54 | 
55 |
56 |
57 | 
58 |
59 |
60 | 
61 |
62 |
63 | 
64 |
65 |
--------------------------------------------------------------------------------
/forward-agent/AgentClient.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "net"
4 |
5 | type AgentClient struct {
6 |
7 | cid string
8 | encode, decode func([]byte) []byte
9 | authed bool
10 | conn net.Conn
11 |
12 | }
--------------------------------------------------------------------------------
/forward-agent/AgentService.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | type AgentService struct {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/forward-agent/AgentServiceV1.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "forward-core/Constant"
6 | "forward-core/NetUtils"
7 | "github.com/astaxie/beego/logs"
8 | "io"
9 | "net"
10 | "strconv"
11 | "time"
12 | )
13 |
14 | type AgentServiceV1 struct {
15 | localConnMap map[int]net.Conn
16 | MagicServerAddr string
17 | AgentOnline bool
18 | }
19 |
20 |
21 | func (_self *AgentServiceV1) ConnToMagicServer() {
22 | serviceConn, err := net.DialTimeout("tcp", _self.MagicServerAddr, 30*time.Second)
23 |
24 | if err != nil {
25 | logs.Error("try dial err", err)
26 | _self.AgentOnline = false
27 | return
28 | }
29 |
30 | callback := func(conn net.Conn, sessionId int, cmd byte, payload []byte) {
31 | //payload 收到的消息内容
32 | _self.OnTunnelRecv(conn, sessionId, cmd, payload)
33 |
34 | }
35 | logs.Debug("开始接收服务端返回指令或数据...")
36 | _self.AgentOnline = true
37 | go NetUtils.ReadConn(serviceConn, callback)
38 | }
39 |
40 | func (_self *AgentServiceV1) OnTunnelRecv(conn net.Conn, sessionId int, cmd byte, payload []byte) {
41 | logs.Debug("收到一条给 sessionId:", sessionId, " 客户端的数据,指令是:", cmd)
42 |
43 | switch cmd {
44 | case Constant.MagicCmd_AgentListenerOpen:
45 | targetAddr := string(payload)
46 | go _self.ListenForClient(targetAddr, _self.MagicServerAddr)
47 | case Constant.MagicCmd_AgentConnOpen:
48 | targetAddr := string(payload)
49 | logs.Debug("sessionId:", sessionId, " 收到 AgentConnOpen 指令是,打开本地连接:", targetAddr)
50 | //AgentConnOpen 让连接进来的客户端,在它的本地创建一个连接,并关联好sessionId
51 | localConn, err := net.DialTimeout("tcp", targetAddr, 30*time.Second)
52 | if err != nil {
53 | logs.Error("try dial err", err)
54 | return
55 | }
56 | _self.localConnMap[sessionId] = localConn
57 | //接收 localConn 返回数据,并将返回的数据,写回给 conn,带上 sessionId
58 | go _self.ReadRawConn(localConn, conn, sessionId, Constant.MagicCmd_DataToMagic)
59 |
60 | case Constant.MagicCmd_DataToAgent:
61 | logs.Debug("sessionId:", sessionId, " 收到 MsgToAgent 指令")
62 | localConn := _self.localConnMap[sessionId]
63 | localConn.Write(payload)
64 | logs.Debug("sessionId:", sessionId, " 数据已写入本地目标连接")
65 | case Constant.MagicCmd_Refused:
66 | //client := string(payload)
67 | logs.Debug("Magic服务端拒绝本次连接")
68 | }
69 |
70 | }
71 |
72 | func (_self *AgentServiceV1) ReadRawConn(from net.Conn, magic_client_Conn net.Conn, sessionId int, cmd byte) {
73 |
74 | arr := make([]byte, 5000)
75 | reader := bufio.NewReader(from)
76 |
77 | for {
78 | size, err := reader.Read(arr)
79 | if err != nil {
80 | break
81 | }
82 |
83 | err = NetUtils.WriteConn(magic_client_Conn, sessionId, cmd, arr[0:size])
84 |
85 | if err != nil {
86 | //有异常
87 | logs.Error(err)
88 | break
89 | }
90 | }
91 | }
92 |
93 | func (_self *AgentServiceV1) ListenForClient(localListenAddr, toAddr string) {
94 | client_listener, err := net.Listen("tcp", localListenAddr)
95 | if err != nil {
96 | logs.Error("ListenForClient err:", err)
97 | return
98 | }
99 |
100 | for {
101 | logs.Debug("ListenForClient Ready to Accept ...")
102 | client_Conn, err := client_listener.Accept()
103 | if err != nil {
104 | logs.Error("Accept err:", err)
105 | break
106 | }
107 |
108 | //连接到远程服务
109 | serviceConn, err := net.DialTimeout("tcp", toAddr, 30*time.Second)
110 |
111 | if err != nil {
112 | logs.Error("try dial err", err)
113 | return
114 | }
115 |
116 | go func() {
117 | _, err = io.Copy(serviceConn, client_Conn)
118 | if err != nil {
119 | logs.Error("to magic_client 网络连接异常:", err)
120 | }
121 | }()
122 |
123 | go func() {
124 | _, err = io.Copy(client_Conn, serviceConn)
125 | if err != nil {
126 | logs.Error("to magic_client 网络连接异常2:", err)
127 | }
128 | }()
129 | }
130 |
131 | }
132 |
133 | func (_self *AgentServiceV1) ConnectToMagicLoop() {
134 | //客户端与服务端建立连接
135 | if !_self.AgentOnline {
136 | _self.ConnToMagicServer()
137 | delay := 3
138 | time.AfterFunc(time.Second*time.Duration(delay), func() {
139 | _self.ConnectToMagicLoop()
140 | })
141 | logs.Debug("reConnect after " + strconv.Itoa(delay) + " seconds")
142 | }
143 |
144 | }
145 |
--------------------------------------------------------------------------------
/forward-agent/build_win.bat:
--------------------------------------------------------------------------------
1 |
2 |
3 | echo "Build For windows..."
4 | set GOOS=windows
5 | set GOARCH=amd64
6 |
7 | go build -o forward-agent.exe
8 |
9 | echo "--------- Build For windows Success!"
10 |
11 |
12 | pause
13 |
14 |
--------------------------------------------------------------------------------
/forward-agent/conf/app.conf:
--------------------------------------------------------------------------------
1 |
2 | magic.server = "127.0.0.1:7000"
3 |
4 |
--------------------------------------------------------------------------------
/forward-agent/go.mod:
--------------------------------------------------------------------------------
1 | module forward-agent
2 |
3 | go 1.14
4 |
5 | require (
6 | forward-core v0.0.0-00010101000000-000000000000
7 | github.com/astaxie/beego v1.12.2
8 | )
9 |
10 | replace forward-core => ../forward-core
11 |
--------------------------------------------------------------------------------
/forward-agent/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "forward-core/Utils"
5 | "github.com/astaxie/beego"
6 | "github.com/astaxie/beego/logs"
7 | )
8 |
9 | func main() {
10 |
11 | logs.SetLogger(logs.AdapterConsole, `{"level":7}`)
12 | logs.SetLogger(logs.AdapterFile, `{"filename":"app.log","level":7,"maxlines":0,"maxsize":0,"daily":true,"maxdays":10}`)
13 |
14 | //输出文件名和行号
15 | logs.EnableFuncCallDepth(true)
16 | logs.SetLogFuncCallDepth(3)
17 | //为了让日志输出不影响性能,开启异步日志
18 | logs.Async()
19 |
20 | //远端外网服务
21 | magicServerAddr := beego.AppConfig.String("magic.server")
22 |
23 | logs.Debug("★★★★★★★★★★★★★★★★★★★★")
24 | logs.Debug(" tcp-forward 启动")
25 | logs.Debug("")
26 | logs.Debug("项目地址:https://github.com/tavenli/port-forward")
27 | logs.Debug("")
28 | logs.Debug("请求远端控制服务器:", magicServerAddr)
29 | logs.Debug("")
30 | logs.Debug("★★★★★★★★★★★★★★★★★★★★")
31 |
32 | if Utils.IsEmpty(magicServerAddr){
33 | magicServerAddr = "forward.apiclub.top:7000"
34 | }
35 |
36 | agentService := new(AgentServiceV1)
37 | agentService.MagicServerAddr = magicServerAddr
38 | agentService.AgentOnline = false
39 |
40 | go agentService.ConnToMagicServer()
41 |
42 | go agentService.ConnectToMagicLoop()
43 |
44 | select {
45 |
46 | }
47 |
48 |
49 | }
--------------------------------------------------------------------------------
/forward-core/Constant/MagicCmd.go:
--------------------------------------------------------------------------------
1 | package Constant
2 |
3 | const (
4 | MagicCmd_AuthFail =byte(iota)
5 | MagicCmd_ReqAuth
6 | MagicCmd_Refused
7 | MagicCmd_AgentListenerOpen
8 | MagicCmd_AgentConnOpen
9 | MagicCmd_AgentConnClose
10 | MagicCmd_DataToMagic
11 | MagicCmd_DataToAgent
12 | )
--------------------------------------------------------------------------------
/forward-core/Constant/RunStatus.go:
--------------------------------------------------------------------------------
1 | package Constant
2 |
3 | const (
4 | RunStatus_Stoped =byte(iota)
5 | RunStatus_Running
6 | )
--------------------------------------------------------------------------------
/forward-core/Models/ForwardConfig.go:
--------------------------------------------------------------------------------
1 | package Models
2 |
3 | type ForwardConfig struct {
4 | RuleId int
5 | Name string
6 | Status int
7 | SrcAddr string
8 | SrcPort int
9 | Protocol string
10 | DestAddr string
11 | DestPort int
12 | Others string
13 | }
14 |
--------------------------------------------------------------------------------
/forward-core/Models/ForwardInfo.go:
--------------------------------------------------------------------------------
1 | package Models
2 |
3 | type ForwardInfo struct {
4 |
5 | Name string
6 | Status byte
7 | SrcAddr string
8 | SrcPort int
9 | Protocol string
10 | DestAddr string
11 | DestPort int
12 | OnlineCount int
13 | Clients []string
14 |
15 | }
--------------------------------------------------------------------------------
/forward-core/Models/FuncResult.go:
--------------------------------------------------------------------------------
1 | package Models
2 |
3 | type FuncResult struct {
4 | Code int32
5 | Msg string
6 | Data interface{}
7 | }
8 |
--------------------------------------------------------------------------------
/forward-core/Models/LoginUser.go:
--------------------------------------------------------------------------------
1 | package Models
2 |
3 | type LoginUser struct {
4 | UserId int
5 | UserName string
6 | }
7 |
--------------------------------------------------------------------------------
/forward-core/Models/PageData.go:
--------------------------------------------------------------------------------
1 | package Models
2 |
3 | type PageData struct {
4 | PIndex int64
5 | PSize int64
6 | TotalRows int64
7 | Pages int64
8 | Data interface{}
9 | }
10 |
--------------------------------------------------------------------------------
/forward-core/Models/PageParam.go:
--------------------------------------------------------------------------------
1 | package Models
2 |
3 | import (
4 | "forward-core/Utils"
5 | )
6 |
7 | type PageParam struct {
8 | PIndex int64
9 | PSize int64
10 | // 要排序的字段名
11 | Sort string
12 | // ASC 或 DESC
13 | Direction string
14 | }
15 |
16 | func (this *PageParam) PageSize() int {
17 |
18 | return int(this.PSize)
19 |
20 | }
21 |
22 | // 分页, 排序处理
23 | func (this *PageParam) SkipRows() int {
24 | this.PIndex = Utils.If(this.PIndex < 1, 1, this.PIndex).(int64)
25 | this.PSize = Utils.If(this.PSize < 1, 5, this.PSize).(int64)
26 |
27 | skipRows := (this.PIndex - 1) * this.PSize
28 | return int(skipRows)
29 | }
30 |
31 | func (this *PageParam) SortField() string {
32 | var sortField string
33 | if !Utils.IsEmpty(this.Sort) {
34 | if !Utils.IsEmpty(this.Direction) && this.Direction == "DESC" {
35 | //降序
36 | sortField = "-" + this.Sort
37 | } else {
38 | //升序
39 | sortField = this.Sort
40 |
41 | }
42 | }
43 |
44 | return sortField
45 | }
46 |
--------------------------------------------------------------------------------
/forward-core/Models/PortForward.go:
--------------------------------------------------------------------------------
1 | package Models
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/astaxie/beego/orm"
7 | )
8 |
9 | type PortForward struct {
10 | Id int `orm:"column(id);pk;auto"`
11 | Name string `orm:"column(name);size(256);null"`
12 | // 0:禁用,1:启用
13 | Status int `orm:"column(status);null"`
14 | Addr string `orm:"column(addr);size(256);null"`
15 | // 端口号
16 | Port int `orm:"column(port);null"`
17 | //协议
18 | Protocol string `orm:"column(protocol);size(32);null"`
19 | TargetAddr string `orm:"column(targetAddr);size(256);null"`
20 | // 端口号
21 | TargetPort int `orm:"column(targetPort);null"`
22 | CreateTime time.Time `orm:"column(createTime);type(datetime)"`
23 | //暂时用来存放端口分发配置,后续版本再调整
24 | Others string `orm:"column(others);size(500);null"`
25 | // 0:普通映射,1:内网穿透映射(Server->Client),2:内网穿透反向映射(Client->Server)
26 | FType int `orm:"column(fType);null"`
27 | }
28 |
29 | func (t *PortForward) TableName() string {
30 | return "t_port_forward"
31 | }
32 |
33 | func init() {
34 | orm.RegisterModel(new(PortForward))
35 | }
36 |
--------------------------------------------------------------------------------
/forward-core/Models/PortInfo.go:
--------------------------------------------------------------------------------
1 | package Models
2 |
3 | type PortInfo struct {
4 |
5 | Addr string
6 | Port int
7 | }
--------------------------------------------------------------------------------
/forward-core/Models/SysUser.go:
--------------------------------------------------------------------------------
1 | package Models
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/astaxie/beego/orm"
7 | )
8 |
9 | type SysUser struct {
10 | Id int `orm:"column(id);pk;auto"`
11 | UserName string `orm:"column(userName);null"`
12 | PassWord string `orm:"column(passWord);null"`
13 | // 0:禁用,1:启用
14 | Status int `orm:"column(status);null"`
15 | CreateTime time.Time `orm:"column(createTime);type(datetime)"`
16 | }
17 |
18 | func (t *SysUser) TableName() string {
19 | return "t_sys_user"
20 | }
21 |
22 | func init() {
23 | orm.RegisterModel(new(SysUser))
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/forward-core/NetUtils/BytesUtil.go:
--------------------------------------------------------------------------------
1 | package NetUtils
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 |
7 | "github.com/axgle/mahonia"
8 | )
9 |
10 | func WriteBYTE(data *bytes.Buffer, val uint8) {
11 | //BYTE 长度:1
12 | buf := make([]byte, 1)
13 | buf[0] = byte(val)
14 |
15 | data.Write(buf)
16 | }
17 |
18 | func WriteWORD(data *bytes.Buffer, val uint16) {
19 | //WORD 长度:2
20 | buf := make([]byte, 2)
21 | buf[0] = byte(val)
22 | buf[1] = byte(val >> 8)
23 |
24 | data.Write(buf)
25 | }
26 |
27 | func WriteDWORD(data *bytes.Buffer, val uint32) {
28 | //DWORD 长度:4
29 | buf := make([]byte, 4)
30 | buf[0] = byte(val)
31 | buf[1] = byte(val >> 8)
32 | buf[2] = byte(val >> 16)
33 | buf[3] = byte(val >> 24)
34 |
35 | data.Write(buf)
36 | }
37 |
38 | func WriteTCHAR(data *bytes.Buffer, size int, val string) {
39 | //TCHAR 长度:由size指定
40 | buf := []byte(val)
41 | data.Write(buf)
42 | //
43 | for i := 0; i < size-len(buf); i++ {
44 | //剩余的补0
45 | data.WriteByte(0)
46 | }
47 |
48 | }
49 |
50 | func WriteUnicodeTCHAR(data *bytes.Buffer, size int, val string) {
51 | //Unicode TCHAR 长度:size*2
52 | realSize := size * 2
53 | buf := []byte(val)
54 | dataBuffer := make([]byte, realSize)
55 | k := 0
56 | for j := 0; j < len(buf); j++ {
57 | dataBuffer[k] = buf[j]
58 | dataBuffer[k+1] = byte(0)
59 | k = k + 2
60 | }
61 |
62 | data.Write(dataBuffer)
63 | //
64 |
65 | }
66 |
67 | func WriteInt(data *bytes.Buffer, val int) {
68 | //Byte 长度:4
69 | buf := make([]byte, 4)
70 | buf[0] = byte(val)
71 | buf[1] = byte(val >> 8)
72 | buf[2] = byte(val >> 16)
73 | buf[3] = byte(val >> 24)
74 |
75 | data.Write(buf)
76 | }
77 |
78 | func _ReadInt_(data *bytes.Buffer, val []byte) (int, error) {
79 |
80 | return data.Read(val)
81 | }
82 |
83 | func ReadWord(val []byte) uint16 {
84 | //binary.LittleEndian.Uint16(rData[4:6])
85 | return binary.LittleEndian.Uint16(val)
86 | }
87 |
88 | func ReadDWord(val []byte) uint32 {
89 | return binary.LittleEndian.Uint32(val)
90 | }
91 |
92 | func ReadTCHAR(val []byte) string {
93 | return string(val)
94 | }
95 |
96 | func UTF8_To_GBK(input string) string {
97 | enc := mahonia.NewEncoder("GBK")
98 | return enc.ConvertString(input)
99 | }
100 |
101 | func GBK_To_UTF8(input string) string {
102 | dec := mahonia.NewDecoder("UTF-8")
103 | return dec.ConvertString(input)
104 | }
105 |
106 | func U2W(input string) string {
107 | //网狐荣耀版本的服务端TCHAR编码需要这样转换
108 | dec := mahonia.NewDecoder("UTF-16LE")
109 | return dec.ConvertString(input)
110 | }
111 |
--------------------------------------------------------------------------------
/forward-core/NetUtils/HttpUtil.go:
--------------------------------------------------------------------------------
1 | package NetUtils
2 |
3 | import (
4 | "errors"
5 | "forward-core/Utils"
6 | "io/ioutil"
7 | "net/http"
8 | "net/url"
9 | "strings"
10 |
11 | "bytes"
12 |
13 | "github.com/astaxie/beego"
14 | "github.com/astaxie/beego/logs"
15 | )
16 |
17 | func GetIP(c *beego.Controller) string {
18 | //utils.GetIP(&c.Controller)
19 | //也可以直接用 c.Ctx.Input.IP() 取真实IP
20 | ip := c.Ctx.Request.Header.Get("X-Real-IP")
21 | if ip != "" {
22 | return ip
23 | }
24 |
25 | ip = c.Ctx.Request.Header.Get("Remote_addr")
26 | if ip == "" {
27 | ip = c.Ctx.Request.RemoteAddr
28 | }
29 | return ip
30 | }
31 |
32 | func HttpGet(url string) (string, error) {
33 | resp, err := http.Get(url)
34 | if err != nil {
35 | logs.Error("HttpGet error: ", err)
36 | return "", err
37 | }
38 |
39 | if resp == nil {
40 | return "", errors.New("返回对象为空")
41 | }
42 |
43 | defer resp.Body.Close()
44 | result := ""
45 | body, err := ioutil.ReadAll(resp.Body)
46 | if err == nil {
47 | result = string(body)
48 | //logs.Debug("HttpGet result: ", result)
49 | } else {
50 | logs.Error("HttpGet error: ", err)
51 | }
52 |
53 | return result, nil
54 | }
55 |
56 | func HttpPostJsonReturnByte(url string, json string) ([]byte, error) {
57 | resp, err := http.Post(url, "application/json", strings.NewReader(json))
58 | if err != nil {
59 | logs.Error("HttpPostJson error: ", err)
60 | return nil, err
61 | }
62 |
63 | if resp == nil {
64 | return nil, errors.New("返回对象为空")
65 | }
66 |
67 | defer resp.Body.Close()
68 |
69 | body, err := ioutil.ReadAll(resp.Body)
70 | if err == nil {
71 | return body, err
72 | //logs.Debug("HttpPostJson result: ", result)
73 | } else {
74 | logs.Error("HttpPostJson error: ", err)
75 | return nil, err
76 | }
77 |
78 | }
79 |
80 | func HttpPost(url string, param map[string]string) (string, error) {
81 |
82 | var paramBuf bytes.Buffer
83 | paramBuf.WriteString("curTime=" + Utils.GetCurrentTime())
84 | for k, v := range param {
85 | paramBuf.WriteString("&" + k + "=" + v)
86 | }
87 |
88 | resp, err := http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(paramBuf.String()))
89 | if err != nil {
90 | logs.Error("HttpPost error: ", err)
91 | return "", err
92 | }
93 |
94 | if resp == nil {
95 | return "", errors.New("返回对象为空")
96 | }
97 |
98 | defer resp.Body.Close()
99 | result := ""
100 | body, err := ioutil.ReadAll(resp.Body)
101 | if err == nil {
102 | result = string(body)
103 | logs.Debug("HttpPost result: ", result)
104 | } else {
105 | logs.Error("HttpPost error: ", err)
106 | }
107 |
108 | return result, nil
109 | }
110 |
111 | func UrlEncode(input string) string {
112 | if Utils.IsEmpty(input) {
113 | return ""
114 | }
115 | return url.QueryEscape(input)
116 | }
117 |
118 | func UrlDecode(input string) string {
119 | if Utils.IsEmpty(input) {
120 | return ""
121 | }
122 | result, err := url.QueryUnescape(input)
123 | if err != nil {
124 | return input
125 | } else {
126 | return result
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/forward-core/NetUtils/NetTool.go:
--------------------------------------------------------------------------------
1 | package NetUtils
2 |
3 | import (
4 | "bufio"
5 | "encoding/binary"
6 | "errors"
7 | "github.com/astaxie/beego/logs"
8 | "io"
9 | "net"
10 | "forward-core/Common"
11 | )
12 |
13 | func NewTCP(addr string) (net.Listener, error) {
14 | tcpSocket, err := net.Listen("tcp", addr)
15 | if err != nil {
16 | return nil, err
17 | }
18 |
19 | return tcpSocket,nil
20 |
21 | }
22 | func NewUDP(addr string) (*net.UDPConn, error) {
23 | udpAddr, err := net.ResolveUDPAddr("udp", addr)
24 | if err != nil {
25 | return nil, err
26 | }
27 | udpSocket, _err := net.ListenUDP("udp", udpAddr)
28 | if _err != nil {
29 | return nil, _err
30 | }
31 |
32 | return udpSocket,nil
33 |
34 | }
35 |
36 | func NewKCP(addr string, setting *Common.KcpSetting) (*Common.UDPListener, error) {
37 | return Common.NewKCP(addr, setting)
38 | }
39 |
40 |
41 | func DataCopy(dst io.Writer, src io.Reader) (written int64, err error) {
42 | return io.Copy(dst, src)
43 | }
44 |
45 | func MultiDataCopy(src io.Reader, dispatchConns []io.Writer) (written int64, err error) {
46 |
47 | mWriter := io.MultiWriter(dispatchConns...)
48 | return io.Copy(mWriter, src)
49 |
50 | }
51 |
52 | type ReadCallBack func(conn net.Conn, id int, cmd byte, arg []byte)
53 |
54 | func ReadConn(conn net.Conn, callback ReadCallBack) {
55 | scanner := bufio.NewScanner(conn)
56 | scanner.Split(func(data []byte, atEOF bool) (adv int, token []byte, err error) {
57 | return NetSplitV1(data, atEOF, conn, callback)
58 | })
59 | for scanner.Scan() {
60 | }
61 | if scanner.Err() != nil {
62 | logs.Error(scanner.Err())
63 | }
64 | }
65 |
66 | func NetSplitV1(data []byte, atEOF bool, conn net.Conn, callback ReadCallBack) (adv int, token []byte, err error) {
67 | l := len(data)
68 | if l < 6 {
69 | return 0, nil, nil
70 | }
71 |
72 | if l > 100000 {
73 | conn.Close()
74 | return 0, nil, errors.New("max data")
75 | }
76 | var id int
77 | var cmd byte
78 | id = int(int32(data[0]) | int32(data[1])<<8 | int32(data[2])<<16 | int32(data[3])<<24)
79 | cmd = data[4]
80 | isShort := data[5]
81 | var payload []byte
82 | var offset int
83 | if isShort == 1 {
84 | offset = 6
85 | } else {
86 | if l < 10 {
87 | return 0, nil, nil
88 | }
89 | ls := binary.LittleEndian.Uint32(data[6:])
90 | tail := l - 10
91 | if tail < int(ls) {
92 | return 0, nil, nil
93 | }
94 | payload = data[10 : 10+ls]
95 | offset = 10 + int(ls)
96 | }
97 |
98 | callback(conn, id, cmd, payload)
99 |
100 | return offset, []byte{}, nil
101 | }
102 |
103 | func WriteConn(conn net.Conn, id int, cmd byte, payload []byte) error {
104 | if conn == nil {
105 | return nil
106 | }
107 | l := len(payload)
108 | var buf []byte
109 | var size int
110 | if l > 0 {
111 | size = 10 + l
112 | //4+1+1+4 id cmd isShort
113 | buf = make([]byte, size)
114 | } else {
115 | size = 6
116 | //4+1+1 id cmd isShort
117 | buf = make([]byte, size)
118 | }
119 | buf[0] = byte(id)
120 | buf[1] = byte(id >> 8)
121 | buf[2] = byte(id >> 16)
122 | buf[3] = byte(id >> 24)
123 | buf[4] = cmd
124 | if l > 0 {
125 | buf[5] = 0
126 | binary.LittleEndian.PutUint32(buf[6:], uint32(l))
127 | copy(buf[10:], []byte(payload))
128 | } else {
129 | buf[5] = 1
130 | }
131 | _, err := conn.Write(buf[:size])
132 | if err != nil {
133 | logs.Error("conn.Write error:", err)
134 | }
135 | return err
136 | }
137 |
--------------------------------------------------------------------------------
/forward-core/Utils/CollectionUtil.go:
--------------------------------------------------------------------------------
1 | package Utils
2 |
3 | func IntArrayFind(slice []int, value int) int {
4 | for p, v := range slice {
5 | if v == value {
6 | return p
7 | }
8 | }
9 | return -1
10 | }
11 |
12 | func IntArrayContain(slice []int, value int) bool {
13 | for _, v := range slice {
14 | if v == value {
15 | return true
16 | }
17 | }
18 | return false
19 | }
20 |
21 | func Int64ArrayFind(slice []int64, value int64) int {
22 | for p, v := range slice {
23 | if v == value {
24 | return p
25 | }
26 | }
27 | return -1
28 | }
29 |
30 | func Int64ArrayContain(slice []int64, value int64) bool {
31 | for _, v := range slice {
32 | if v == value {
33 | return true
34 | }
35 | }
36 | return false
37 | }
38 |
39 | func StrArrayContain(slice []string, value string) bool {
40 | for _, v := range slice {
41 | if v == value {
42 | return true
43 | }
44 | }
45 | return false
46 | }
47 |
--------------------------------------------------------------------------------
/forward-core/Utils/CryptoUtil.go:
--------------------------------------------------------------------------------
1 | package Utils
2 |
3 | import (
4 | "crypto/md5"
5 | "encoding/hex"
6 | "fmt"
7 | "io"
8 | )
9 |
10 | //生成32位md5字串
11 | func GetMd5(input string) string {
12 | hash := md5.New()
13 | hash.Write([]byte(input))
14 | return hex.EncodeToString(hash.Sum(nil))
15 | }
16 |
17 | func GetSaltMD5(input, salt string) string {
18 | hash := md5.New()
19 | //salt = "salt123456" //盐值
20 | io.WriteString(hash, input+salt)
21 | result := fmt.Sprintf("%x", hash.Sum(nil))
22 | return result
23 | }
24 |
--------------------------------------------------------------------------------
/forward-core/Utils/DateUtil.go:
--------------------------------------------------------------------------------
1 | package Utils
2 |
3 | import (
4 | "fmt"
5 | "math"
6 | "time"
7 | )
8 |
9 | // GO的诞辰
10 | const timeLayout = "2006-01-02 15:04:05"
11 |
12 | // 取当前系统时间
13 | func GetTimeNow() time.Time {
14 | return time.Now()
15 | }
16 |
17 | func GetTime(timeStr string) time.Time {
18 | toTime, _ := ToTime(timeStr)
19 | return toTime
20 | }
21 |
22 | func JavaLongTime(javaLong int64) time.Time {
23 | //1492566520958 -> 2017-04-19 09:48:40
24 | //fmt.Println(time.Unix(1492566520958/1000, 0))
25 | //fmt.Println(time.Unix(0, 1492566520958*1000000))
26 | return time.Unix(0, javaLong*1000000)
27 | }
28 |
29 | func ToTime(timeStr string) (time.Time, error) {
30 | loc, _ := time.LoadLocation("Local")
31 | toTime, err := time.ParseInLocation(timeLayout, timeStr, loc)
32 | //toTime, err := time.Parse(timeLayout, timeStr)
33 | return toTime, err
34 |
35 | }
36 |
37 | func ToTimeByFm(timeStr string, format string) (time.Time, error) {
38 | loc, _ := time.LoadLocation("Local")
39 | toTime, err := time.ParseInLocation(format, timeStr, loc)
40 | //toTime, err := time.Parse(timeLayout, timeStr)
41 | return toTime, err
42 |
43 | }
44 |
45 | //要想格式化为:yyyyMMddHHmmss
46 | //则 format = "20060102150405"
47 | //要想格式化为:yyyy-MM-dd HH:mm:ss
48 | //则 format = "2006-01-02 15:04:05"
49 | //要想格式化为:yyyy-MM-dd
50 | //则 format = "2006-01-02"
51 | func FormatTimeByFm(t time.Time, format string) string {
52 |
53 | return t.Format(format)
54 | }
55 |
56 | func GetCurrentTime() string {
57 | return FormatTime(time.Now())
58 | }
59 |
60 | func GetCurrentDay() string {
61 | return FormatTimeByFm(time.Now(), "2006-01-02")
62 | }
63 |
64 | func FormatTime(t time.Time) string {
65 | //
66 | return FormatTimeByFm(t, "2006-01-02 15:04:05")
67 | }
68 |
69 | func FormatTimeToNum(t time.Time) string {
70 | //
71 | return FormatTimeByFm(t, "20060102150405")
72 | }
73 |
74 | // 在当前时间之前
75 | func IsBeforeNow(t time.Time) (result bool) {
76 | result = false
77 | if &t != nil && t.Before(time.Now()) {
78 | result = true
79 | }
80 | return
81 | }
82 |
83 | // 在当前时间之后
84 | func IsAfterNow(t time.Time) (result bool) {
85 | result = false
86 | if &t != nil && t.After(time.Now()) {
87 | result = true
88 | }
89 | return
90 | }
91 |
92 | func SubDateTime(firstTime time.Time, secondTime time.Time) (result time.Duration) {
93 | result = time.Duration(0)
94 | if &firstTime != nil && &secondTime != nil {
95 | result = secondTime.Sub(firstTime)
96 | }
97 | return
98 | }
99 |
100 | func DifferDays(firstTime time.Time, secondTime time.Time) int64 {
101 | result := SubDateTime(firstTime, secondTime).Hours()
102 | return int64(math.Abs(result) / 24)
103 | }
104 |
105 | func DifferHour(firstTime time.Time, secondTime time.Time) int64 {
106 | result := SubDateTime(firstTime, secondTime).Hours()
107 | //return int64(result) 两个时间的先后顺序不一样,可能出现负数
108 | return int64(math.Abs(result))
109 | }
110 |
111 | func DifferMin(firstTime time.Time, secondTime time.Time) int64 {
112 | result := SubDateTime(firstTime, secondTime).Minutes()
113 | return int64(math.Abs(result))
114 | }
115 |
116 | func DifferSec(firstTime time.Time, secondTime time.Time) int64 {
117 | result := SubDateTime(firstTime, secondTime).Seconds()
118 | return int64(math.Abs(result))
119 | }
120 |
121 | // 24小时前的时间
122 | func Before24h() time.Time {
123 | t, _ := time.ParseDuration("-24h")
124 | return time.Now().Add(t)
125 | }
126 |
127 | func AddSecs(_time time.Time, secs int64) time.Time {
128 | t, _ := time.ParseDuration("1s")
129 | return time.Now().Add(t * time.Duration(secs))
130 | }
131 |
132 | /*
133 | 增加10分钟:utils.AddMins(time.Now(), 10)
134 | 减少5分钟:utils.AddMins(time.Now(), -5)
135 | */
136 | func AddMins(_time time.Time, mins int64) time.Time {
137 | t, _ := time.ParseDuration("1m")
138 | return time.Now().Add(t * time.Duration(mins))
139 | }
140 |
141 | func AddHours(_time time.Time, hours int64) time.Time {
142 | t, _ := time.ParseDuration("1h")
143 | return time.Now().Add(t * time.Duration(hours))
144 | }
145 |
146 | func AddDays(_time time.Time, days int) time.Time {
147 | return _time.AddDate(0, 0, days)
148 | }
149 |
150 | func AddMonths(_time time.Time, months int) time.Time {
151 | return _time.AddDate(0, months, 0)
152 | }
153 |
154 | func GetBeginTime(_time time.Time) time.Time {
155 | //2017-06-28 00:00:00 +0800 CST
156 | return GetBeginTimeByLoc(_time, time.Local)
157 | //return GetBeginTimeByLoc(_time, time.UTC)
158 |
159 | }
160 |
161 | func GetEndTime(_time time.Time) time.Time {
162 | //2017-06-28 23:59:59.999999999 +0800 CST
163 | return GetEndTimeByLoc(_time, time.Local)
164 | }
165 |
166 | func GetBeginTimeByLoc(_time time.Time, loc *time.Location) time.Time {
167 | year, month, day := _time.Date()
168 | return time.Date(year, month, day, 0, 0, 0, 0, loc)
169 |
170 | }
171 |
172 | func GetEndTimeByLoc(_time time.Time, loc *time.Location) time.Time {
173 | year, month, day := _time.Date()
174 | return time.Date(year, month, day, 23, 59, 59, 999999999, loc)
175 | }
176 |
177 | // 一行代码计算代码执行时间
178 | // defer utils.TimeCost(time.Now())
179 | func TimeCost(start time.Time) {
180 | terminal := time.Since(start)
181 | fmt.Println("TimeCost:", terminal)
182 | }
183 |
--------------------------------------------------------------------------------
/forward-core/Utils/JsonUtil.go:
--------------------------------------------------------------------------------
1 | package Utils
2 |
3 | import (
4 | "bytes"
5 | "encoding/gob"
6 | "encoding/json"
7 | "fmt"
8 | "github.com/json-iterator/go"
9 | "reflect"
10 |
11 | "github.com/astaxie/beego/logs"
12 | "github.com/ugorji/go/codec"
13 | )
14 |
15 | var jsonIter = jsoniter.ConfigCompatibleWithStandardLibrary
16 |
17 | func ToJson(obj interface{}) ([]byte, error) {
18 | return json.Marshal(obj)
19 | }
20 |
21 | func FromJson(data []byte, t interface{}) error {
22 | return json.Unmarshal(data, t)
23 | }
24 |
25 | func ToJsonIterator(obj interface{}) ([]byte, error) {
26 | return jsonIter.Marshal(obj)
27 | }
28 |
29 | func FromJsonIterator(data []byte, t interface{}) error {
30 | return jsonIter.Unmarshal(data, t)
31 | }
32 |
33 | func MsgpEncode(obj interface{}) []byte {
34 | var mh codec.MsgpackHandle
35 | mh.MapType = reflect.TypeOf(obj)
36 | var buf bytes.Buffer
37 | enc := codec.NewEncoder(&buf, &mh)
38 | err := enc.Encode(obj)
39 | if err == nil {
40 | return buf.Bytes()
41 | } else {
42 | logs.Error(err)
43 | return nil
44 | }
45 |
46 | }
47 |
48 | func MsgpDecode(data []byte, obj interface{}) error {
49 | var mh codec.MsgpackHandle
50 | mh.MapType = reflect.TypeOf(obj)
51 | dec := codec.NewDecoder(bytes.NewReader(data), &mh)
52 | err := dec.Decode(&obj)
53 | return err
54 |
55 | }
56 |
57 | func DeepCopy(dst, src interface{}) error {
58 | var buf bytes.Buffer
59 | if err := gob.NewEncoder(&buf).Encode(src); err != nil {
60 | return err
61 | }
62 | return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
63 | }
64 |
65 | func ShowObjAllProp(obj interface{}) {
66 | value_method := reflect.ValueOf(obj)
67 | obj_type := value_method.Type()
68 |
69 | fmt.Printf("输出对象的属性和方法\t%v\n", obj)
70 |
71 | fmt.Println("\tMethods...")
72 |
73 | for i := 0; i < value_method.NumMethod(); i++ {
74 | fmt.Printf("\t%d\t%s\n", i, obj_type.Method(i).Name)
75 | }
76 |
77 | value_element := reflect.ValueOf(obj).Elem()
78 | obj_element := value_element.Type()
79 |
80 | fmt.Println("\tFields...")
81 | for i := 0; i < value_element.NumField(); i++ {
82 | fmt.Printf("\t%d\t%s\n", i, obj_element.Field(i).Name)
83 |
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/forward-core/Utils/MathUtil.go:
--------------------------------------------------------------------------------
1 | package Utils
2 |
3 | import (
4 | "math"
5 | )
6 |
7 | func AbsInt(num float64) int {
8 | //result := math.Abs(float64(num))
9 | result := math.Abs(num)
10 | return int(result)
11 | }
12 |
13 | func AbsInt64(num float64) int64 {
14 | result := math.Abs(num)
15 | return int64(result)
16 | }
17 |
18 | func CeilInt(num float64) int {
19 | result := math.Ceil(num)
20 | return int(result)
21 | }
22 |
23 | func CeilInt64(num float64) int64 {
24 | //CeilInt64(5.9) = 6
25 | //CeilInt64(5.3) = 6
26 | //CeilInt64(5) = 5
27 | result := math.Ceil(num)
28 | return int64(result)
29 | }
30 |
31 | func Float64ToInt64(num float64) int64 {
32 | return int64(num)
33 | }
34 |
35 | func Float64TryToInt64(num interface{}) int64 {
36 | return int64(num.(float64))
37 | }
38 |
39 | // 返回最大值
40 | func MaxInt(a, b int) int {
41 | if a > b {
42 | return a
43 | }
44 | return b
45 | }
46 |
47 | // 返回最小值
48 | func MinInt(a, b int) int {
49 | if a < b {
50 | return a
51 | }
52 | return b
53 | }
54 |
55 | func Pages(total, psize int64) int64 {
56 |
57 | pages := float64(total) / float64(psize)
58 | result := math.Ceil(pages)
59 | return int64(result)
60 | }
61 |
--------------------------------------------------------------------------------
/forward-core/Utils/StringUtil.go:
--------------------------------------------------------------------------------
1 | package Utils
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "regexp"
7 | "strconv"
8 | "strings"
9 | )
10 |
11 | func ToInt(str string) int {
12 | _num, _ := strconv.Atoi(str)
13 | return _num
14 | }
15 |
16 | func ToInt64(str string) int64 {
17 | _num, _ := strconv.ParseInt(str, 10, 64)
18 | return _num
19 | }
20 |
21 | func ToInteger(str string) (int, error) {
22 | _num, _err := strconv.Atoi(str)
23 | return _num, _err
24 | }
25 |
26 | func ToLong(str string) (int64, error) {
27 | _num, _err := strconv.ParseInt(str, 10, 64)
28 | return _num, _err
29 | }
30 |
31 | func ToFloat64(str string) (float64, error) {
32 | _num, _err := strconv.ParseFloat(str, 64)
33 | return _num, _err
34 | }
35 |
36 | func BinaryToInt(str string) (int64, error) {
37 | _num, _err := strconv.ParseInt(str, 2, 64)
38 | return _num, _err
39 | }
40 |
41 | func IntToBinary(num int64) string {
42 | bin := strconv.FormatInt(num, 2)
43 | return bin
44 | }
45 |
46 | func IsBinaryOverInt(binStr string, number int64) bool {
47 | _num, _ := strconv.ParseInt(binStr, 2, 64)
48 | return (_num & number) == number
49 | }
50 |
51 | func IsBinNumOverInt(binNum int64, number int64) bool {
52 |
53 | return (binNum & number) == number
54 | }
55 |
56 | func ToStr(_num int) string {
57 | return strconv.Itoa(_num)
58 | }
59 |
60 | func FormatInt(_num int) string {
61 | return strconv.FormatInt(int64(_num), 10)
62 | }
63 |
64 | func FormatInt64(_num int64) string {
65 | return strconv.FormatInt(_num, 10)
66 | }
67 |
68 | func FormatFloat64(_num float64) string {
69 | return strconv.FormatFloat(_num, 'f', 2, 64)
70 | }
71 |
72 | func IsEmpty(str string) bool {
73 |
74 | return Len(str) <= 0
75 | }
76 |
77 | func IsNotEmpty(str string) bool {
78 |
79 | return !IsEmpty(str)
80 | }
81 |
82 | func Replace(str string, find string, to string) string {
83 |
84 | return strings.Replace(str, find, to, 1)
85 | }
86 |
87 | func ReplaceAll(str string, find string, to string) string {
88 |
89 | return strings.Replace(str, find, to, -1)
90 | }
91 |
92 | func Split(str string, spChar string) []string {
93 |
94 | return strings.Split(str, spChar)
95 | }
96 |
97 | func Contains(str string, find string) bool {
98 |
99 | return strings.Contains(str, find)
100 | }
101 |
102 | // strings.HasPrefix("ABC_xyz", "ABC")
103 | func StartsWith(str string, find string) bool {
104 |
105 | return strings.HasPrefix(str, find)
106 | }
107 |
108 | // strings.HasSuffix("ABC_xyz", "xyz")
109 | func EndsWith(str string, find string) bool {
110 |
111 | return strings.HasSuffix(str, find)
112 | }
113 |
114 | // strings.Count("cheese", "e") = 3
115 | func Count(str string, find string) int {
116 |
117 | return strings.Count(str, find)
118 | }
119 |
120 | // 返回第一个匹配字符的位置,返回-1为未找到
121 | // strings.Index("ABC_xyz", "xyz") = 4
122 | // strings.Index("ABC_xyz", "B") = 1
123 | func Index(str string, find string) int {
124 |
125 | return strings.Index(str, find)
126 | }
127 |
128 | //strings.Join(arrays, ",") = "foo, bar, bas"
129 | func Join(strs []string, spChar string) string {
130 |
131 | return strings.Join(strs, spChar)
132 | }
133 |
134 | // 字母转为小写
135 | // strings.ToLower("Love GoLang") = "love golang"
136 | func ToLower(str string) string {
137 |
138 | return strings.ToLower(str)
139 | }
140 |
141 | // 字母转为大写
142 | // strings.ToTitle("love 中国") = "LOVE 中国"
143 | func ToUpper(str string) string {
144 | return strings.ToUpper(str)
145 | //return strings.ToTitle(str)
146 | }
147 |
148 | func Len(str string) int {
149 |
150 | return len(str)
151 | }
152 |
153 | func Print(str string) {
154 | //var show = fmt.Println
155 | //show(str)
156 | fmt.Println(str)
157 | }
158 |
159 | func FilterByRegex(expr, input, placeTo string) string {
160 | regx, _ := regexp.Compile(expr)
161 | return regx.ReplaceAllString(input, placeTo)
162 | }
163 |
164 | func FilterStyle(input string) string {
165 | //regx, _ := regexp.Compile("")
167 | return regx.ReplaceAllString(input, "")
168 | }
169 |
170 | func FilterScript(input string) string {
171 | //regx, _ := regexp.Compile("")
173 | return regx.ReplaceAllString(input, "")
174 | }
175 |
176 | func FilterHtml(input string) string {
177 | regx, _ := regexp.Compile("<.+?>")
178 | //regx, _ := regexp.Compile("\\<[\\S\\s]+?\\>")
179 | return regx.ReplaceAllString(input, "")
180 | }
181 |
182 | func FilterA(input string) string {
183 |
184 | regx, _ := regexp.Compile("<.?a(.|\n)*?>")
185 | return regx.ReplaceAllString(input, "")
186 | }
187 |
188 | func FilterImage(input string) string {
189 |
190 | regx, _ := regexp.Compile("")
191 | return regx.ReplaceAllString(input, "")
192 | }
193 |
194 | func FilterSpecialChar(input string) string {
195 |
196 | regx, _ := regexp.Compile("[+=|{}':;',]")
197 | return regx.ReplaceAllString(input, "")
198 | }
199 |
200 | func FilterUrlPrefix(input string) string {
201 |
202 | regx, _ := regexp.Compile("\\w+://")
203 | return regx.ReplaceAllString(input, "")
204 | }
205 |
206 | func IsNumber(input string) bool {
207 |
208 | match, _ := regexp.MatchString("^\\d+$", input)
209 | return match
210 | }
211 |
212 | func IsIP(input string) bool {
213 |
214 | match, _ := regexp.MatchString("^((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$", input)
215 | return match
216 | }
217 |
218 | func IsEMail(input string) bool {
219 |
220 | match, _ := regexp.MatchString("^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$", input)
221 | return match
222 | }
223 |
224 | //高效拼接字符串
225 | func LinkStrs(inputs ...string) string {
226 | var buf bytes.Buffer
227 | for _, v := range inputs {
228 | buf.WriteString(v)
229 | }
230 | return buf.String()
231 | }
232 |
233 | func LinkInputs(inputs ...interface{}) string {
234 | var buf bytes.Buffer
235 | for _, v := range inputs {
236 | switch t := v.(type) {
237 | case string:
238 | buf.WriteString(t)
239 | default:
240 | buf.WriteString(fmt.Sprint(t))
241 |
242 | }
243 | }
244 | return buf.String()
245 | }
246 |
247 | // func LinkInputs(inputs ...interface{}) string {
248 | // var buf bytes.Buffer
249 | // for _, v := range inputs {
250 | // switch t := v.(type) {
251 | // case string:
252 | // buf.WriteString(t)
253 | // //case int, int64:
254 | // case int64:
255 | // buf.WriteString(FormatInt64(t))
256 | // case int:
257 | // buf.WriteString(FormatInt(t))
258 | // case float64:
259 | // buf.WriteString(FormatFloat64(t))
260 | // default:
261 | // buf.WriteString(fmt.Sprint(t))
262 |
263 | // }
264 |
265 | // fmt.Println("v:", v)
266 |
267 | // }
268 | // return buf.String()
269 | // }
270 |
--------------------------------------------------------------------------------
/forward-core/Utils/SysUtil.go:
--------------------------------------------------------------------------------
1 | package Utils
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "runtime"
7 | "time"
8 | )
9 |
10 | // 显示当前系统基本信息
11 | func ShowSysInf() {
12 |
13 | fmt.Println("★★★★★★★★★★★★★★★★★★★★★★★★")
14 | fmt.Println("runtime.Version --->", runtime.Version()) //GO的版本
15 | fmt.Println("runtime.NumCPU --->", runtime.NumCPU()) //CPU核数
16 | fmt.Println("runtime.GOOS --->", runtime.GOOS) //运行GO的OS操作系统
17 | fmt.Println("runtime.GOARCH --->", runtime.GOARCH) //CPU架构
18 | fmt.Println("runtime.Version --->", runtime.Version()) //当前GO语言版本
19 | fmt.Println("time --->", time.Now()) //系统当前时间
20 | fmt.Println("★★★★★★★★★★★★★★★★★★★★★★★★")
21 |
22 | //var memStats runtime.MemStats
23 | //runtime.ReadMemStats(&memStats)
24 | //fmt.Println("runtime.memStats --->", memStats)
25 |
26 | //获取全部的环境变量
27 | // data := os.Environ()
28 | // for _, val := range data {
29 | // fmt.Println(val)
30 | // }
31 |
32 | }
33 |
34 | // go不支持三元表达式,可以使用自定义的函数实现
35 | // 例如:max := utils.If(x > y, x, y).(int)
36 | func If(condition bool, trueVal, falseVal interface{}) interface{} {
37 |
38 | if condition {
39 | return trueVal
40 | }
41 | return falseVal
42 | }
43 |
44 | /*
45 | 交换int数据:a, b := utils.Swap(2, 9)
46 | 交换字符串数据:A, B := utils.Swap("Li", "Chen")
47 | */
48 | func Swap(x, y interface{}) (interface{}, interface{}) {
49 | return y, x
50 | }
51 |
52 | // 设置环境变量
53 | func SetEnv(key, value string) error {
54 |
55 | return os.Setenv(key, value)
56 | }
57 |
58 | // 取环境变量的值
59 | func GetEnv(key string) string {
60 |
61 | return os.Getenv(key)
62 | }
63 |
64 | //取进程ID
65 | func GetPid() int {
66 | return os.Getpid()
67 | }
68 |
69 | func KillByPid(pid int) {
70 | p, _ := os.FindProcess(pid)
71 | fmt.Println("KillByPid", p)
72 | p.Kill()
73 | }
74 |
75 | func StartProcessDemo() {
76 | //例子演示
77 | attr := &os.ProcAttr{
78 | Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
79 | }
80 | p, _ := os.StartProcess("xxx.exe", []string{"xxx", "1.txt"}, attr)
81 | p.Release()
82 | time.Sleep(10000)
83 | p.Signal(os.Kill)
84 | os.Exit(10)
85 | }
86 |
--------------------------------------------------------------------------------
/forward-core/go.mod:
--------------------------------------------------------------------------------
1 | module forward-core
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/astaxie/beego v1.12.2
7 | github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394
8 | github.com/json-iterator/go v1.1.10
9 | github.com/klauspost/reedsolomon v1.9.9
10 | github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 // indirect
11 | github.com/ugorji/go/codec v1.1.13
12 | github.com/vzex/internal v0.0.0-20171013154326-dfb733a0d236 // indirect
13 | github.com/vzex/mathutil v0.0.0-20170925121414-53c70789c7ff // indirect
14 | github.com/vzex/zappy v0.0.0-20171014114341-f5f547bd8793
15 | )
16 |
--------------------------------------------------------------------------------
/forward-core/ikcp/ikcp_h.go:
--------------------------------------------------------------------------------
1 | package ikcp
2 | import "container/list"
3 | //=====================================================================
4 | //
5 | // KCP - A Better ARQ Protocol Implementation
6 | // skywind3000 (at) gmail.com, 2010-2011
7 | //
8 | // Features:
9 | // + Average RTT reduce 30% - 40% vs traditional ARQ like tcp.
10 | // + Maximum RTT reduce three times vs tcp.
11 | // + Lightweight, distributed as a single source file.
12 | //
13 | //=====================================================================
14 | //---------------------------------------------------------------------
15 | // IKCPCB
16 | //---------------------------------------------------------------------
17 | type IKCPCB struct {
18 | conv, mtu, mss, state uint32
19 | snd_una, snd_nxt, rcv_nxt uint32
20 | ts_recent, ts_lastack, ssthresh uint32
21 | rx_rttval, rx_srtt, rx_rto, rx_minrto uint32
22 | snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe uint32
23 | current, interval, ts_flush, xmit uint32
24 | nrcv_buf, nsnd_buf uint32
25 | nrcv_que, nsnd_que uint32
26 | nodelay, updated uint32
27 | ts_probe, probe_wait uint32
28 | dead_link, incr uint32
29 | snd_queue, rcv_queue, snd_buf, rcv_buf *list.List
30 | acklist []uint32
31 | ackcount uint32
32 | ackblock uint32
33 | user interface{}
34 | buffer []byte
35 | fastresend int32
36 | nocwnd int32
37 | logmask int32
38 | writelog func (log []byte, kcp *Ikcpcb, user []byte)
39 |
40 | Output func (buf []byte, _len int32, kcp *Ikcpcb, user interface{}) (int32)
41 | }
42 |
43 |
44 | type Ikcpcb struct {IKCPCB}
45 |
46 | const IKCP_LOG_OUTPUT = 1
47 | const IKCP_LOG_INPUT = 2
48 | const IKCP_LOG_SEND = 4
49 | const IKCP_LOG_RECV = 8
50 | const IKCP_LOG_IN_DATA = 16
51 | const IKCP_LOG_IN_ACK = 32
52 | const IKCP_LOG_IN_PROBE = 64
53 | const IKCP_LOG_IN_WIN = 128
54 | const IKCP_LOG_OUT_DATA =256
55 | const IKCP_LOG_OUT_ACK = 512
56 | const IKCP_LOG_OUT_PROBE = 1024
57 | const IKCP_LOG_OUT_WINS = 2048
58 |
--------------------------------------------------------------------------------
/forward-core/ikcp/ikcp_test.go:
--------------------------------------------------------------------------------
1 | package ikcp
2 | import "encoding/binary"
3 | import "bytes"
4 | import "time"
5 | import "fmt"
6 | import "testing"
7 | //=====================================================================
8 | //=====================================================================
9 |
10 | // 模拟网络
11 | var vnet *LatencySimulator
12 |
13 | // 模拟网络:模拟发送一个 udp包
14 | func udp_output(buf []byte, _len int32, kcp *Ikcpcb, user interface{}) int32 {
15 | arr := (user).([]byte)
16 | var id uint32 = uint32(arr[0])
17 | //println("send!!!!", id, _len)
18 | if vnet.send(int(id), buf, int(_len)) != 1 {
19 | //println("wocao !!!", id, _len)
20 | }
21 | return 0
22 | }
23 |
24 | // 测试用例
25 | func test(mode int) {
26 | // 创建模拟网络:丢包率10%,Rtt 60ms~125ms
27 | vnet = &LatencySimulator{}
28 | vnet.Init(10, 60, 125, 1000)
29 |
30 | // 创建两个端点的 kcp对象,第一个参数 conv是会话编号,同一个会话需要相同
31 | // 最后一个是 user参数,用来传递标识
32 | a := []byte {0}
33 | b := []byte {1}
34 | kcp1 := Ikcp_create(0x11223344, a)
35 | kcp2 := Ikcp_create(0x11223344, b)
36 |
37 | // 设置kcp的下层输出,这里为 udp_output,模拟udp网络输出函数
38 | kcp1.Output = udp_output
39 | kcp2.Output = udp_output
40 |
41 | current := uint32(iclock())
42 | slap := current + 20
43 | index := 0
44 | next := 0
45 | var sumrtt uint32 = 0
46 | count := 0
47 | maxrtt := 0
48 |
49 | // 配置窗口大小:平均延迟200ms,每20ms发送一个包,
50 | // 而考虑到丢包重发,设置最大收发窗口为128
51 | Ikcp_wndsize(kcp1, 128, 128)
52 | Ikcp_wndsize(kcp2, 128, 128)
53 |
54 | // 判断测试用例的模式
55 | if (mode == 0) {
56 | // 默认模式
57 | Ikcp_nodelay(kcp1, 0, 10, 0, 0)
58 | Ikcp_nodelay(kcp2, 0, 10, 0, 0)
59 | } else if (mode == 1) {
60 | // 普通模式,关闭流控等
61 | Ikcp_nodelay(kcp1, 0, 10, 0, 1)
62 | Ikcp_nodelay(kcp2, 0, 10, 0, 1)
63 | } else {
64 | // 启动快速模式
65 | // 第二个参数 nodelay-启用以后若干常规加速将启动
66 | // 第三个参数 interval为内部处理时钟,默认设置为 10ms
67 | // 第四个参数 resend为快速重传指标,设置为2
68 | // 第五个参数 为是否禁用常规流控,这里禁止
69 | Ikcp_nodelay(kcp1, 1, 10, 2, 1)
70 | Ikcp_nodelay(kcp2, 1, 10, 2, 1)
71 | }
72 |
73 |
74 | var buffer []byte = make([]byte, 2000)
75 | var hr int32
76 |
77 | ts1 := iclock()
78 |
79 | for {
80 | time.Sleep(10* time.Millisecond)
81 | current = uint32(iclock())
82 | Ikcp_update(kcp1,uint32(iclock()))
83 | Ikcp_update(kcp2, uint32(iclock()))
84 |
85 | // 每隔 20ms,kcp1发送数据
86 | for ; current >= slap; slap += 20 {
87 | buf := new(bytes.Buffer)
88 | binary.Write(buf, binary.LittleEndian, uint32(index))
89 | index++
90 | binary.Write(buf, binary.LittleEndian, uint64(current))
91 | // 发送上层协议包
92 | Ikcp_send(kcp1, buf.Bytes(), 8)
93 | //println("now", iclock())
94 | }
95 |
96 | // 处理虚拟网络:检测是否有udp包从p1->p2
97 | for {
98 | hr = vnet.recv(1, buffer, 2000)
99 | if (hr < 0) {
100 | break
101 | }
102 | // 如果 p2收到udp,则作为下层协议输入到kcp2
103 | Ikcp_input(kcp2, buffer, int(hr))
104 | }
105 |
106 | // 处理虚拟网络:检测是否有udp包从p2->p1
107 | for {
108 | hr = vnet.recv(0, buffer, 2000)
109 | if (hr < 0) { break }
110 | // 如果 p1收到udp,则作为下层协议输入到kcp1
111 | Ikcp_input(kcp1, buffer, int(hr))
112 | //println("@@@@", hr, r)
113 | }
114 |
115 | // kcp2接收到任何包都返回回去
116 | for {
117 | hr = Ikcp_recv(kcp2, buffer, 10)
118 | // 没有收到包就退出
119 | if (hr < 0) { break }
120 | // 如果收到包就回射
121 | buf := bytes.NewReader(buffer)
122 | var sn uint32
123 | binary.Read(buf, binary.LittleEndian, &sn)
124 | Ikcp_send(kcp2, buffer, int(hr))
125 | }
126 |
127 | // kcp1收到kcp2的回射数据
128 | for {
129 | hr = Ikcp_recv(kcp1, buffer, 10)
130 | buf := bytes.NewReader(buffer)
131 | // 没有收到包就退出
132 | if (hr < 0) { break }
133 | var sn uint32
134 | var ts, rtt uint32
135 | binary.Read(buf, binary.LittleEndian, &sn)
136 | binary.Read(buf, binary.LittleEndian, &ts)
137 | rtt = uint32(current) - ts
138 |
139 | if (sn != uint32(next)) {
140 | // 如果收到的包不连续
141 | //for i:=0;i<8 ;i++ {
142 | //println("---", i, buffer[i])
143 | //}
144 | println("ERROR sn ", count, "<->", next, sn)
145 | return
146 | }
147 |
148 | next++
149 | sumrtt += rtt
150 | count++
151 | if (rtt > uint32(maxrtt)) { maxrtt = int(rtt) }
152 |
153 | println("[RECV] mode=", mode, " sn=",sn, " rtt=", rtt)
154 | }
155 | if (next > 100) { break }
156 | }
157 |
158 | ts1 = iclock() - ts1
159 |
160 | names := []string{ "default", "normal", "fast" }
161 | fmt.Printf("%s mode result (%dms):\n", names[mode], ts1)
162 | fmt.Printf("avgrtt=%d maxrtt=%d\n", int(sumrtt / uint32(count)), maxrtt)
163 | }
164 |
165 | func TestNetwork(t *testing.T) {
166 | test(0); // 默认模式,类似 TCP:正常模式,无快速重传,常规流控
167 | test(1); // 普通模式,关闭流控等
168 | test(2); // 快速模式,所有开关都打开,且关闭流控
169 | }
170 |
171 | /*
172 | default mode result (20917ms):
173 | avgrtt=740 maxrtt=1507
174 |
175 | normal mode result (20131ms):
176 | avgrtt=156 maxrtt=571
177 |
178 | fast mode result (20207ms):
179 | avgrtt=138 maxrtt=392
180 | */
181 |
182 |
--------------------------------------------------------------------------------
/forward-core/ikcp/ikcp_test_h.go:
--------------------------------------------------------------------------------
1 | package ikcp
2 | import "container/list"
3 | import "math/rand"
4 | import "time"
5 |
6 | func iclock() int32 {
7 | return int32((time.Now().UnixNano()/1000000) & 0xffffffff)
8 | }
9 | type DelayPacket struct {
10 | _ptr []byte
11 | _size int
12 | _ts int32
13 | }
14 |
15 | func (p *DelayPacket) Init(size int, src []byte) {
16 | p._ptr = make([]byte, size)
17 | p._size = size
18 | copy(p._ptr, src[:size])
19 | }
20 |
21 | func (p *DelayPacket) ptr() []byte { return p._ptr }
22 | func (p *DelayPacket) size() int { return p._size }
23 | func (p *DelayPacket) ts() int32 { return p._ts }
24 | func (p *DelayPacket) setts(ts int32) { p._ts = ts}
25 |
26 | type DelayTunnel struct {*list.List}
27 | type Random *rand.Rand
28 | type LatencySimulator struct {
29 | current int32
30 | lostrate, rttmin, rttmax, nmax int
31 | p12 DelayTunnel
32 | p21 DelayTunnel
33 | r12 *rand.Rand
34 | r21 *rand.Rand
35 | }
36 |
37 | // lostrate: 往返一周丢包率的百分比,默认 10%
38 | // rttmin:rtt最小值,默认 60
39 | // rttmax:rtt最大值,默认 125
40 | //func (p *LatencySimulator)Init(int lostrate = 10, int rttmin = 60, int rttmax = 125, int nmax = 1000):
41 | func (p *LatencySimulator)Init(lostrate, rttmin ,rttmax, nmax int) {
42 | p.r12 = rand.New(rand.NewSource(9))
43 | p.r21 = rand.New(rand.NewSource(99))
44 | p.p12 = DelayTunnel{list.New()}
45 | p.p21 = DelayTunnel{list.New()}
46 | p.current = iclock()
47 | p.lostrate = lostrate / 2; // 上面数据是往返丢包率,单程除以2
48 | p.rttmin = rttmin / 2
49 | p.rttmax = rttmax / 2
50 | p.nmax = nmax
51 | }
52 | // 发送数据
53 | // peer - 端点0/1,从0发送,从1接收;从1发送从0接收
54 | func (p *LatencySimulator) send(peer int, data []byte, size int) int {
55 | rnd := 0
56 | if (peer == 0) {
57 | rnd = p.r12.Intn(100)
58 | } else {
59 | rnd = p.r21.Intn(100)
60 | }
61 | //println("!!!!!!!!!!!!!!!!!!!!", rnd, p.lostrate, peer)
62 | if (rnd < p.lostrate) { return 0}
63 | pkt := &DelayPacket{}
64 | pkt.Init(size, data)
65 | p.current = iclock()
66 | delay := p.rttmin
67 | if (p.rttmax > p.rttmin) {
68 | delay += rand.Int() % (p.rttmax - p.rttmin)
69 | }
70 | pkt.setts(p.current + int32(delay))
71 | if (peer == 0) {
72 | p.p12.PushBack(pkt)
73 | } else {
74 | p.p21.PushBack(pkt)
75 | }
76 | return 1
77 | }
78 |
79 | // 接收数据
80 | func (p *LatencySimulator)recv(peer int, data []byte, maxsize int) int32 {
81 | var it *list.Element
82 | if (peer == 0) {
83 | it = p.p21.Front()
84 | if (p.p21.Len() == 0){ return -1}
85 | } else {
86 | it = p.p12.Front()
87 | if (p.p12.Len() == 0){ return -1}
88 | }
89 | pkt := it.Value.(*DelayPacket)
90 | p.current = iclock()
91 | if (p.current < pkt.ts()) { return -2 }
92 | if (maxsize < pkt.size()) { return -3 }
93 | if (peer == 0) {
94 | p.p21.Remove(it)
95 | } else {
96 | p.p12.Remove(it)
97 | }
98 | maxsize = pkt.size()
99 | copy(data, pkt.ptr()[:maxsize])
100 | return int32(maxsize)
101 | }
102 |
--------------------------------------------------------------------------------
/forward-server/Controllers/BaseCtrl/ApiCtrl.go:
--------------------------------------------------------------------------------
1 | package BaseCtrl
2 |
3 | import (
4 | "forward-core/NetUtils"
5 | "github.com/astaxie/beego"
6 | "github.com/astaxie/beego/logs"
7 | )
8 |
9 | type ApiCtrl struct {
10 | beego.Controller
11 | }
12 |
13 | var apiAuth = beego.AppConfig.String("api.auth")
14 |
15 | func (c *ApiCtrl) Prepare() {
16 | reqUrl := c.Ctx.Request.RequestURI
17 | userIp := NetUtils.GetIP(&c.Controller)
18 |
19 | logs.Debug("执行Prepare,当前reqUrl:", reqUrl, " userIp:", userIp)
20 |
21 | //校验鉴权参数
22 | auth := c.GetString("auth")
23 |
24 | if auth != apiAuth {
25 | //logs.Error("apiAuth验证失败:", auth)
26 | //c.Ctx.Redirect(302, "/apiAuthFail")
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/forward-server/Controllers/BaseCtrl/ConsoleCtrl.go:
--------------------------------------------------------------------------------
1 | package BaseCtrl
2 |
3 | import (
4 | "forward-core/Models"
5 | "github.com/astaxie/beego"
6 | "github.com/astaxie/beego/logs"
7 | )
8 |
9 | var (
10 | ConsoleLoginUrl string = "/login"
11 | )
12 |
13 | type ConsoleCtrl struct {
14 | beego.Controller
15 | LoginUser *Models.LoginUser
16 | }
17 |
18 | func (c *ConsoleCtrl) Prepare() {
19 | reqUrl := c.Ctx.Request.RequestURI
20 | logs.Debug("执行Prepare,当前reqUrl:", reqUrl)
21 |
22 | if ConsoleLoginUrl == reqUrl {
23 | //如果是登录地址,则不校验
24 | return
25 | }
26 |
27 | //开始访问每个action前,执行登录和权限检查
28 | userInfo := c.GetUserInfo()
29 |
30 | if userInfo == nil {
31 | //未登录
32 | c.Ctx.Redirect(302, ConsoleLoginUrl)
33 | }
34 |
35 | c.LoginUser = userInfo
36 |
37 | }
38 |
39 | //判断用户是否登录.
40 | func (c *ConsoleCtrl) isUserLoggedIn() bool {
41 | return c.LoginUser != nil && c.LoginUser.UserId > 0
42 | }
43 |
44 | func (c *ConsoleCtrl) StoreUserInfo(loginUser *Models.LoginUser) {
45 | c.SetSession("userInfo", loginUser)
46 | }
47 |
48 | func (c *ConsoleCtrl) GetUserInfo() *Models.LoginUser {
49 |
50 | userInfo := c.GetSession("userInfo")
51 | if userInfo == nil {
52 | return nil
53 | }
54 | return userInfo.(*Models.LoginUser)
55 |
56 | }
57 |
58 | func (c *ConsoleCtrl) ClearUserInfo() {
59 |
60 | c.DelSession("userInfo")
61 | c.LoginUser = nil
62 |
63 | }
--------------------------------------------------------------------------------
/forward-server/Controllers/BaseCtrl/WebCtrl.go:
--------------------------------------------------------------------------------
1 | package BaseCtrl
2 |
3 | import (
4 | "github.com/astaxie/beego"
5 | "github.com/astaxie/beego/logs"
6 | )
7 |
8 | type WebCtrl struct {
9 | beego.Controller
10 | }
11 |
12 | func (c *WebCtrl) Prepare() {
13 | reqUrl := c.Ctx.Request.RequestURI
14 | logs.Debug("执行Prepare,当前reqUrl:", reqUrl)
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/forward-server/Controllers/DefaultCtrl.go:
--------------------------------------------------------------------------------
1 | package Controllers
2 |
3 | import (
4 | "forward-core/Models"
5 | "forward-server/Controllers/BaseCtrl"
6 | )
7 |
8 | type DefaultCtrl struct {
9 | BaseCtrl.WebCtrl
10 | }
11 |
12 |
13 | // @router / [get]
14 | func (c *DefaultCtrl) Default() {
15 |
16 | c.Ctx.Redirect(302, "/login")
17 |
18 | //c.Data["currentTime"] = time.Now()
19 | //c.TplName = "index.html"
20 | }
21 |
22 | // @router /apiAuthFail [get]
23 | func (c *DefaultCtrl) ApiAuthFail() {
24 |
25 | c.Data["json"] = Models.FuncResult{Code: 1, Msg: "ApiAuth鉴权失败"}
26 |
27 | c.ServeJSON()
28 |
29 | }
--------------------------------------------------------------------------------
/forward-server/Controllers/LoginCtrl.go:
--------------------------------------------------------------------------------
1 | package Controllers
2 |
3 | import (
4 | "forward-core/Models"
5 | "forward-core/NetUtils"
6 | "forward-core/Utils"
7 | "forward-server/Controllers/BaseCtrl"
8 | "forward-server/Service"
9 | "github.com/astaxie/beego/logs"
10 | )
11 |
12 | type LoginCtrl struct {
13 | BaseCtrl.ConsoleCtrl
14 | }
15 |
16 | // @router /logout
17 | func (c *LoginCtrl) Logout() {
18 | c.ClearUserInfo()
19 | c.Ctx.Redirect(302, "/login")
20 |
21 | }
22 |
23 | // @router /login [get]
24 | func (c *LoginCtrl) Login() {
25 |
26 | c.TplName = "login.html"
27 |
28 | }
29 |
30 | // @router /login [post]
31 | func (c *LoginCtrl) DoLogin() {
32 | userName := c.GetString("userName")
33 | passWord := c.GetString("passWord")
34 |
35 | sysUser := Service.SysDataS.GetSysUserByName(userName)
36 | if sysUser == nil {
37 | logs.Debug("用户不存在")
38 | c.Ctx.Redirect(302, "/login")
39 | return
40 | }
41 |
42 | descryptPwd := Utils.GetMd5(passWord)
43 | logs.Debug("存储的密码:", sysUser.PassWord, " 输入的密码:", descryptPwd)
44 | if sysUser.PassWord == descryptPwd {
45 | logs.Info("用户登录:", userName, " IP:", NetUtils.GetIP(&c.Controller))
46 | loginUser := new(Models.LoginUser)
47 | loginUser.UserId = 1
48 | loginUser.UserName = userName
49 |
50 | c.SetSession("userInfo", loginUser)
51 | c.Ctx.Redirect(302, "/u/main")
52 | } else {
53 | logs.Debug("用户登录失败")
54 | c.Ctx.Redirect(302, "/login")
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/forward-server/Controllers/RestApiCtrl.go:
--------------------------------------------------------------------------------
1 | package Controllers
2 |
3 | import (
4 | "forward-core/Models"
5 | "forward-core/Utils"
6 | "forward-server/Controllers/BaseCtrl"
7 | "forward-server/Service"
8 | "runtime"
9 | "strings"
10 | "time"
11 | )
12 |
13 | type RestApiCtrl struct {
14 | BaseCtrl.ApiCtrl
15 | }
16 |
17 | // @router /ServerSummary [get,post]
18 | func (c *RestApiCtrl) ServerSummary() {
19 | obj := make(map[string]interface{})
20 | obj["runtime_NumGoroutine"] = runtime.NumGoroutine()
21 | obj["runtime_GOOS"] = runtime.GOOS
22 | obj["runtime_GOARCH"] = runtime.GOARCH
23 | obj["server_Time"] = time.Now()
24 |
25 | obj["forwardList"] = Service.ForWardServ.FindAllForward()
26 |
27 | c.Data["json"] = obj
28 |
29 | c.ServeJSON()
30 |
31 | }
32 |
33 | // @router /OpenForward [get,post]
34 | func (c *RestApiCtrl) OpenForward() {
35 |
36 | fromAddr := c.GetString("fromAddr")
37 | toAddr := c.GetString("toAddr")
38 | protocol := c.GetString("protocol", "TCP")
39 |
40 | entity := Service.SysDataS.ChkPortForwardByApi(fromAddr, protocol, toAddr)
41 | if entity == nil {
42 | var err error
43 | entity, err = Service.SysDataS.SavePortForwardByApi(fromAddr, protocol, toAddr)
44 | if err != nil {
45 | c.Data["json"] = Models.FuncResult{Code: 1, Msg: "保存端口配置失败"}
46 | c.ServeJSON()
47 | return
48 | }
49 | }
50 | //测试
51 | //http://127.0.0.1:8000/api/v1/OpenForward?auth=26CCD056107481F45D1AC805A24A9E59&fromAddr=:8010&toAddr=127.0.0.1:3306
52 | resultChan := make(chan Models.FuncResult)
53 | config := Service.SysDataS.ToForwardConfig(entity)
54 | go Service.ForWardServ.OpenForward(config, resultChan)
55 |
56 | c.Data["json"] = <-resultChan
57 |
58 | c.ServeJSON()
59 |
60 | }
61 |
62 | // @router /CloseForward [get,post]
63 | func (c *RestApiCtrl) CloseForward() {
64 |
65 | fromAddr := c.GetString("fromAddr")
66 | toAddr := c.GetString("toAddr")
67 | protocol := c.GetString("protocol", "TCP")
68 | //fType, _ := c.GetInt("fType", 0)
69 |
70 | //测试
71 | //http://127.0.0.1:8000/api/v1/CloseForward?auth=26CCD056107481F45D1AC805A24A9E59&fromAddr=:8010&toAddr=127.0.0.1:3306
72 |
73 | config := new(Models.ForwardConfig)
74 | config.RuleId = 0
75 | config.Name = ""
76 | config.Protocol = protocol
77 | config.SrcAddr = strings.Split(fromAddr, ":")[0]
78 | config.SrcPort = Utils.ToInt(strings.Split(fromAddr, ":")[1])
79 | config.DestAddr = strings.Split(toAddr, ":")[0]
80 | config.DestPort = Utils.ToInt(strings.Split(toAddr, ":")[1])
81 | config.Status = 0
82 | Service.ForWardServ.CloseForward(config)
83 |
84 | c.Data["json"] = Models.FuncResult{Code: 0, Msg: ""}
85 |
86 | c.ServeJSON()
87 | }
88 |
--------------------------------------------------------------------------------
/forward-server/Controllers/UCenterCtrl.go:
--------------------------------------------------------------------------------
1 | package Controllers
2 |
3 | import (
4 | "forward-core/Models"
5 | "forward-core/Utils"
6 | "forward-server/Controllers/BaseCtrl"
7 | "forward-server/Service"
8 | "runtime"
9 | "time"
10 | )
11 |
12 | type UCenterCtrl struct {
13 | BaseCtrl.ConsoleCtrl
14 | }
15 |
16 | // @router /u/main [get]
17 | func (c *UCenterCtrl) Main() {
18 |
19 | c.Layout = "ucenter/layout.html"
20 | c.TplName = "ucenter/main.html"
21 |
22 | }
23 |
24 | // @router /u/index [get]
25 | func (c *UCenterCtrl) Index() {
26 |
27 | c.Data["runtime_NumCPU"] = runtime.NumCPU()
28 | c.Data["runtime_GOOS"] = runtime.GOOS
29 | c.Data["runtime_GOARCH"] = runtime.GOARCH
30 | c.Data["runtime_NumGoroutine"] = runtime.NumGoroutine()
31 | c.Data["server_Time"] = time.Now()
32 |
33 | c.TplName = "ucenter/index.html"
34 | }
35 |
36 | // @router /u/getServerTime [post]
37 | func (c *UCenterCtrl) GetServerTime(){
38 |
39 | c.Data["json"] = Utils.GetCurrentTime()
40 |
41 | c.ServeJSON()
42 |
43 | }
44 |
45 | // @router /u/changePwd [get]
46 | func (c *UCenterCtrl) ChangePwd() {
47 |
48 | c.TplName = "ucenter/changePwd.html"
49 | }
50 |
51 | // @router /u/doChangePwd [post]
52 | func (c *UCenterCtrl) DoChangePwd() {
53 | userInfo := c.GetUserInfo()
54 |
55 | passWord := c.GetString("passWord")
56 | passWord2 := c.GetString("passWord2")
57 |
58 | if Utils.IsEmpty(passWord) {
59 | c.Data["json"] = Models.FuncResult{Code: 1, Msg: "密码不能为空"}
60 | c.ServeJSON()
61 | return
62 | }
63 |
64 | if passWord != passWord2 {
65 | c.Data["json"] = Models.FuncResult{Code: 1, Msg: "两次输入的密码不一致"}
66 | c.ServeJSON()
67 | return
68 | }
69 |
70 | err := Service.SysDataS.ChangeUserPwd(userInfo.UserId, passWord)
71 | if err == nil {
72 | c.Data["json"] = Models.FuncResult{Code: 0, Msg: "密码修改成功"}
73 | } else {
74 | c.Data["json"] = Models.FuncResult{Code: 1, Msg: err.Error()}
75 | }
76 |
77 | c.ServeJSON()
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/forward-server/Service/ConsoleServer.go:
--------------------------------------------------------------------------------
1 | package Service
2 |
3 | import (
4 | "encoding/gob"
5 | "forward-core/Models"
6 | "github.com/astaxie/beego"
7 | "github.com/astaxie/beego/logs"
8 | )
9 |
10 | type ConsoleServer struct {
11 |
12 | }
13 |
14 | func (_self *ConsoleServer) StartHttpServer() {
15 |
16 | //开启seesion支持,默认使用的存储引擎为:memory
17 | beego.BConfig.WebConfig.Session.SessionOn = true
18 | beego.BConfig.WebConfig.Session.SessionName = "sessionID"
19 | beego.BConfig.WebConfig.Session.SessionGCMaxLifetime = 3600
20 |
21 | //默认static目录是可以直接访问的,其它目录需要单独指定
22 | beego.SetStaticPath("/theme", "theme")
23 |
24 | //
25 | gob.Register(&Models.LoginUser{})
26 |
27 | logs.Debug("Http 服务启动...")
28 |
29 | //启动应用
30 | beego.Run()
31 |
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/forward-server/Service/ForWardJob.go:
--------------------------------------------------------------------------------
1 | package Service
2 |
3 | import (
4 | "fmt"
5 | "forward-core/Constant"
6 | "forward-core/Models"
7 | "forward-core/NetUtils"
8 | "forward-core/Utils"
9 | "io"
10 | "net"
11 | "sync"
12 | "time"
13 |
14 | "github.com/astaxie/beego/logs"
15 | )
16 |
17 | type ForWardJob struct {
18 | Config *Models.ForwardConfig
19 | ClientMap map[string]*ForWardClient
20 | ClientMapLock sync.Mutex
21 | Status byte
22 | PortListener net.Listener
23 | UdpForwardJob *UdpForward
24 | }
25 |
26 | func (_self *ForWardJob) StartJob(result chan Models.FuncResult) {
27 |
28 | sourceAddr := fmt.Sprint(_self.Config.SrcAddr, ":", _self.Config.SrcPort)
29 | destAddr := fmt.Sprint(_self.Config.DestAddr, ":", _self.Config.DestPort)
30 |
31 | resultData := &Models.FuncResult{Code: 0, Msg: "success"}
32 | var err error
33 | if _self.IsUdpJob() {
34 | //_self.PortListener, err = NetUtils.NewKCP(sourceAddr, Common.DefaultKcpSetting())
35 | //_self.UdpForwardJob.UdpListenerConn, err = NetUtils.NewUDP(sourceAddr)
36 |
37 | err = _self.UdpForwardJob.DoUdpForward(sourceAddr, destAddr)
38 |
39 | if err != nil {
40 | logs.Error("启动UDP监听 ", sourceAddr, " 出错:", err)
41 | resultData.Code = 1
42 | resultData.Msg = fmt.Sprint("启动UDP监听 ", sourceAddr, " 出错:", err)
43 | result <- *resultData
44 | return
45 | }
46 |
47 | _self.Status = Constant.RunStatus_Running
48 | logs.Debug("启动UDP端口转发,从 ", sourceAddr, " 到 ", destAddr)
49 | result <- *resultData
50 |
51 | } else {
52 | _self.PortListener, err = NetUtils.NewTCP(sourceAddr)
53 |
54 | if err != nil {
55 | logs.Error("启动监听 ", sourceAddr, " 出错:", err)
56 | resultData.Code = 1
57 | resultData.Msg = fmt.Sprint("启动监听 ", sourceAddr, " 出错:", err)
58 | result <- *resultData
59 | return
60 | }
61 |
62 | _self.Status = Constant.RunStatus_Running
63 | logs.Debug("启动端口转发,从 ", sourceAddr, " 到 ", destAddr)
64 | result <- *resultData
65 |
66 | _self.doTcpForward(destAddr)
67 |
68 | }
69 |
70 | }
71 |
72 | func (_self *ForWardJob) doTcpForward(destAddr string) {
73 |
74 | for {
75 | realClientConn, err := _self.PortListener.Accept()
76 | if err != nil {
77 | logs.Error("Forward Accept err:", err.Error())
78 | logs.Error(fmt.Sprint("转发出现异常:", _self.Config.SrcAddr, ":", _self.Config.SrcPort, "->", destAddr))
79 | _self.StopJob()
80 | break
81 | }
82 |
83 | if ForWardDebug == true {
84 | logs.Info("新用户 ", realClientConn.RemoteAddr().String(), " 数据转发规则:", fmt.Sprint(_self.Config.SrcAddr, ":", _self.Config.SrcPort), "->", destAddr)
85 | }
86 |
87 | var destConn net.Conn
88 | if _self.Config.Protocol == "UDP" {
89 | //destConn, err = Common.DialKcpTimeout(destAddr, 100)
90 | destConn, err = net.DialTimeout("UDP", destAddr, 30*time.Second)
91 | } else {
92 | destConn, err = net.DialTimeout("tcp", destAddr, 30*time.Second)
93 | }
94 |
95 | if err != nil {
96 | if ForWardDebug == true {
97 | logs.Warn("转发出现异常 Forward to Dest Addr err:", err.Error())
98 | }
99 |
100 | //break
101 | continue
102 |
103 | }
104 |
105 | forwardClient := &ForWardClient{realClientConn, destConn, nil, _self.ClosedCallBack}
106 |
107 | if Utils.IsNotEmpty(_self.Config.Others) {
108 | var dispatchConns []io.Writer
109 | //分发方式
110 | dispatchTargets := Utils.Split(_self.Config.Others, ";")
111 |
112 | for _, dispatchTarget := range dispatchTargets {
113 | logs.Debug("分发到:", dispatchTarget)
114 | dispatchTargetConn, err := net.DialTimeout("tcp", dispatchTarget, 30*time.Second)
115 | if err == nil {
116 | dispatchConns = append(dispatchConns, dispatchTargetConn)
117 | }
118 |
119 | }
120 |
121 | forwardClient.DispatchConns = dispatchConns
122 |
123 | go forwardClient.DispatchData(dispatchConns)
124 | } else {
125 | go forwardClient.StartForward()
126 | }
127 |
128 | _self.RegistryClient(_self.GetClientId(realClientConn), forwardClient)
129 | //_self.RegistryClient(fmt.Sprint(sourceAddr, "_", "TCP", "_", id), forwardClient)
130 |
131 | }
132 | }
133 |
134 | func (_self *ForWardJob) ClosedCallBack(srcConn net.Conn, destConn net.Conn) {
135 |
136 | _self.UnRegistryClient(_self.GetClientId(srcConn))
137 | }
138 |
139 | func (_self *ForWardJob) GetClientId(conn net.Conn) string {
140 | return conn.RemoteAddr().String()
141 | }
142 |
143 | func (_self *ForWardJob) RegistryClient(srcAddr string, forwardClient *ForWardClient) {
144 | _self.ClientMapLock.Lock()
145 | defer _self.ClientMapLock.Unlock()
146 |
147 | _self.ClientMap[srcAddr] = forwardClient
148 |
149 | }
150 |
151 | func (_self *ForWardJob) UnRegistryClient(srcAddr string) {
152 | _self.ClientMapLock.Lock()
153 | defer _self.ClientMapLock.Unlock()
154 |
155 | delete(_self.ClientMap, srcAddr)
156 | if ForWardDebug == true {
157 | logs.Debug("UnRegistryClient srcAddr: ", srcAddr)
158 | }
159 |
160 | }
161 |
162 | func (_self *ForWardJob) IsJobRunning() bool {
163 |
164 | return _self.Status == Constant.RunStatus_Running
165 |
166 | }
167 |
168 | func (_self *ForWardJob) IsUdpJob() bool {
169 | return Utils.ToUpper(_self.Config.Protocol) == "UDP"
170 | }
171 |
172 | func (_self *ForWardJob) StopJob() {
173 |
174 | if _self.IsUdpJob() {
175 | _self.stopUdpJob()
176 | } else {
177 | _self.stopTcpJob()
178 | }
179 |
180 | _self.Status = Constant.RunStatus_Stoped
181 | }
182 |
183 | func (_self *ForWardJob) stopTcpJob() {
184 |
185 | _self.PortListener.Close()
186 |
187 | for srcAddr, client := range _self.ClientMap {
188 | if ForWardDebug == true {
189 | logs.Debug("停止真实用户连接:", srcAddr)
190 | }
191 | client.StopForward()
192 | }
193 |
194 | _self.ClientMap = nil
195 | }
196 |
197 | func (_self *ForWardJob) stopUdpJob() {
198 |
199 | _self.UdpForwardJob.Close()
200 | }
201 |
--------------------------------------------------------------------------------
/forward-server/Service/ForwardClient.go:
--------------------------------------------------------------------------------
1 | package Service
2 |
3 | import (
4 | "io"
5 | "net"
6 |
7 | "github.com/astaxie/beego/logs"
8 | )
9 |
10 | type ForWardClient struct {
11 | SrcConn net.Conn
12 | DestConn net.Conn
13 | DispatchConns []io.Writer
14 | ClosedCallBack func(srcConn net.Conn, destConn net.Conn)
15 | }
16 |
17 | func (_self *ForWardClient) StartForward() {
18 |
19 | //io.Copy(dst, src)
20 |
21 | go func() {
22 | _, err := io.Copy(_self.DestConn, _self.SrcConn)
23 | if err != nil {
24 | //logs.Error("客户端来源数据转发到目标端口异常:", err)
25 | _self.StopForward()
26 | }
27 | }()
28 |
29 | go func() {
30 | _, err := io.Copy(_self.SrcConn, _self.DestConn)
31 | if err != nil {
32 | //logs.Error("目标端口返回响应数据异常:", err)
33 | _self.StopForward()
34 | }
35 | }()
36 | }
37 |
38 | func (_self *ForWardClient) DispatchData(dispatchConns []io.Writer) {
39 | //将数据克隆给其它端口
40 | go func() {
41 | mWriter := io.MultiWriter(append(dispatchConns, _self.DestConn)...)
42 | _, err := io.Copy(mWriter, _self.SrcConn)
43 | if err != nil {
44 | logs.Error("Dispatch网络连接异常:", err)
45 | _self.StopForward()
46 | }
47 | }()
48 |
49 | go func() {
50 | _, err := io.Copy(_self.SrcConn, _self.DestConn)
51 | if err != nil {
52 | //logs.Error("目标端口返回响应数据异常:", err)
53 | _self.StopForward()
54 | }
55 | }()
56 | }
57 |
58 | func (_self *ForWardClient) StopForward() {
59 | logs.Debug("关闭一个连接:", _self.SrcConn.RemoteAddr(), " on ", _self.SrcConn.LocalAddr())
60 | _self.SrcConn.Close()
61 | _self.DestConn.Close()
62 | _self.ClosedCallBack(_self.SrcConn, _self.DestConn)
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/forward-server/Service/ForwardServer.go:
--------------------------------------------------------------------------------
1 | package Service
2 |
3 | import (
4 | "fmt"
5 | "forward-core/Constant"
6 | "forward-core/Models"
7 | "net"
8 | "sync"
9 |
10 | "github.com/astaxie/beego/logs"
11 | )
12 |
13 | type ForWardServer struct {
14 | JobMap map[string]*ForWardJob
15 | JobMapLock sync.Mutex
16 | }
17 |
18 | func NewForWardServer() *ForWardServer {
19 | return &ForWardServer{
20 | JobMap: make(map[string]*ForWardJob, 200),
21 | }
22 | }
23 |
24 | func (_self *ForWardServer) FindAllForward() []*Models.ForwardInfo {
25 | var forwardList []*Models.ForwardInfo
26 | for _, forWardJob := range _self.JobMap {
27 |
28 | forwardInfo := new(Models.ForwardInfo)
29 |
30 | forwardInfo.Name = forWardJob.Config.Name
31 | forwardInfo.Status = forWardJob.Status
32 | forwardInfo.Protocol = forWardJob.Config.Protocol
33 | forwardInfo.SrcAddr = forWardJob.Config.SrcAddr
34 | forwardInfo.SrcPort = forWardJob.Config.SrcPort
35 | forwardInfo.DestAddr = forWardJob.Config.DestAddr
36 | forwardInfo.DestPort = forWardJob.Config.DestPort
37 |
38 | if forWardJob.IsUdpJob() {
39 | for key, _ := range forWardJob.UdpForwardJob.UdpConns {
40 |
41 | forwardInfo.Clients = append(forwardInfo.Clients, key)
42 | }
43 | } else {
44 | for _, client := range forWardJob.ClientMap {
45 | forwardInfo.Clients = append(forwardInfo.Clients, client.SrcConn.RemoteAddr().String())
46 | }
47 | }
48 |
49 | forwardInfo.OnlineCount = len(forwardInfo.Clients)
50 |
51 | forwardList = append(forwardList, forwardInfo)
52 | }
53 |
54 | return forwardList
55 | }
56 |
57 | func (_self *ForWardServer) GetForwardInfo(config *Models.ForwardConfig) *Models.ForwardInfo {
58 |
59 | forwardInfo := new(Models.ForwardInfo)
60 | forWardJob := _self.GetRegistryJob(config)
61 | if forWardJob != nil {
62 | forwardInfo.Name = forWardJob.Config.Name
63 | forwardInfo.Status = forWardJob.Status
64 | forwardInfo.Protocol = forWardJob.Config.Protocol
65 | forwardInfo.SrcAddr = forWardJob.Config.SrcAddr
66 | forwardInfo.SrcPort = forWardJob.Config.SrcPort
67 | forwardInfo.DestAddr = forWardJob.Config.DestAddr
68 | forwardInfo.DestPort = forWardJob.Config.DestPort
69 |
70 | for _, client := range forWardJob.ClientMap {
71 | forwardInfo.Clients = append(forwardInfo.Clients, client.SrcConn.RemoteAddr().String())
72 | }
73 |
74 | forwardInfo.OnlineCount = len(forwardInfo.Clients)
75 |
76 | }
77 |
78 | return forwardInfo
79 | }
80 |
81 | func (_self *ForWardServer) GetForwardJob(config *Models.ForwardConfig) *ForWardJob {
82 | return _self.GetRegistryJob(config)
83 | }
84 |
85 | func (_self *ForWardServer) OpenForward(config *Models.ForwardConfig, result chan Models.FuncResult) {
86 | hasJob := _self.GetForwardJob(config)
87 | if hasJob != nil && hasJob.Status == Constant.RunStatus_Running {
88 | resultData := &Models.FuncResult{Code: 1, Msg: "该端口转发正在执行中"}
89 | result <- *resultData
90 | return
91 | }
92 |
93 | forWardJob := new(ForWardJob)
94 | forWardJob.ClientMap = make(map[string]*ForWardClient, 500)
95 | forWardJob.Config = config
96 | forWardJob.UdpForwardJob = NewUdpForward()
97 |
98 | go forWardJob.StartJob(result)
99 |
100 | _self.RegistryJob(config, forWardJob)
101 |
102 | }
103 |
104 | func (_self *ForWardServer) GetJobKey(config *Models.ForwardConfig) string {
105 | srcAddr := fmt.Sprint(config.SrcAddr, ":", config.SrcPort)
106 | destAddr := fmt.Sprint(config.DestAddr, ":", config.DestPort)
107 |
108 | return fmt.Sprint(srcAddr, "_", config.Protocol, "_", destAddr)
109 | }
110 |
111 | func (_self *ForWardServer) GetClientId(conn net.Conn) string {
112 | return conn.RemoteAddr().String()
113 | }
114 |
115 | func (_self *ForWardServer) RegistryJob(config *Models.ForwardConfig, forWardJob *ForWardJob) {
116 | _self.JobMapLock.Lock()
117 | defer _self.JobMapLock.Unlock()
118 |
119 | _self.JobMap[_self.GetJobKey(config)] = forWardJob
120 |
121 | }
122 |
123 | func (_self *ForWardServer) UnRegistryJob(config *Models.ForwardConfig) {
124 | _self.JobMapLock.Lock()
125 | defer _self.JobMapLock.Unlock()
126 |
127 | key := _self.GetJobKey(config)
128 | delete(_self.JobMap, key)
129 | if ForWardDebug == true {
130 | logs.Debug("UnRegistryClient key: ", key)
131 | }
132 |
133 | }
134 |
135 | func (_self *ForWardServer) GetRegistryJob(config *Models.ForwardConfig) *ForWardJob {
136 | if forWardJob, ok := _self.JobMap[_self.GetJobKey(config)]; ok {
137 | return forWardJob
138 | }
139 |
140 | return nil
141 | }
142 |
143 | func (_self *ForWardServer) CloseForward(config *Models.ForwardConfig) {
144 |
145 | forWardJob := _self.GetRegistryJob(config)
146 | if forWardJob != nil {
147 | logs.Debug("停止转发,找到执行者:", _self.GetJobKey(config))
148 | forWardJob.StopJob()
149 | _self.UnRegistryJob(config)
150 | }
151 |
152 | }
153 |
154 | func (_self *ForWardServer) CloseAllForward() {
155 |
156 | for _, forWardJob := range _self.JobMap {
157 | forWardJob.StopJob()
158 | //delete(_self.JobMap, key)
159 | _self.UnRegistryJob(forWardJob.Config)
160 |
161 | }
162 |
163 | //_self.JobMap = nil
164 | _self.JobMap = make(map[string]*ForWardJob, 200)
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/forward-server/Service/InitServices.go:
--------------------------------------------------------------------------------
1 | package Service
2 |
3 | import (
4 | "fmt"
5 | "forward-core/Models"
6 |
7 | "github.com/astaxie/beego"
8 | "github.com/astaxie/beego/orm"
9 | _ "github.com/mattn/go-sqlite3"
10 | )
11 |
12 | var (
13 | OrmerS orm.Ormer
14 | //ForWardServ = new(ForWardServer)
15 | ForWardServ = NewForWardServer()
16 | MagicServ = NewMagicServiceV1()
17 | //MagicServ = new(MagicServer)
18 | ConsoleServ = new(ConsoleServer)
19 | SysDataS = new(SysDataService)
20 |
21 | ForWardDebug = true
22 | )
23 |
24 | func init() {
25 | //开启DEBUG模式,输出SQL信息
26 | orm.Debug = true
27 |
28 | //_ "github.com/mattn/go-sqlite3"
29 | orm.RegisterDriver("sqlite3", orm.DRSqlite)
30 | orm.RegisterDataBase("default", "sqlite3", "file:data/data.db?cache=shared&loc=auto")
31 |
32 | OrmerS = orm.NewOrm()
33 | OrmerS.Using("default")
34 |
35 | onstartForward := beego.AppConfig.String("onstart.forward")
36 | if onstartForward == "true" {
37 | onStartForward()
38 | }
39 | }
40 |
41 | func onStartForward() {
42 | forwards := SysDataS.GetAllPortForwardList(1)
43 | for _, entity := range forwards {
44 | resultChan := make(chan Models.FuncResult)
45 | config := SysDataS.ToForwardConfig(entity)
46 | go ForWardServ.OpenForward(config, resultChan)
47 |
48 | fmt.Println(<-resultChan)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/forward-server/Service/MagicClient.go:
--------------------------------------------------------------------------------
1 | package Service
2 |
3 | import "net"
4 |
5 | type MagicClient struct {
6 |
7 | cid string
8 | encode, decode func([]byte) []byte
9 | authed bool
10 | conn net.Conn
11 |
12 | }
--------------------------------------------------------------------------------
/forward-server/Service/MagicServer.go:
--------------------------------------------------------------------------------
1 | package Service
2 |
3 | import "forward-core/Models"
4 |
5 | type MagicServer struct {
6 | UseUDP bool
7 | }
8 |
9 | func (_self *MagicServer) StartMagicService(netAddr string, result chan Models.FuncResult) {
10 |
11 | }
--------------------------------------------------------------------------------
/forward-server/Service/UdpForward.go:
--------------------------------------------------------------------------------
1 | package Service
2 |
3 | import (
4 | "github.com/astaxie/beego/logs"
5 | "net"
6 | "sync"
7 | "time"
8 | )
9 |
10 | type UdpForward struct {
11 | SrcAddr *net.UDPAddr
12 | DestAddr *net.UDPAddr
13 | LClientAddr *net.UDPAddr
14 | UdpListenerConn *net.UDPConn
15 | UdpConns map[string]UdpConn
16 | UdpConnsMutex *sync.RWMutex
17 | ChkActTime time.Duration
18 | Closed bool
19 | ConnectedEvent func(addr string)
20 | DisConnectedEvent func(addr string)
21 | }
22 |
23 | type UdpConn struct {
24 | udp *net.UDPConn
25 | lastActive time.Time
26 | }
27 |
28 | const bufferSize = 4096
29 | var chkActTime = time.Minute * 1
30 |
31 | func NewUdpForward() *UdpForward {
32 | return &UdpForward{
33 | UdpConns:make(map[string]UdpConn),
34 | UdpConnsMutex:new(sync.RWMutex),
35 | ChkActTime:chkActTime,
36 | ConnectedEvent:func(addr string) {},
37 | DisConnectedEvent:func(addr string){},
38 | }
39 | }
40 |
41 | func (_self *UdpForward) DoUdpForward (srcAddr string, destAddr string) error {
42 |
43 | var err error
44 | _self.SrcAddr, err = net.ResolveUDPAddr("udp", srcAddr)
45 | if err != nil {
46 | logs.Error("ResolveUDPAddr ", srcAddr, " 出错:", err)
47 | return err
48 | }
49 |
50 | _self.DestAddr, err = net.ResolveUDPAddr("udp", destAddr)
51 | if err != nil {
52 | logs.Error("ResolveUDPAddr ", destAddr, " 出错:", err)
53 | return err
54 | }
55 |
56 | _self.LClientAddr = &net.UDPAddr{
57 | IP: _self.SrcAddr.IP,
58 | Port: 0,
59 | Zone: _self.SrcAddr.Zone,
60 | }
61 |
62 | _self.UdpListenerConn, err = net.ListenUDP("udp", _self.SrcAddr)
63 | if err != nil {
64 | logs.Error("启动UDP监听 ", srcAddr, " 出错:", err)
65 | return err
66 | }
67 |
68 | go _self.checkAlive()
69 | go _self.runForward()
70 |
71 | return nil
72 | }
73 |
74 |
75 | func (_self *UdpForward) runForward() {
76 | for {
77 | buf := make([]byte, bufferSize)
78 | n, addr, err := _self.UdpListenerConn.ReadFromUDP(buf)
79 | if err != nil {
80 | return
81 | }
82 | go _self.forwardHandler(buf[:n], addr)
83 | }
84 | }
85 |
86 | func (_self *UdpForward) forwardHandler(data []byte, addr *net.UDPAddr) {
87 |
88 | _self.UdpConnsMutex.RLock()
89 | udpConn, found := _self.UdpConns[addr.String()]
90 | _self.UdpConnsMutex.RUnlock()
91 |
92 | if found {
93 |
94 | udpConn.udp.WriteTo(data, _self.DestAddr)
95 | }else{
96 |
97 | conn, err := net.ListenUDP("udp", _self.LClientAddr)
98 | if err != nil {
99 | logs.Error("udp-forwader: failed to dial:", err)
100 | return
101 | }
102 |
103 | _self.UdpConnsMutex.Lock()
104 | _self.UdpConns[addr.String()] = UdpConn{
105 | udp: conn,
106 | lastActive: time.Now(),
107 | }
108 | _self.UdpConnsMutex.Unlock()
109 |
110 | _self.ConnectedEvent(addr.String())
111 |
112 | conn.WriteTo(data, _self.DestAddr)
113 |
114 | for {
115 | buf := make([]byte, bufferSize)
116 | n, _, err := conn.ReadFromUDP(buf)
117 | if err != nil {
118 | _self.UdpConnsMutex.Lock()
119 | conn.Close()
120 | delete(_self.UdpConns, addr.String())
121 | _self.UdpConnsMutex.Unlock()
122 | return
123 | }
124 |
125 | go func(data []byte, conn *net.UDPConn, addr *net.UDPAddr) {
126 | _self.UdpListenerConn.WriteTo(data, addr)
127 | }(buf[:n], conn, addr)
128 | }
129 | }
130 |
131 | _self.updateActiveTime(addr)
132 | }
133 |
134 | func (_self *UdpForward) updateActiveTime(addr *net.UDPAddr) {
135 |
136 | needUpdateTime := false
137 | _self.UdpConnsMutex.RLock()
138 | if _, found := _self.UdpConns[addr.String()]; found {
139 | if _self.UdpConns[addr.String()].lastActive.Before(
140 | time.Now().Add(_self.ChkActTime / 4)) {
141 | needUpdateTime = true
142 | //logs.Debug("needUpdateTime")
143 | }
144 | }
145 | _self.UdpConnsMutex.RUnlock()
146 |
147 | if needUpdateTime {
148 | _self.UdpConnsMutex.Lock()
149 | //
150 | if _, found := _self.UdpConns[addr.String()]; found {
151 | connWrapper := _self.UdpConns[addr.String()]
152 | connWrapper.lastActive = time.Now()
153 | _self.UdpConns[addr.String()] = connWrapper
154 | }
155 | _self.UdpConnsMutex.Unlock()
156 | }
157 | }
158 |
159 | func (_self *UdpForward) checkAlive() {
160 |
161 | for !_self.Closed {
162 | time.Sleep(_self.ChkActTime)
163 | var keysToDelete []string
164 |
165 | _self.UdpConnsMutex.RLock()
166 | for k, conn := range _self.UdpConns {
167 | if conn.lastActive.Before(time.Now().Add(-_self.ChkActTime)) {
168 | keysToDelete = append(keysToDelete, k)
169 | //logs.Debug("need delete udp conn")
170 | }
171 | }
172 | _self.UdpConnsMutex.RUnlock()
173 |
174 | _self.UdpConnsMutex.Lock()
175 | for _, k := range keysToDelete {
176 | _self.UdpConns[k].udp.Close()
177 | delete(_self.UdpConns, k)
178 | }
179 | _self.UdpConnsMutex.Unlock()
180 |
181 | for _, k := range keysToDelete {
182 | _self.DisConnectedEvent(k)
183 | }
184 | }
185 |
186 | }
187 |
188 |
189 | func (_self *UdpForward) Close() {
190 | _self.UdpConnsMutex.Lock()
191 | _self.Closed = true
192 | for _, conn := range _self.UdpConns {
193 | conn.udp.Close()
194 | }
195 | _self.UdpListenerConn.Close()
196 | _self.UdpConnsMutex.Unlock()
197 | }
198 |
199 | func (_self *UdpForward) GetConnsInfo() []string {
200 | _self.UdpConnsMutex.Lock()
201 | defer _self.UdpConnsMutex.Unlock()
202 | results := make([]string, 0, len(_self.UdpConns))
203 | for key := range _self.UdpConns {
204 | results = append(results, key)
205 | }
206 | return results
207 | }
--------------------------------------------------------------------------------
/forward-server/build_linux.bat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/build_linux.bat
--------------------------------------------------------------------------------
/forward-server/build_linux.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | export GoDevWork=/Users/tavenli/Desktop/Work/port-forward-v2
4 |
5 | echo "Build For Linux..."
6 | export GOOS=linux
7 | export GOARCH=amd64
8 | export GOPATH=${GoDevWork}:${GOPATH}
9 | go build -o forward-server
10 |
11 | echo "--------- Build For Linux Success!"
12 |
13 |
14 |
--------------------------------------------------------------------------------
/forward-server/build_mac.bat:
--------------------------------------------------------------------------------
1 |
2 |
3 | echo "Build For Mac..."
4 | set GOOS=darwin
5 | set GOARCH=amd64
6 |
7 | go build -o forward-server
8 |
9 | echo "--------- Build For Mac Success!"
10 |
11 |
12 | pause
13 |
14 |
--------------------------------------------------------------------------------
/forward-server/build_win.bat:
--------------------------------------------------------------------------------
1 |
2 | ::set GoDevWork="D:\CodeWork\port-forward\"
3 |
4 | echo "Clean for build..."
5 | go clean
6 |
7 | echo "Build For windows..."
8 |
9 | set GOOS=windows
10 | set GOARCH=amd64
11 | ::set GOPATH=%GoDevWork%;%GOPATH%
12 | go build -o forward-server.exe
13 |
14 | echo "--------- Build For windows Success!"
15 |
16 |
17 | pause
18 |
19 |
--------------------------------------------------------------------------------
/forward-server/conf/app.conf:
--------------------------------------------------------------------------------
1 |
2 | app.name = "PortForward"
3 | app.cname = "端口转发系统"
4 | servername = "PortForward-Server"
5 | runmode ="dev"
6 | #runmode ="prod"
7 | viewspath = "views"
8 | #logfile.config = "close"
9 |
10 | [dev]
11 | httpaddr = "0.0.0.0"
12 | httpport = 8080
13 | recoverpanic = false
14 | [prod]
15 | httpaddr = "0.0.0.0"
16 | httpport = 8080
17 | recoverpanic = true
18 | graceful = true
19 |
20 | include "data.conf"
21 |
--------------------------------------------------------------------------------
/forward-server/conf/data.conf:
--------------------------------------------------------------------------------
1 | magic.service = ":7000"
2 | api.auth = "26CCD056107481F45D1AC805A24A9E59"
3 |
4 | agent.auth = "722ED8F8D9900CC1AB17243DC7B51A2D"
5 |
6 | onstart.forward = "false"
--------------------------------------------------------------------------------
/forward-server/data/data.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/data/data.db
--------------------------------------------------------------------------------
/forward-server/go.mod:
--------------------------------------------------------------------------------
1 | module forward-server
2 |
3 | go 1.14
4 |
5 | require (
6 | forward-core v0.0.0-00010101000000-000000000000
7 | github.com/astaxie/beego v1.12.2
8 | github.com/mattn/go-sqlite3 v2.0.3+incompatible
9 | github.com/vmihailenco/msgpack v4.0.4+incompatible
10 | google.golang.org/appengine v1.6.7 // indirect
11 | )
12 |
13 | replace forward-core => ../forward-core
14 |
--------------------------------------------------------------------------------
/forward-server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "forward-server/Service"
6 | _ "forward-server/routers"
7 |
8 | "forward-core/Models"
9 |
10 | "github.com/astaxie/beego"
11 | "github.com/astaxie/beego/logs"
12 | _ "github.com/mattn/go-sqlite3"
13 | "github.com/vmihailenco/msgpack"
14 | )
15 |
16 | func main() {
17 |
18 | logFileConfig := beego.AppConfig.String("logfile.config")
19 |
20 | //日志级别:"emergency", "alert", "critical", "error", "warning", "notice", "info", "debug"
21 | logs.SetLogger(logs.AdapterConsole, `{"level":7}`)
22 |
23 | if len(logFileConfig) == 0 {
24 | //logFileConfig = `{"filename":"app.log","level":7,"maxlines":0,"maxsize":0,"daily":true,"maxdays":10}`
25 | logFileConfig = `{"filename":"app.log","level":7,"maxlines":0,"maxsize":0,"daily":true,"maxdays":10,"separate":["error"]}`
26 |
27 | }
28 |
29 | if logFileConfig != "close" {
30 | //logs.SetLogger(logs.AdapterFile, `{"filename":"app.log","level":7,"maxlines":0,"maxsize":0,"daily":true,"maxdays":10}`)
31 | //logs.SetLogger(logs.AdapterFile, logFileConfig)
32 | logs.SetLogger(logs.AdapterMultiFile, logFileConfig)
33 | }
34 |
35 | //输出文件名和行号
36 | logs.EnableFuncCallDepth(true)
37 | logs.SetLogFuncCallDepth(3)
38 | //为了让日志输出不影响性能,开启异步日志
39 | logs.Async()
40 |
41 | logs.Debug("★★★★★★★★★★★★★★★★★★★★")
42 | logs.Debug(" port-forward 启动")
43 | logs.Debug("")
44 | logs.Debug("项目地址:https://github.com/tavenli/port-forward")
45 | logs.Debug("")
46 | logs.Debug("★★★★★★★★★★★★★★★★★★★★")
47 |
48 | defer logs.GetBeeLogger().Flush()
49 |
50 | //test1()
51 |
52 | //启动Web控制台和接口
53 | Service.ConsoleServ.StartHttpServer()
54 |
55 | //select {}
56 |
57 | //endRunning := make(chan bool, 1)
58 | //time.Sleep(1* time.Second)
59 | //endRunning <- true
60 | //<-endRunning
61 | }
62 |
63 | func test1() {
64 |
65 | //github.com/gogf/gf/g/os/glog
66 | //glog.Debug("This is Debug")
67 | //glog.Info("This is Info")
68 |
69 | b, err := msgpack.Marshal(&Models.PortInfo{Addr: "bar"})
70 | if err != nil {
71 | panic(err)
72 | }
73 |
74 | var item Models.PortInfo
75 | err = msgpack.Unmarshal(b, &item)
76 | if err != nil {
77 | panic(err)
78 | }
79 |
80 | logs.Debug(item.Addr)
81 |
82 | //
83 | config := new(Models.ForwardConfig)
84 | //config.Protocol = "TCP"
85 | config.Protocol = "UDP"
86 | config.SrcAddr = ""
87 | config.SrcPort = 8888
88 | //106.14.184.192:9999
89 | //config.DestAddr = "106.14.184.192"
90 | //config.DestPort = 9999
91 | config.DestAddr = "svn.apiclub.top"
92 | config.DestPort = 9900
93 | config.Status = 0
94 | config.Name = "测试1"
95 | config.RuleId = 1
96 |
97 | resultChan := make(chan Models.FuncResult)
98 | Service.ForWardServ.OpenForward(config, resultChan)
99 |
100 | fmt.Println(<-resultChan)
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/forward-server/routers/router.go:
--------------------------------------------------------------------------------
1 | package routers
2 |
3 | import (
4 | "forward-server/Controllers"
5 | "github.com/astaxie/beego"
6 | )
7 |
8 | func init() {
9 |
10 | //
11 | beego.Include(&Controllers.DefaultCtrl{})
12 | beego.Include(&Controllers.LoginCtrl{})
13 | beego.Include(&Controllers.UCenterCtrl{})
14 | beego.Include(&Controllers.ForwardCtrl{})
15 |
16 | api_ns := beego.NewNamespace("/api",
17 | beego.NSNamespace("/v1",
18 | beego.NSInclude(
19 | &Controllers.RestApiCtrl{},
20 | ),
21 | ),
22 | )
23 |
24 | beego.AddNamespace(api_ns)
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/forward-server/static/js/timer.min.js:
--------------------------------------------------------------------------------
1 | /*! timer.jquery 0.4.14 2016-05-14*/!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],b):b(a.jQuery)}(this,function(a){function b(b){var c=b.element;a(c).data("intr",setInterval(d.bind(b),b.options.updateFrequency)),a(c).data("isTimerRunning",!0)}function c(b){clearInterval(a(b.element).data("intr")),a(b.element).data("isTimerRunning",!1)}function d(){a(this.element).data("totalSeconds",g()-a(this.element).data("startTime")),e(this),a(this.element).data("duration")&&a(this.element).data("totalSeconds")%a(this.element).data("duration")===0&&(this.options.repeat||(a(this.element).data("duration",null),this.options.duration=null),this.options.countdown&&(c(this),this.options.countdown=!1,a(this.element).data("state",r)),this.options.callback())}function e(b){var c=b.element,d=a(c).data("totalSeconds");b.options.countdown&&a(c).data("duration")>0&&(d=a(c).data("duration")-a(c).data("totalSeconds")),a(c)[q](i(d,b)),a(c).data("seconds",d)}function f(b){var c=b.element;a(c).on("focus",function(){l(b)}),a(c).on("blur",function(){var d,e=a(c)[q]();e.indexOf("sec")>0?a(c).data("totalSeconds",Number(e.replace(/\ssec/g,""))):e.indexOf("min")>0?(e=e.replace(/\smin/g,""),d=e.split(":"),a(c).data("totalSeconds",Number(60*d[0])+Number(d[1]))):e.match(/\d{1,2}:\d{2}:\d{2}/)&&(d=e.split(":"),a(c).data("totalSeconds",Number(3600*d[0])+Number(60*d[1])+Number(d[2]))),m(b)})}function g(){return Math.round((new Date).getTime()/1e3)}function h(a){var b,c=0,d=Math.floor(a/60),e=d;return a>=3600&&(c=Math.floor(a/3600)),a>=3600&&(e=Math.floor(a%3600/60)),10>e&&c>0&&(e="0"+e),b=a%60,10>b&&(e>0||c>0)&&(b="0"+b),{hours:c,minutes:e,totalMinutes:d,seconds:b,totalSeconds:a}}function i(a,b){var c="",d=h(a);if(b.options.format){var e=[{identifier:"%h",value:d.hours,pad:!1},{identifier:"%m",value:d.minutes,pad:!1},{identifier:"%s",value:d.seconds,pad:!1},{identifier:"%g",value:d.totalMinutes,pad:!1},{identifier:"%t",value:d.totalSeconds,pad:!1},{identifier:"%H",value:parseInt(d.hours),pad:!0},{identifier:"%M",value:parseInt(d.minutes),pad:!0},{identifier:"%S",value:parseInt(d.seconds),pad:!0},{identifier:"%G",value:parseInt(d.totalMinutes),pad:!0},{identifier:"%T",value:parseInt(d.totalSeconds),pad:!0}];c=b.options.format,e.forEach(function(a){c=c.replace(new RegExp(a.identifier.replace(/([.*+?^=!:${}()|\[\]\/\\])/g,"\\$1"),"g"),a.pad&&a.value<10?"0"+a.value:a.value)})}else c=d.hours?d.hours+":"+d.minutes+":"+d.seconds:d.minutes?d.minutes+":"+d.seconds+" min":d.seconds+" sec";return c}function j(a){if(!isNaN(Number(a)))return a;var b=a.match(/\d{1,2}h/),c=a.match(/\d{1,2}m/),d=a.match(/\d{1,2}s/),e=0;return a=a.toLowerCase(),b&&(e+=3600*Number(b[0].replace("h",""))),c&&(e+=60*Number(c[0].replace("m",""))),d&&(e+=Number(d[0].replace("s",""))),e}function k(c){var d=c.element;a(d).data("isTimerRunning")||(e(c),b(c),a(d).data("state",s),c.options.startTimer.bind(c).call())}function l(b){var d=b.element;a(d).data("isTimerRunning")&&(c(b),a(d).data("state",t),b.options.pauseTimer.bind(b).call())}function m(c){var d=c.element;a(d).data("isTimerRunning")||(a(d).data("startTime",g()-a(d).data("totalSeconds")),b(c),a(d).data("state",s),c.options.resumeTimer.bind(c).call())}function n(a){o(a),k(a)}function o(b){var d=b.element;c(b),b.options.removeTimer.bind(b).call(),a(d).data("isTimerRunning",null),a(d).data("plugin_"+v,null),a(d).data("seconds",null),a(d).data("totalSeconds",null),a(d).data("state",null),a(d)[q]("")}var p={seconds:0,editable:!1,restart:!1,duration:null,callback:function(){alert("Time up!")},startTimer:function(){},pauseTimer:function(){},resumeTimer:function(){},resetTimer:function(){},removeTimer:function(){},repeat:!1,countdown:!1,format:null,updateFrequency:1e3,state:"running"},q="html",r="stopped",s="running",t="paused",u=function(b,c){var d;this.options=p=a.extend(this.options,p,c),this.element=b,a(b).data("totalSeconds",p.seconds),a(b).data("startTime",g()-a(b).data("totalSeconds")),a(b).data("seconds",a(b).data("totalSeconds")),a(b).data("state",r),d=a(b).prop("tagName").toLowerCase(),"input"!==d&&"textarea"!==d||(q="val"),this.options.duration&&(a(b).data("duration",j(this.options.duration)),this.options.duration=j(this.options.duration)),this.options.editable&&f(this)};u.prototype={start:function(){k(this)},pause:function(){l(this)},resume:function(){m(this)},reset:function(){n(this)},remove:function(){o(this)}};var v="timer";a.fn[v]=function(b){return b=b||"start",this.each(function(){a.data(this,"plugin_"+v)instanceof u||a.data(this,"plugin_"+v,new u(this,b));var c=a.data(this,"plugin_"+v);"string"==typeof b&&"function"==typeof c[b]&&c[b].call(c),"object"==typeof b&&(c.options.state===s?c.start.call(c):e(c))})}});
--------------------------------------------------------------------------------
/forward-server/static/layui/css/modules/code.css:
--------------------------------------------------------------------------------
1 | /** layui-v2.5.6 MIT License By https://www.layui.com */
2 | html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-h3,.layui-code-view{position:relative;font-size:12px}.layui-code-view{display:block;margin:10px 0;padding:0;border:1px solid #e2e2e2;border-left-width:6px;background-color:#F2F2F2;color:#333;font-family:Courier New}.layui-code-h3{padding:0 10px;height:32px;line-height:32px;border-bottom:1px solid #e2e2e2}.layui-code-h3 a{position:absolute;right:10px;top:0;color:#999}.layui-code-view .layui-code-ol{position:relative;overflow:auto}.layui-code-view .layui-code-ol li{position:relative;margin-left:45px;line-height:20px;padding:0 5px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view pre{margin:0}.layui-code-notepad{border:1px solid #0C0C0C;border-left-color:#3F3F3F;background-color:#0C0C0C;color:#C2BE9E}.layui-code-notepad .layui-code-h3{border-bottom:none}.layui-code-notepad .layui-code-ol li{background-color:#3F3F3F;border-left:none}
--------------------------------------------------------------------------------
/forward-server/static/layui/css/modules/laydate/default/laydate.css:
--------------------------------------------------------------------------------
1 | /** layui-v2.5.6 MIT License By https://www.layui.com */
2 | .laydate-set-ym,.layui-laydate,.layui-laydate *,.layui-laydate-list{box-sizing:border-box}html #layuicss-laydate{display:none;position:absolute;width:1989px}.layui-laydate *{margin:0;padding:0}.layui-laydate{position:absolute;z-index:66666666;margin:5px 0;border-radius:2px;font-size:14px;-webkit-animation-duration:.3s;animation-duration:.3s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-name:laydate-upbit;animation-name:laydate-upbit}.layui-laydate-main{width:272px}.layui-laydate-content td,.layui-laydate-header *,.layui-laydate-list li{transition-duration:.3s;-webkit-transition-duration:.3s}@-webkit-keyframes laydate-upbit{from{-webkit-transform:translate3d(0,20px,0);opacity:.3}to{-webkit-transform:translate3d(0,0,0);opacity:1}}@keyframes laydate-upbit{from{transform:translate3d(0,20px,0);opacity:.3}to{transform:translate3d(0,0,0);opacity:1}}.layui-laydate-static{position:relative;z-index:0;display:inline-block;margin:0;-webkit-animation:none;animation:none}.laydate-ym-show .laydate-next-m,.laydate-ym-show .laydate-prev-m{display:none!important}.laydate-ym-show .laydate-next-y,.laydate-ym-show .laydate-prev-y{display:inline-block!important}.laydate-time-show .laydate-set-ym span[lay-type=month],.laydate-time-show .laydate-set-ym span[lay-type=year],.laydate-time-show .layui-laydate-header .layui-icon,.laydate-ym-show .laydate-set-ym span[lay-type=month]{display:none!important}.layui-laydate-header{position:relative;line-height:30px;padding:10px 70px 5px}.laydate-set-ym span,.layui-laydate-header i{padding:0 5px;cursor:pointer}.layui-laydate-header *{display:inline-block;vertical-align:bottom}.layui-laydate-header i{position:absolute;top:10px;color:#999;font-size:18px}.layui-laydate-header i.laydate-prev-y{left:15px}.layui-laydate-header i.laydate-prev-m{left:45px}.layui-laydate-header i.laydate-next-y{right:15px}.layui-laydate-header i.laydate-next-m{right:45px}.laydate-set-ym{width:100%;text-align:center;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.laydate-time-text{cursor:default!important}.layui-laydate-content{position:relative;padding:10px;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-laydate-content table{border-collapse:collapse;border-spacing:0}.layui-laydate-content td,.layui-laydate-content th{width:36px;height:30px;padding:5px;text-align:center}.layui-laydate-content td{position:relative;cursor:pointer}.laydate-day-mark{position:absolute;left:0;top:0;width:100%;height:100%;line-height:30px;font-size:12px;overflow:hidden}.laydate-day-mark::after{position:absolute;content:'';right:2px;top:2px;width:5px;height:5px;border-radius:50%}.layui-laydate-footer{position:relative;height:46px;line-height:26px;padding:10px 20px}.layui-laydate-footer span{margin-right:15px;display:inline-block;cursor:pointer;font-size:12px}.layui-laydate-footer span:hover{color:#5FB878}.laydate-footer-btns{position:absolute;right:10px;top:10px}.laydate-footer-btns span{height:26px;line-height:26px;margin:0 0 0 -1px;padding:0 10px;border:1px solid #C9C9C9;background-color:#fff;white-space:nowrap;vertical-align:top;border-radius:2px}.layui-laydate-list>li,.layui-laydate-range .layui-laydate-main{display:inline-block;vertical-align:middle}.layui-laydate-list{position:absolute;left:0;top:0;width:100%;height:100%;padding:10px;background-color:#fff}.layui-laydate-list>li{position:relative;width:33.3%;height:36px;line-height:36px;margin:3px 0;text-align:center;cursor:pointer}.laydate-month-list>li{width:25%;margin:17px 0}.laydate-time-list>li{height:100%;margin:0;line-height:normal;cursor:default}.laydate-time-list p{position:relative;top:-4px;line-height:29px}.laydate-time-list ol{height:181px;overflow:hidden}.laydate-time-list>li:hover ol{overflow-y:auto}.laydate-time-list ol li{width:130%;padding-left:33px;line-height:30px;text-align:left;cursor:pointer}.layui-laydate-hint{position:absolute;top:115px;left:50%;width:250px;margin-left:-125px;line-height:20px;padding:15px;text-align:center;font-size:12px}.layui-laydate-range{width:546px}.layui-laydate-range .laydate-main-list-0 .laydate-next-m,.layui-laydate-range .laydate-main-list-0 .laydate-next-y,.layui-laydate-range .laydate-main-list-1 .laydate-prev-m,.layui-laydate-range .laydate-main-list-1 .laydate-prev-y{display:none}.layui-laydate-range .laydate-main-list-1 .layui-laydate-content{border-left:1px solid #e2e2e2}.layui-laydate,.layui-laydate-hint{border:1px solid #d2d2d2;box-shadow:0 2px 4px rgba(0,0,0,.12);background-color:#fff;color:#666}.layui-laydate-header{border-bottom:1px solid #e2e2e2}.layui-laydate-header i:hover,.layui-laydate-header span:hover{color:#5FB878}.layui-laydate-content{border-top:none 0;border-bottom:none 0}.layui-laydate-content th{font-weight:400;color:#333}.layui-laydate-content td{color:#666}.layui-laydate-content td.laydate-selected{background-color:#00F7DE}.laydate-selected:hover{background-color:#00F7DE!important}.layui-laydate-content td:hover,.layui-laydate-list li:hover{background-color:#eaeaea;color:#333}.laydate-time-list li ol{margin:0;padding:0;border:1px solid #e2e2e2;border-left-width:0}.laydate-time-list li:first-child ol{border-left-width:1px}.laydate-time-list>li:hover{background:0 0}.layui-laydate-content .laydate-day-next,.layui-laydate-content .laydate-day-prev{color:#d2d2d2}.laydate-selected.laydate-day-next,.laydate-selected.laydate-day-prev{background-color:#f8f8f8!important}.layui-laydate-footer{border-top:1px solid #e2e2e2}.layui-laydate-hint{color:#FF5722}.laydate-day-mark::after{background-color:#5FB878}.layui-laydate-content td.layui-this .laydate-day-mark::after{display:none}.layui-laydate-footer span[lay-type=date]{color:#5FB878}.layui-laydate .layui-this{background-color:#009688!important;color:#fff!important}.layui-laydate .laydate-disabled,.layui-laydate .laydate-disabled:hover{background:0 0!important;color:#d2d2d2!important;cursor:not-allowed!important;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.laydate-theme-molv{border:none}.laydate-theme-molv.layui-laydate-range{width:548px}.laydate-theme-molv .layui-laydate-main{width:274px}.laydate-theme-molv .layui-laydate-header{border:none;background-color:#009688}.laydate-theme-molv .layui-laydate-header i,.laydate-theme-molv .layui-laydate-header span{color:#f6f6f6}.laydate-theme-molv .layui-laydate-header i:hover,.laydate-theme-molv .layui-laydate-header span:hover{color:#fff}.laydate-theme-molv .layui-laydate-content{border:1px solid #e2e2e2;border-top:none;border-bottom:none}.laydate-theme-molv .laydate-main-list-1 .layui-laydate-content{border-left:none}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li,.laydate-theme-grid .layui-laydate-content td,.laydate-theme-grid .layui-laydate-content thead,.laydate-theme-molv .layui-laydate-footer{border:1px solid #e2e2e2}.laydate-theme-grid .laydate-selected,.laydate-theme-grid .laydate-selected:hover{background-color:#f2f2f2!important;color:#009688!important}.laydate-theme-grid .laydate-selected.laydate-day-next,.laydate-theme-grid .laydate-selected.laydate-day-prev{color:#d2d2d2!important}.laydate-theme-grid .laydate-month-list,.laydate-theme-grid .laydate-year-list{margin:1px 0 0 1px}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li{margin:0 -1px -1px 0}.laydate-theme-grid .laydate-year-list>li{height:43px;line-height:43px}.laydate-theme-grid .laydate-month-list>li{height:71px;line-height:71px}
--------------------------------------------------------------------------------
/forward-server/static/layui/css/modules/laydate/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/css/modules/laydate/icon.png
--------------------------------------------------------------------------------
/forward-server/static/layui/css/modules/layer/default/icon-ext.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/css/modules/layer/default/icon-ext.png
--------------------------------------------------------------------------------
/forward-server/static/layui/css/modules/layer/default/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/css/modules/layer/default/icon.png
--------------------------------------------------------------------------------
/forward-server/static/layui/css/modules/layer/default/loading-0.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/css/modules/layer/default/loading-0.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/css/modules/layer/default/loading-1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/css/modules/layer/default/loading-1.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/css/modules/layer/default/loading-2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/css/modules/layer/default/loading-2.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/font/iconfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/font/iconfont.eot
--------------------------------------------------------------------------------
/forward-server/static/layui/font/iconfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/font/iconfont.ttf
--------------------------------------------------------------------------------
/forward-server/static/layui/font/iconfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/font/iconfont.woff
--------------------------------------------------------------------------------
/forward-server/static/layui/font/iconfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/font/iconfont.woff2
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/0.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/0.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/1.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/10.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/10.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/11.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/11.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/12.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/12.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/13.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/13.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/14.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/14.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/15.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/15.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/16.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/16.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/17.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/17.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/18.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/18.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/19.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/19.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/2.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/20.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/20.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/21.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/21.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/22.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/22.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/23.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/23.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/24.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/24.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/25.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/25.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/26.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/26.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/27.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/27.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/28.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/28.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/29.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/29.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/3.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/30.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/30.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/31.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/31.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/32.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/32.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/33.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/33.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/34.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/34.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/35.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/35.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/36.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/36.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/37.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/37.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/38.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/38.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/39.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/39.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/4.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/40.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/40.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/41.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/41.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/42.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/42.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/43.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/43.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/44.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/44.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/45.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/45.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/46.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/46.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/47.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/47.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/48.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/48.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/49.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/49.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/5.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/50.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/50.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/51.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/51.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/52.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/52.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/53.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/53.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/54.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/54.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/55.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/55.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/56.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/56.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/57.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/57.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/58.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/58.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/59.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/59.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/6.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/6.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/60.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/60.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/61.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/61.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/62.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/62.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/63.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/63.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/64.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/64.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/65.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/65.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/66.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/66.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/67.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/67.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/68.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/68.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/69.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/69.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/7.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/7.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/70.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/70.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/71.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/71.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/8.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/8.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/images/face/9.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tavenli/port-forward/0e9367f99f92b5b638efd5d0a3e76bcaa588dae6/forward-server/static/layui/images/face/9.gif
--------------------------------------------------------------------------------
/forward-server/static/layui/lay/modules/carousel.js:
--------------------------------------------------------------------------------
1 | /** layui-v2.5.6 MIT License By https://www.layui.com */
2 | ;layui.define("jquery",function(e){"use strict";var i=layui.$,n=(layui.hint(),layui.device(),{config:{},set:function(e){var n=this;return n.config=i.extend({},n.config,e),n},on:function(e,i){return layui.onevent.call(this,t,e,i)}}),t="carousel",a="layui-this",l=">*[carousel-item]>*",o="layui-carousel-left",r="layui-carousel-right",d="layui-carousel-prev",s="layui-carousel-next",u="layui-carousel-arrow",c="layui-carousel-ind",m=function(e){var t=this;t.config=i.extend({},t.config,n.config,e),t.render()};m.prototype.config={width:"600px",height:"280px",full:!1,arrow:"hover",indicator:"inside",autoplay:!0,interval:3e3,anim:"",trigger:"click",index:0},m.prototype.render=function(){var e=this,n=e.config;n.elem=i(n.elem),n.elem[0]&&(e.elemItem=n.elem.find(l),n.index<0&&(n.index=0),n.index>=e.elemItem.length&&(n.index=e.elemItem.length-1),n.interval<800&&(n.interval=800),n.full?n.elem.css({position:"fixed",width:"100%",height:"100%",zIndex:9999}):n.elem.css({width:n.width,height:n.height}),n.elem.attr("lay-anim",n.anim),e.elemItem.eq(n.index).addClass(a),e.elemItem.length<=1||(e.indicator(),e.arrow(),e.autoplay(),e.events()))},m.prototype.reload=function(e){var n=this;clearInterval(n.timer),n.config=i.extend({},n.config,e),n.render()},m.prototype.prevIndex=function(){var e=this,i=e.config,n=i.index-1;return n<0&&(n=e.elemItem.length-1),n},m.prototype.nextIndex=function(){var e=this,i=e.config,n=i.index+1;return n>=e.elemItem.length&&(n=0),n},m.prototype.addIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index+e,n.index>=i.elemItem.length&&(n.index=0)},m.prototype.subIndex=function(e){var i=this,n=i.config;e=e||1,n.index=n.index-e,n.index<0&&(n.index=i.elemItem.length-1)},m.prototype.autoplay=function(){var e=this,i=e.config;i.autoplay&&(clearInterval(e.timer),e.timer=setInterval(function(){e.slide()},i.interval))},m.prototype.arrow=function(){var e=this,n=e.config,t=i(['",'"].join(""));n.elem.attr("lay-arrow",n.arrow),n.elem.find("."+u)[0]&&n.elem.find("."+u).remove(),n.elem.append(t),t.on("click",function(){var n=i(this),t=n.attr("lay-type");e.slide(t)})},m.prototype.indicator=function(){var e=this,n=e.config,t=e.elemInd=i(['
'+(t||"")+"
");e.find("."+d)[0]&&e.find("."+d).remove(),t.replace(/\s/g,"")&&e.append(n)},x.prototype.setValue=function(){var e=this,t=e.config,n=[];return e.layBox.eq(1).find("."+y+' input[type="checkbox"]').each(function(){var e=a(this).data("hide");e||n.push(this.value)}),t.value=n,e},x.prototype.parseData=function(e){var t=this,n=t.config,i=[];return layui.each(n.data,function(t,l){l=("function"==typeof n.parseData?n.parseData(l):l)||l,i.push(l=a.extend({},l)),layui.each(n.value,function(e,a){a==l.value&&(l.selected=!0)}),e&&e(l)}),n.data=i,t},x.prototype.getData=function(e){var a=this,t=a.config,n=[];return a.setValue(),layui.each(e||t.value,function(e,a){layui.each(t.data,function(e,t){delete t.selected,a==t.value&&n.push(t)})}),n},x.prototype.events=function(){var e=this,t=e.config;e.elem.on("click",'input[lay-filter="layTransferCheckbox"]+',function(){var t=a(this).prev(),n=t[0].checked,i=t.parents("."+s).eq(0).find("."+y);t[0].disabled||("all"===t.attr("lay-type")&&i.find('input[type="checkbox"]').each(function(){this.disabled||(this.checked=n)}),e.renderCheckBtn({stopNone:!0}))}),e.layBtn.on("click",function(){var n=a(this),i=n.data("index"),l=e.layBox.eq(i),r=[];if(!n.hasClass(o)){e.layBox.eq(i).each(function(t){var n=a(this),i=n.find("."+y);i.children("li").each(function(){var t=a(this),n=t.find('input[type="checkbox"]'),i=n.data("hide");n[0].checked&&!i&&(n[0].checked=!1,l.siblings("."+s).find("."+y).append(t.clone()),t.remove(),r.push(n[0].value)),e.setValue()})}),e.renderCheckBtn();var c=l.siblings("."+s).find("."+h+" input");""===c.val()||c.trigger("keyup"),t.onchange&&t.onchange(e.getData(r),i)}}),e.laySearch.find("input").on("keyup",function(){var n=this.value,i=a(this).parents("."+h).eq(0).siblings("."+y),l=i.children("li");l.each(function(){var e=a(this),t=e.find('input[type="checkbox"]'),i=t[0].title.indexOf(n)!==-1;e[i?"removeClass":"addClass"](c),t.data("hide",!i)}),e.renderCheckBtn();var r=l.length===i.children("li."+c).length;e.noneView(i,r?t.text.searchNone:"")})},r.that={},r.config={},l.reload=function(e,a){var t=r.that[e];return t.reload(a),r.call(t)},l.getData=function(e){var a=r.that[e];return a.getData()},l.render=function(e){var a=new x(e);return r.call(a)},e(i,l)}); -------------------------------------------------------------------------------- /forward-server/static/layui/lay/modules/upload.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.5.6 MIT License By https://www.layui.com */ 2 | ;layui.define("layer",function(e){"use strict";var t=layui.$,i=layui.layer,n=layui.hint(),o=layui.device(),a={config:{},set:function(e){var i=this;return i.config=t.extend({},i.config,e),i},on:function(e,t){return layui.onevent.call(this,r,e,t)}},l=function(){var e=this;return{upload:function(t){e.upload.call(e,t)},reload:function(t){e.reload.call(e,t)},config:e.config}},r="upload",u="layui-upload-file",c="layui-upload-form",f="layui-upload-iframe",s="layui-upload-choose",p=function(e){var i=this;i.config=t.extend({},i.config,a.config,e),i.render()};p.prototype.config={accept:"images",exts:"",auto:!0,bindAction:"",url:"",field:"file",acceptMime:"",method:"post",data:{},drag:!0,size:0,number:0,multiple:!1},p.prototype.render=function(e){var i=this,e=i.config;e.elem=t(e.elem),e.bindAction=t(e.bindAction),i.file(),i.events()},p.prototype.file=function(){var e=this,i=e.config,n=e.elemFile=t(['"].join("")),a=i.elem.next();(a.hasClass(u)||a.hasClass(c))&&a.remove(),o.ie&&o.ie<10&&i.elem.wrap(''),e.isFile()?(e.elemFile=i.elem,i.field=i.elem[0].name):i.elem.after(n),o.ie&&o.ie<10&&e.initIE()},p.prototype.initIE=function(){var e=this,i=e.config,n=t(''),o=t(['"].join(""));t("#"+f)[0]||t("body").append(n),i.elem.next().hasClass(c)||(e.elemFile.wrap(o),i.elem.next("."+c).append(function(){var e=[];return layui.each(i.data,function(t,i){i="function"==typeof i?i():i,e.push('')}),e.join("")}()))},p.prototype.msg=function(e){return i.msg(e,{icon:2,shift:6})},p.prototype.isFile=function(){var e=this.config.elem[0];if(e)return"input"===e.tagName.toLocaleLowerCase()&&"file"===e.type},p.prototype.preview=function(e){var t=this;window.FileReader&&layui.each(t.chooseFiles,function(t,i){var n=new FileReader;n.readAsDataURL(i),n.onload=function(){e&&e(t,i,this.result)}})},p.prototype.upload=function(e,i){var n,a=this,l=a.config,r=a.elemFile[0],u=function(){var i=0,n=0,o=e||a.files||a.chooseFiles||r.files,u=function(){l.multiple&&i+n===a.fileLength&&"function"==typeof l.allDone&&l.allDone({total:a.fileLength,successful:i,aborted:n})};layui.each(o,function(e,o){var r=new FormData;r.append(l.field,o),layui.each(l.data,function(e,t){t="function"==typeof t?t():t,r.append(e,t)});var c={url:l.url,type:"post",data:r,contentType:!1,processData:!1,dataType:"json",headers:l.headers||{},success:function(t){i++,d(e,t),u()},error:function(){n++,a.msg("请求上传接口出现异常"),m(e),u()}};"function"==typeof l.progress&&(c.xhr=function(){var e=t.ajaxSettings.xhr();return e.upload.addEventListener("progress",function(e){if(e.lengthComputable){var t=Math.floor(e.loaded/e.total*100);l.progress(t,l.item[0],e)}}),e}),t.ajax(c)})},c=function(){var e=t("#"+f);a.elemFile.parent().submit(),clearInterval(p.timer),p.timer=setInterval(function(){var t,i=e.contents().find("body");try{t=i.text()}catch(n){a.msg("获取上传后的响应信息出现异常"),clearInterval(p.timer),m()}t&&(clearInterval(p.timer),i.html(""),d(0,t))},30)},d=function(e,t){if(a.elemFile.next("."+s).remove(),r.value="","object"!=typeof t)try{t=JSON.parse(t)}catch(i){return t={},a.msg("请对上传接口返回有效JSON")}"function"==typeof l.done&&l.done(t,e||0,function(e){a.upload(e)})},m=function(e){l.auto&&(r.value=""),"function"==typeof l.error&&l.error(e||0,function(e){a.upload(e)})},h=l.exts,v=function(){var t=[];return layui.each(e||a.chooseFiles,function(e,i){t.push(i.name)}),t}(),g={preview:function(e){a.preview(e)},upload:function(e,t){var i={};i[e]=t,a.upload(i)},pushFile:function(){return a.files=a.files||{},layui.each(a.chooseFiles,function(e,t){a.files[e]=t}),a.files},resetFile:function(e,t,i){var n=new File([t],i);a.files=a.files||{},a.files[e]=n}},y=function(){if("choose"!==i&&!l.auto||(l.choose&&l.choose(g),"choose"!==i))return l.before&&l.before(g),o.ie?o.ie>9?u():c():void u()};if(v=0===v.length?r.value.match(/[^\/\\]+\..+/g)||[]||"":v,0!==v.length){switch(l.accept){case"file":if(h&&!RegExp("\\w\\.("+h+")$","i").test(escape(v)))return a.msg("选择的文件中包含不支持的格式"),r.value="";break;case"video":if(!RegExp("\\w\\.("+(h||"avi|mp4|wma|rmvb|rm|flash|3gp|flv")+")$","i").test(escape(v)))return a.msg("选择的视频中包含不支持的格式"),r.value="";break;case"audio":if(!RegExp("\\w\\.("+(h||"mp3|wav|mid")+")$","i").test(escape(v)))return a.msg("选择的音频中包含不支持的格式"),r.value="";break;default:if(layui.each(v,function(e,t){RegExp("\\w\\.("+(h||"jpg|png|gif|bmp|jpeg$")+")","i").test(escape(t))||(n=!0)}),n)return a.msg("选择的图片中包含不支持的格式"),r.value=""}if(a.fileLength=function(){var t=0,i=e||a.files||a.chooseFiles||r.files;return layui.each(i,function(){t++}),t}(),l.number&&a.fileLength>l.number)return a.msg("同时最多只能上传的数量为:"+l.number);if(l.size>0&&!(o.ie&&o.ie<10)){var F;if(layui.each(a.chooseFiles,function(e,t){if(t.size>1024*l.size){var i=l.size/1024;i=i>=1?i.toFixed(2)+"MB":l.size+"KB",r.value="",F=i}}),F)return a.msg("文件不能超过"+F)}y()}},p.prototype.reload=function(e){e=e||{},delete e.elem,delete e.bindAction;var i=this,e=i.config=t.extend({},i.config,a.config,e),n=e.elem.next();n.attr({name:e.name,accept:e.acceptMime,multiple:e.multiple})},p.prototype.events=function(){var e=this,i=e.config,a=function(t){e.chooseFiles={},layui.each(t,function(t,i){var n=(new Date).getTime();e.chooseFiles[n+"-"+t]=i})},l=function(t,n){var o=e.elemFile,a=t.length>1?t.length+"个文件":(t[0]||{}).name||o[0].value.match(/[^\/\\]+\..+/g)||[]||"";o.next().hasClass(s)&&o.next().remove(),e.upload(null,"choose"),e.isFile()||i.choose||o.after(''+a+"")};i.elem.off("upload.start").on("upload.start",function(){var o=t(this),a=o.attr("lay-data");if(a)try{a=new Function("return "+a)(),e.config=t.extend({},i,a)}catch(l){n.error("Upload element property lay-data configuration item has a syntax error: "+a)}e.config.item=o,e.elemFile[0].click()}),o.ie&&o.ie<10||i.elem.off("upload.over").on("upload.over",function(){var e=t(this);e.attr("lay-over","")}).off("upload.leave").on("upload.leave",function(){var e=t(this);e.removeAttr("lay-over")}).off("upload.drop").on("upload.drop",function(n,o){var r=t(this),u=o.originalEvent.dataTransfer.files||[];r.removeAttr("lay-over"),a(u),i.auto?e.upload(u):l(u)}),e.elemFile.off("upload.change").on("upload.change",function(){var t=this.files||[];a(t),i.auto?e.upload():l(t)}),i.bindAction.off("upload.action").on("upload.action",function(){e.upload()}),i.elem.data("haveEvents")||(e.elemFile.on("change",function(){t(this).trigger("upload.change")}),i.elem.on("click",function(){e.isFile()||t(this).trigger("upload.start")}),i.drag&&i.elem.on("dragover",function(e){e.preventDefault(),t(this).trigger("upload.over")}).on("dragleave",function(e){t(this).trigger("upload.leave")}).on("drop",function(e){e.preventDefault(),t(this).trigger("upload.drop",e)}),i.bindAction.on("click",function(){t(this).trigger("upload.action")}),i.elem.data("haveEvents",!0))},a.render=function(e){var t=new p(e);return l.call(t)},e(r,a)}); -------------------------------------------------------------------------------- /forward-server/static/layui/lay/modules/util.js: -------------------------------------------------------------------------------- 1 | /** layui-v2.5.6 MIT License By https://www.layui.com */ 2 | ;layui.define("jquery",function(e){"use strict";var t=layui.$,i={fixbar:function(e){var i,n,a="layui-fixbar",o="layui-fixbar-top",r=t(document),l=t("body");e=t.extend({showHeight:200},e),e.bar1=e.bar1===!0?"":e.bar1,e.bar2=e.bar2===!0?"":e.bar2,e.bgcolor=e.bgcolor?"background-color:"+e.bgcolor:"";var c=[e.bar1,e.bar2,""],u=t(['6 | 本工具提供RestfulApi,方便与其它系统工具进行集成,可在其它系统中直接通过接口,实现端口转发的开启和关闭。 7 |8 |
11 | 当前API的AUTH鉴权密钥为:{{config "String" "api.auth" "请在data.conf中配置api.auth属性"}} 12 |13 | 14 | 17 | 84 | 85 | 88 | 154 | 155 |