├── .gitignore
├── LICENSE
├── MainForm.gfm
├── MainForm.go
├── MainFormImpl.go
├── README.md
├── Sample
└── sample.go
├── build
├── README.md
├── build-darwin32-GUI.sh
├── build-darwin32.sh
├── build-darwin64-GUI.sh
├── build-darwin64.sh
├── build-linux32-GUI.sh
├── build-linux32.sh
├── build-linux64-GUI.sh
├── build-linux64.sh
├── build-win32-GUI.bat
├── build-win32.bat
├── build-win64-GUI.bat
├── build-win64.bat
└── rpbuild.go
├── cert
├── README.md
├── ca.der
├── ca.key
├── ca.pem
├── client.key
├── client.pem
├── server.key
└── server.pem
├── conf
├── config.cfg
└── confighttps.cfg
├── icon_posix.go
├── icon_windows.go
├── imgs
├── README.md
├── cli.jpg
├── rp_linux.jpg
├── rp_macOS.jpg
├── rp_windows.jpg
├── rproxy.icns
├── rproxy.ico
├── rproxy.png
└── svr.jpg
├── librp
├── client.go
├── common.go
├── config.go
├── log.go
├── log_posix.go
├── log_windows.go
├── net.go
├── net_test.go
└── server.go
├── lzUI
├── build.bat
├── lzUI.ico
├── lzUI.lpi
├── lzUI.lpr
├── lzUI.lps
├── lzUI.res
├── umain.lfm
└── umain.pas
├── main_gui.go
└── main_nogui.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 | *.test
24 | *.prof
25 | .idea
26 | /cmd
27 | /lzUI/backup
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MainForm.gfm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ying32/rproxy/6d385c87b5d6799e62c0f07ec08701d6d3aa2cdb/MainForm.gfm
--------------------------------------------------------------------------------
/MainForm.go:
--------------------------------------------------------------------------------
1 | // +build gui
2 |
3 | // 由res2go自动生成,不要编辑。
4 | package main
5 |
6 | import (
7 | "github.com/ying32/govcl/vcl"
8 | )
9 |
10 | type TMainForm struct {
11 | *vcl.TForm
12 | StatusBar1 *vcl.TStatusBar
13 | Panel1 *vcl.TPanel
14 | Panel2 *vcl.TPanel
15 | BtnStart *vcl.TButton
16 | BtnStop *vcl.TButton
17 | Label12 *vcl.TLabel
18 | PageControl1 *vcl.TPageControl
19 | TabSheet1 *vcl.TTabSheet
20 | RGMode *vcl.TRadioGroup
21 | GBBase *vcl.TGroupBox
22 | Label1 *vcl.TLabel
23 | Label2 *vcl.TLabel
24 | Label3 *vcl.TLabel
25 | Label7 *vcl.TLabel
26 | SpinTCPPort *vcl.TSpinEdit
27 | SpinCliHTTPPort *vcl.TSpinEdit
28 | ChkIsZip *vcl.TCheckBox
29 | EditVerifyKey *vcl.TEdit
30 | BtnRandKey *vcl.TButton
31 | BtnSaveCfg *vcl.TButton
32 | BtnLoadCfg *vcl.TButton
33 | BtnNewCfg *vcl.TButton
34 | EditSvrAddr *vcl.TEdit
35 | ChkIsHttps *vcl.TCheckBox
36 | Label8 *vcl.TLabel
37 | SpinSvrHTTPPort *vcl.TSpinEdit
38 | GBTLS *vcl.TGroupBox
39 | Panel3 *vcl.TPanel
40 | Label4 *vcl.TLabel
41 | EditTLSCAFile *vcl.TEdit
42 | BtnCAOpen *vcl.TButton
43 | PageControl2 *vcl.TPageControl
44 | TabSheet3 *vcl.TTabSheet
45 | Panel4 *vcl.TPanel
46 | Label6 *vcl.TLabel
47 | EditTLSCliKeyFile *vcl.TEdit
48 | Label5 *vcl.TLabel
49 | EditTLSCliCertFile *vcl.TEdit
50 | BtnCliCertOpen *vcl.TButton
51 | BtnCliKeyOpen *vcl.TButton
52 | TabSheet4 *vcl.TTabSheet
53 | Panel5 *vcl.TPanel
54 | Label10 *vcl.TLabel
55 | EditTLSSvrCertFile *vcl.TEdit
56 | BtnSvrCertOpen *vcl.TButton
57 | BtnSvrKeyOpen *vcl.TButton
58 | EditTLSSvrKeyFile *vcl.TEdit
59 | Label11 *vcl.TLabel
60 | GBAppSettings *vcl.TGroupBox
61 | ChkAutoReconnect *vcl.TCheckBox
62 | Label9 *vcl.TLabel
63 | SpinMaxLogLine *vcl.TSpinEdit
64 | TabSheet2 *vcl.TTabSheet
65 | LstLogs *vcl.TListBox
66 | DlgSaveCfg *vcl.TSaveDialog
67 | DlgOpen *vcl.TOpenDialog
68 | ActionList1 *vcl.TActionList
69 | ActStart *vcl.TAction
70 | ActStop *vcl.TAction
71 | TrayIcon1 *vcl.TTrayIcon
72 |
73 | //::private::
74 | TMainFormFields
75 | }
76 |
77 | var MainForm *TMainForm
78 |
79 |
80 |
81 |
82 | // 以字节形式加载
83 | // vcl.Application.CreateForm(&MainForm)
84 |
85 | func NewMainForm(owner vcl.IComponent) (root *TMainForm) {
86 | vcl.CreateResForm(owner, &root)
87 | return
88 | }
89 |
90 | var mainFormBytes = []byte("\x54\x50\x46\x30\x09\x54\x4D\x61\x69\x6E\x46\x6F\x72\x6D\x08\x4D\x61\x69\x6E\x46\x6F\x72\x6D\x04\x4C\x65\x66\x74\x03\x89\x03\x06\x48\x65\x69\x67\x68\x74\x03\x46\x02\x03\x54\x6F\x70\x03\x20\x01\x05\x57\x69\x64\x74\x68\x03\xF2\x01\x0B\x42\x6F\x72\x64\x65\x72\x49\x63\x6F\x6E\x73\x0B\x0C\x62\x69\x53\x79\x73\x74\x65\x6D\x4D\x65\x6E\x75\x0A\x62\x69\x4D\x69\x6E\x69\x6D\x69\x7A\x65\x00\x0B\x42\x6F\x72\x64\x65\x72\x53\x74\x79\x6C\x65\x07\x08\x62\x73\x53\x69\x6E\x67\x6C\x65\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x06\x72\x70\x72\x6F\x78\x79\x0C\x43\x6C\x69\x65\x6E\x74\x48\x65\x69\x67\x68\x74\x03\x46\x02\x0B\x43\x6C\x69\x65\x6E\x74\x57\x69\x64\x74\x68\x03\xF2\x01\x08\x50\x6F\x73\x69\x74\x69\x6F\x6E\x07\x0E\x70\x6F\x53\x63\x72\x65\x65\x6E\x43\x65\x6E\x74\x65\x72\x0A\x4C\x43\x4C\x56\x65\x72\x73\x69\x6F\x6E\x06\x07\x32\x2E\x30\x2E\x36\x2E\x30\x00\x0A\x54\x53\x74\x61\x74\x75\x73\x42\x61\x72\x0A\x53\x74\x61\x74\x75\x73\x42\x61\x72\x31\x04\x4C\x65\x66\x74\x02\x00\x06\x48\x65\x69\x67\x68\x74\x02\x16\x03\x54\x6F\x70\x03\x30\x02\x05\x57\x69\x64\x74\x68\x03\xF2\x01\x06\x50\x61\x6E\x65\x6C\x73\x0E\x00\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x00\x06\x54\x50\x61\x6E\x65\x6C\x06\x50\x61\x6E\x65\x6C\x31\x04\x4C\x65\x66\x74\x02\x00\x06\x48\x65\x69\x67\x68\x74\x03\x30\x02\x03\x54\x6F\x70\x02\x00\x05\x57\x69\x64\x74\x68\x03\xF2\x01\x05\x41\x6C\x69\x67\x6E\x07\x08\x61\x6C\x43\x6C\x69\x65\x6E\x74\x0A\x42\x65\x76\x65\x6C\x4F\x75\x74\x65\x72\x07\x06\x62\x76\x4E\x6F\x6E\x65\x0C\x43\x6C\x69\x65\x6E\x74\x48\x65\x69\x67\x68\x74\x03\x30\x02\x0B\x43\x6C\x69\x65\x6E\x74\x57\x69\x64\x74\x68\x03\xF2\x01\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x01\x00\x06\x54\x50\x61\x6E\x65\x6C\x06\x50\x61\x6E\x65\x6C\x32\x04\x4C\x65\x66\x74\x02\x00\x06\x48\x65\x69\x67\x68\x74\x02\x22\x03\x54\x6F\x70\x03\x0E\x02\x05\x57\x69\x64\x74\x68\x03\xF2\x01\x05\x41\x6C\x69\x67\x6E\x07\x08\x61\x6C\x42\x6F\x74\x74\x6F\x6D\x0C\x43\x6C\x69\x65\x6E\x74\x48\x65\x69\x67\x68\x74\x02\x22\x0B\x43\x6C\x69\x65\x6E\x74\x57\x69\x64\x74\x68\x03\xF2\x01\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x00\x00\x07\x54\x42\x75\x74\x74\x6F\x6E\x08\x42\x74\x6E\x53\x74\x61\x72\x74\x04\x4C\x65\x66\x74\x03\x46\x01\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x02\x04\x05\x57\x69\x64\x74\x68\x02\x4B\x06\x41\x63\x74\x69\x6F\x6E\x07\x08\x41\x63\x74\x53\x74\x61\x72\x74\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x00\x00\x00\x07\x54\x42\x75\x74\x74\x6F\x6E\x07\x42\x74\x6E\x53\x74\x6F\x70\x04\x4C\x65\x66\x74\x03\x9A\x01\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x02\x04\x05\x57\x69\x64\x74\x68\x02\x4B\x06\x41\x63\x74\x69\x6F\x6E\x07\x07\x41\x63\x74\x53\x74\x6F\x70\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x01\x00\x00\x06\x54\x4C\x61\x62\x65\x6C\x07\x4C\x61\x62\x65\x6C\x31\x32\x04\x4C\x65\x66\x74\x02\x20\x06\x48\x65\x69\x67\x68\x74\x02\x11\x03\x54\x6F\x70\x02\x09\x05\x57\x69\x64\x74\x68\x03\x14\x01\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x45\xE6\xB3\xA8\xEF\xBC\x9A\xE5\xA6\x82\xE6\x9C\x89\xE4\xBF\xAE\xE6\x94\xB9\xE9\x85\x8D\xE7\xBD\xAE\xEF\xBC\x8C\xE9\x9C\x80\xE8\xA6\x81\xE3\x80\x90\xE4\xBF\x9D\xE5\xAD\x98\xE9\x85\x8D\xE7\xBD\xAE\xE3\x80\x91\xE5\x90\x8E\xE6\x96\xB9\xE8\x83\xBD\xE7\x94\x9F\xE6\x95\x88\xE3\x80\x82\x0A\x46\x6F\x6E\x74\x2E\x43\x6F\x6C\x6F\x72\x07\x07\x63\x6C\x47\x72\x65\x65\x6E\x0B\x50\x61\x72\x65\x6E\x74\x43\x6F\x6C\x6F\x72\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x00\x00\x0C\x54\x50\x61\x67\x65\x43\x6F\x6E\x74\x72\x6F\x6C\x0C\x50\x61\x67\x65\x43\x6F\x6E\x74\x72\x6F\x6C\x31\x04\x4C\x65\x66\x74\x02\x00\x06\x48\x65\x69\x67\x68\x74\x03\x0E\x02\x03\x54\x6F\x70\x02\x00\x05\x57\x69\x64\x74\x68\x03\xF2\x01\x0A\x41\x63\x74\x69\x76\x65\x50\x61\x67\x65\x07\x09\x54\x61\x62\x53\x68\x65\x65\x74\x31\x05\x41\x6C\x69\x67\x6E\x07\x08\x61\x6C\x43\x6C\x69\x65\x6E\x74\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x49\x6E\x64\x65\x78\x02\x00\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x01\x08\x4F\x6E\x43\x68\x61\x6E\x67\x65\x07\x12\x50\x61\x67\x65\x43\x6F\x6E\x74\x72\x6F\x6C\x31\x43\x68\x61\x6E\x67\x65\x00\x09\x54\x54\x61\x62\x53\x68\x65\x65\x74\x09\x54\x61\x62\x53\x68\x65\x65\x74\x31\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x19\xE6\x9C\x8D\xE5\x8A\xA1\xE7\xAB\xAF\x2F\xE5\xAE\xA2\xE6\x88\xB7\xE7\xAB\xAF\xE8\xAE\xBE\xE7\xBD\xAE\x0C\x43\x6C\x69\x65\x6E\x74\x48\x65\x69\x67\x68\x74\x03\xF0\x01\x0B\x43\x6C\x69\x65\x6E\x74\x57\x69\x64\x74\x68\x03\xEA\x01\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x0B\x54\x52\x61\x64\x69\x6F\x47\x72\x6F\x75\x70\x06\x52\x47\x4D\x6F\x64\x65\x04\x4C\x65\x66\x74\x02\x00\x06\x48\x65\x69\x67\x68\x74\x02\x3A\x03\x54\x6F\x70\x02\x00\x05\x57\x69\x64\x74\x68\x03\xEA\x01\x05\x41\x6C\x69\x67\x6E\x07\x05\x61\x6C\x54\x6F\x70\x08\x41\x75\x74\x6F\x46\x69\x6C\x6C\x09\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x0C\xE6\xA8\xA1\xE5\xBC\x8F\xE9\x80\x89\xE6\x8B\xA9\x1C\x43\x68\x69\x6C\x64\x53\x69\x7A\x69\x6E\x67\x2E\x4C\x65\x66\x74\x52\x69\x67\x68\x74\x53\x70\x61\x63\x69\x6E\x67\x02\x06\x1D\x43\x68\x69\x6C\x64\x53\x69\x7A\x69\x6E\x67\x2E\x45\x6E\x6C\x61\x72\x67\x65\x48\x6F\x72\x69\x7A\x6F\x6E\x74\x61\x6C\x07\x18\x63\x72\x73\x48\x6F\x6D\x6F\x67\x65\x6E\x6F\x75\x73\x43\x68\x69\x6C\x64\x52\x65\x73\x69\x7A\x65\x1B\x43\x68\x69\x6C\x64\x53\x69\x7A\x69\x6E\x67\x2E\x45\x6E\x6C\x61\x72\x67\x65\x56\x65\x72\x74\x69\x63\x61\x6C\x07\x18\x63\x72\x73\x48\x6F\x6D\x6F\x67\x65\x6E\x6F\x75\x73\x43\x68\x69\x6C\x64\x52\x65\x73\x69\x7A\x65\x1C\x43\x68\x69\x6C\x64\x53\x69\x7A\x69\x6E\x67\x2E\x53\x68\x72\x69\x6E\x6B\x48\x6F\x72\x69\x7A\x6F\x6E\x74\x61\x6C\x07\x0E\x63\x72\x73\x53\x63\x61\x6C\x65\x43\x68\x69\x6C\x64\x73\x1A\x43\x68\x69\x6C\x64\x53\x69\x7A\x69\x6E\x67\x2E\x53\x68\x72\x69\x6E\x6B\x56\x65\x72\x74\x69\x63\x61\x6C\x07\x0E\x63\x72\x73\x53\x63\x61\x6C\x65\x43\x68\x69\x6C\x64\x73\x12\x43\x68\x69\x6C\x64\x53\x69\x7A\x69\x6E\x67\x2E\x4C\x61\x79\x6F\x75\x74\x07\x1D\x63\x63\x6C\x4C\x65\x66\x74\x54\x6F\x52\x69\x67\x68\x74\x54\x68\x65\x6E\x54\x6F\x70\x54\x6F\x42\x6F\x74\x74\x6F\x6D\x1B\x43\x68\x69\x6C\x64\x53\x69\x7A\x69\x6E\x67\x2E\x43\x6F\x6E\x74\x72\x6F\x6C\x73\x50\x65\x72\x4C\x69\x6E\x65\x02\x02\x0C\x43\x6C\x69\x65\x6E\x74\x48\x65\x69\x67\x68\x74\x02\x24\x0B\x43\x6C\x69\x65\x6E\x74\x57\x69\x64\x74\x68\x03\xE6\x01\x07\x43\x6F\x6C\x75\x6D\x6E\x73\x02\x02\x09\x49\x74\x65\x6D\x49\x6E\x64\x65\x78\x02\x00\x0D\x49\x74\x65\x6D\x73\x2E\x53\x74\x72\x69\x6E\x67\x73\x01\x06\x09\xE5\xAE\xA2\xE6\x88\xB7\xE7\xAB\xAF\x06\x09\xE6\x9C\x8D\xE5\x8A\xA1\xE7\xAB\xAF\x00\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x00\x00\x00\x09\x54\x47\x72\x6F\x75\x70\x42\x6F\x78\x06\x47\x42\x42\x61\x73\x65\x04\x4C\x65\x66\x74\x02\x00\x06\x48\x65\x69\x67\x68\x74\x03\xDD\x00\x03\x54\x6F\x70\x02\x6D\x05\x57\x69\x64\x74\x68\x03\xEA\x01\x05\x41\x6C\x69\x67\x6E\x07\x08\x61\x6C\x43\x6C\x69\x65\x6E\x74\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x19\xE6\x9C\x8D\xE5\x8A\xA1\xE7\xAB\xAF\x2F\xE5\xAE\xA2\xE6\x88\xB7\xE7\xAB\xAF\xE8\xAE\xBE\xE7\xBD\xAE\x0C\x43\x6C\x69\x65\x6E\x74\x48\x65\x69\x67\x68\x74\x03\xC7\x00\x0B\x43\x6C\x69\x65\x6E\x74\x57\x69\x64\x74\x68\x03\xE6\x01\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x01\x00\x06\x54\x4C\x61\x62\x65\x6C\x06\x4C\x61\x62\x65\x6C\x31\x04\x4C\x65\x66\x74\x02\x11\x06\x48\x65\x69\x67\x68\x74\x02\x16\x03\x54\x6F\x70\x02\x06\x05\x57\x69\x64\x74\x68\x03\xA2\x00\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x1E\xE8\xBF\x9E\xE6\x8E\xA5\xE6\x88\x96\xE7\x9B\x91\xE5\x90\xAC\xE7\x9A\x84\x54\x43\x50\xE7\xAB\xAF\xE5\x8F\xA3\xEF\xBC\x9A\x06\x4C\x61\x79\x6F\x75\x74\x07\x08\x74\x6C\x43\x65\x6E\x74\x65\x72\x0B\x50\x61\x72\x65\x6E\x74\x43\x6F\x6C\x6F\x72\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x00\x06\x54\x4C\x61\x62\x65\x6C\x06\x4C\x61\x62\x65\x6C\x32\x04\x4C\x65\x66\x74\x02\x11\x06\x48\x65\x69\x67\x68\x74\x02\x16\x03\x54\x6F\x70\x02\x46\x05\x57\x69\x64\x74\x68\x03\xA2\x00\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x22\xE5\xAE\xA2\xE6\x88\xB7\xE7\xAB\xAF\xE8\xBD\xAC\xE5\x8F\x91\xE7\x9A\x84\x48\x54\x54\x50\x28\x53\x29\xE7\xAB\xAF\xE5\x8F\xA3\xEF\xBC\x9A\x06\x4C\x61\x79\x6F\x75\x74\x07\x08\x74\x6C\x43\x65\x6E\x74\x65\x72\x0B\x50\x61\x72\x65\x6E\x74\x43\x6F\x6C\x6F\x72\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x00\x06\x54\x4C\x61\x62\x65\x6C\x06\x4C\x61\x62\x65\x6C\x33\x04\x4C\x65\x66\x74\x02\x13\x06\x48\x65\x69\x67\x68\x74\x02\x16\x03\x54\x6F\x70\x03\x87\x00\x05\x57\x69\x64\x74\x68\x02\x46\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x0C\xE9\xAA\x8C\xE8\xAF\x81\x4B\x45\x59\xEF\xBC\x9A\x06\x4C\x61\x79\x6F\x75\x74\x07\x08\x74\x6C\x43\x65\x6E\x74\x65\x72\x0B\x50\x61\x72\x65\x6E\x74\x43\x6F\x6C\x6F\x72\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x00\x06\x54\x4C\x61\x62\x65\x6C\x06\x4C\x61\x62\x65\x6C\x37\x04\x4C\x65\x66\x74\x02\x13\x06\x48\x65\x69\x67\x68\x74\x02\x16\x03\x54\x6F\x70\x02\x66\x05\x57\x69\x64\x74\x68\x02\x47\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x12\xE6\x9C\x8D\xE5\x8A\xA1\xE5\x99\xA8\xE5\x9C\xB0\xE5\x9D\x80\xEF\xBC\x9A\x06\x4C\x61\x79\x6F\x75\x74\x07\x08\x74\x6C\x43\x65\x6E\x74\x65\x72\x0B\x50\x61\x72\x65\x6E\x74\x43\x6F\x6C\x6F\x72\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x00\x09\x54\x53\x70\x69\x6E\x45\x64\x69\x74\x0B\x53\x70\x69\x6E\x54\x43\x50\x50\x6F\x72\x74\x04\x4C\x65\x66\x74\x03\xCE\x00\x06\x48\x65\x69\x67\x68\x74\x02\x16\x04\x48\x69\x6E\x74\x06\x10\xE5\x8F\xAF\xE8\xBE\x93\xE5\x85\xA5\x31\x2D\x36\x35\x35\x33\x35\x03\x54\x6F\x70\x02\x06\x05\x57\x69\x64\x74\x68\x02\x4E\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x08\x4D\x61\x78\x56\x61\x6C\x75\x65\x04\xFF\xFF\x00\x00\x08\x4D\x69\x6E\x56\x61\x6C\x75\x65\x02\x01\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x00\x05\x56\x61\x6C\x75\x65\x03\x5D\x20\x00\x00\x09\x54\x53\x70\x69\x6E\x45\x64\x69\x74\x0F\x53\x70\x69\x6E\x43\x6C\x69\x48\x54\x54\x50\x50\x6F\x72\x74\x04\x4C\x65\x66\x74\x03\xCE\x00\x06\x48\x65\x69\x67\x68\x74\x02\x16\x04\x48\x69\x6E\x74\x06\x10\xE5\x8F\xAF\xE8\xBE\x93\xE5\x85\xA5\x31\x2D\x36\x35\x35\x33\x35\x03\x54\x6F\x70\x02\x46\x05\x57\x69\x64\x74\x68\x02\x4E\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x08\x4D\x61\x78\x56\x61\x6C\x75\x65\x04\xFF\xFF\x00\x00\x08\x4D\x69\x6E\x56\x61\x6C\x75\x65\x02\x01\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x01\x05\x56\x61\x6C\x75\x65\x03\x5E\x20\x00\x00\x09\x54\x43\x68\x65\x63\x6B\x42\x6F\x78\x08\x43\x68\x6B\x49\x73\x5A\x69\x70\x04\x4C\x65\x66\x74\x02\x13\x06\x48\x65\x69\x67\x68\x74\x02\x15\x03\x54\x6F\x70\x03\xAA\x00\x05\x57\x69\x64\x74\x68\x02\x58\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x0F\xE5\xBC\x80\xE5\x90\xAF\x5A\x49\x50\xE5\x8E\x8B\xE7\xBC\xA9\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x02\x00\x00\x05\x54\x45\x64\x69\x74\x0D\x45\x64\x69\x74\x56\x65\x72\x69\x66\x79\x4B\x65\x79\x04\x4C\x65\x66\x74\x02\x5C\x06\x48\x65\x69\x67\x68\x74\x02\x16\x03\x54\x6F\x70\x03\x87\x00\x05\x57\x69\x64\x74\x68\x03\x95\x00\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x03\x00\x00\x07\x54\x42\x75\x74\x74\x6F\x6E\x0A\x42\x74\x6E\x52\x61\x6E\x64\x4B\x65\x79\x04\x4C\x65\x66\x74\x03\xF2\x00\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x03\x86\x00\x05\x57\x69\x64\x74\x68\x02\x2B\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x06\xE9\x9A\x8F\xE6\x9C\xBA\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x04\x00\x00\x07\x54\x42\x75\x74\x74\x6F\x6E\x0A\x42\x74\x6E\x53\x61\x76\x65\x43\x66\x67\x04\x4C\x65\x66\x74\x03\x93\x01\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x02\x3E\x05\x57\x69\x64\x74\x68\x02\x4B\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x0C\xE4\xBF\x9D\xE5\xAD\x98\xE9\x85\x8D\xE7\xBD\xAE\x07\x4F\x6E\x43\x6C\x69\x63\x6B\x07\x0F\x42\x74\x6E\x53\x61\x76\x65\x43\x66\x67\x43\x6C\x69\x63\x6B\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x05\x00\x00\x07\x54\x42\x75\x74\x74\x6F\x6E\x0A\x42\x74\x6E\x4C\x6F\x61\x64\x43\x66\x67\x04\x4C\x65\x66\x74\x03\x93\x01\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x02\x06\x05\x57\x69\x64\x74\x68\x02\x4B\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x0C\xE8\xBD\xBD\xE5\x85\xA5\xE9\x85\x8D\xE7\xBD\xAE\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x06\x00\x00\x07\x54\x42\x75\x74\x74\x6F\x6E\x09\x42\x74\x6E\x4E\x65\x77\x43\x66\x67\x04\x4C\x65\x66\x74\x03\x93\x01\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x02\x22\x05\x57\x69\x64\x74\x68\x02\x4B\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x0C\xE6\x96\xB0\xE5\xBB\xBA\xE9\x85\x8D\xE7\xBD\xAE\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x07\x00\x00\x05\x54\x45\x64\x69\x74\x0B\x45\x64\x69\x74\x53\x76\x72\x41\x64\x64\x72\x04\x4C\x65\x66\x74\x02\x5C\x06\x48\x65\x69\x67\x68\x74\x02\x16\x03\x54\x6F\x70\x02\x66\x05\x57\x69\x64\x74\x68\x03\xC1\x00\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x08\x00\x00\x09\x54\x43\x68\x65\x63\x6B\x42\x6F\x78\x0A\x43\x68\x6B\x49\x73\x48\x74\x74\x70\x73\x04\x4C\x65\x66\x74\x02\x7C\x06\x48\x65\x69\x67\x68\x74\x02\x15\x03\x54\x6F\x70\x03\xAA\x00\x05\x57\x69\x64\x74\x68\x02\x70\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x12\xE7\x9B\x91\xE5\x90\xAC\x2F\xE8\xBD\xAC\xE5\x8F\x91\x48\x54\x54\x50\x53\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x09\x00\x00\x06\x54\x4C\x61\x62\x65\x6C\x06\x4C\x61\x62\x65\x6C\x38\x04\x4C\x65\x66\x74\x02\x11\x06\x48\x65\x69\x67\x68\x74\x02\x16\x03\x54\x6F\x70\x02\x26\x05\x57\x69\x64\x74\x68\x03\xA2\x00\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x22\xE6\x9C\x8D\xE5\x8A\xA1\xE7\xAB\xAF\xE7\x9B\x91\xE5\x90\xAC\xE7\x9A\x84\x48\x54\x54\x50\x28\x53\x29\xE7\xAB\xAF\xE5\x8F\xA3\xEF\xBC\x9A\x06\x4C\x61\x79\x6F\x75\x74\x07\x08\x74\x6C\x43\x65\x6E\x74\x65\x72\x0B\x50\x61\x72\x65\x6E\x74\x43\x6F\x6C\x6F\x72\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x00\x09\x54\x53\x70\x69\x6E\x45\x64\x69\x74\x0F\x53\x70\x69\x6E\x53\x76\x72\x48\x54\x54\x50\x50\x6F\x72\x74\x04\x4C\x65\x66\x74\x03\xCE\x00\x06\x48\x65\x69\x67\x68\x74\x02\x16\x04\x48\x69\x6E\x74\x06\x10\xE5\x8F\xAF\xE8\xBE\x93\xE5\x85\xA5\x31\x2D\x36\x35\x35\x33\x35\x03\x54\x6F\x70\x02\x26\x05\x57\x69\x64\x74\x68\x02\x4E\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x08\x4D\x61\x78\x56\x61\x6C\x75\x65\x04\xFF\xFF\x00\x00\x08\x4D\x69\x6E\x56\x61\x6C\x75\x65\x02\x01\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x0A\x05\x56\x61\x6C\x75\x65\x03\x5E\x20\x00\x00\x00\x09\x54\x47\x72\x6F\x75\x70\x42\x6F\x78\x05\x47\x42\x54\x4C\x53\x04\x4C\x65\x66\x74\x02\x00\x06\x48\x65\x69\x67\x68\x74\x03\xA6\x00\x03\x54\x6F\x70\x03\x4A\x01\x05\x57\x69\x64\x74\x68\x03\xEA\x01\x05\x41\x6C\x69\x67\x6E\x07\x08\x61\x6C\x42\x6F\x74\x74\x6F\x6D\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x0F\x54\x4C\x53\xE8\xAF\x81\xE4\xB9\xA6\xE8\xAE\xBE\xE7\xBD\xAE\x0C\x43\x6C\x69\x65\x6E\x74\x48\x65\x69\x67\x68\x74\x03\x90\x00\x0B\x43\x6C\x69\x65\x6E\x74\x57\x69\x64\x74\x68\x03\xE6\x01\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x02\x00\x06\x54\x50\x61\x6E\x65\x6C\x06\x50\x61\x6E\x65\x6C\x33\x04\x4C\x65\x66\x74\x02\x00\x06\x48\x65\x69\x67\x68\x74\x02\x20\x03\x54\x6F\x70\x02\x00\x05\x57\x69\x64\x74\x68\x03\xE6\x01\x05\x41\x6C\x69\x67\x6E\x07\x05\x61\x6C\x54\x6F\x70\x0A\x42\x65\x76\x65\x6C\x4F\x75\x74\x65\x72\x07\x06\x62\x76\x4E\x6F\x6E\x65\x0C\x43\x6C\x69\x65\x6E\x74\x48\x65\x69\x67\x68\x74\x02\x20\x0B\x43\x6C\x69\x65\x6E\x74\x57\x69\x64\x74\x68\x03\xE6\x01\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x00\x00\x06\x54\x4C\x61\x62\x65\x6C\x06\x4C\x61\x62\x65\x6C\x34\x04\x4C\x65\x66\x74\x02\x11\x06\x48\x65\x69\x67\x68\x74\x02\x16\x03\x54\x6F\x70\x02\x07\x05\x57\x69\x64\x74\x68\x02\x56\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x12\x54\x4C\x53\x20\x43\x41\xE6\xA0\xB9\xE8\xAF\x81\xE4\xB9\xA6\xEF\xBC\x9A\x06\x4C\x61\x79\x6F\x75\x74\x07\x08\x74\x6C\x43\x65\x6E\x74\x65\x72\x0B\x50\x61\x72\x65\x6E\x74\x43\x6F\x6C\x6F\x72\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x00\x05\x54\x45\x64\x69\x74\x0D\x45\x64\x69\x74\x54\x4C\x53\x43\x41\x46\x69\x6C\x65\x04\x4C\x65\x66\x74\x02\x70\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x02\x03\x05\x57\x69\x64\x74\x68\x03\x4A\x01\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x00\x00\x00\x07\x54\x42\x75\x74\x74\x6F\x6E\x09\x42\x74\x6E\x43\x41\x4F\x70\x65\x6E\x04\x4C\x65\x66\x74\x03\xBC\x01\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x02\x03\x05\x57\x69\x64\x74\x68\x02\x1B\x08\x42\x69\x64\x69\x4D\x6F\x64\x65\x07\x0D\x62\x64\x52\x69\x67\x68\x74\x54\x6F\x4C\x65\x66\x74\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x03\x2E\x2E\x2E\x0E\x50\x61\x72\x65\x6E\x74\x42\x69\x64\x69\x4D\x6F\x64\x65\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x01\x00\x00\x00\x0C\x54\x50\x61\x67\x65\x43\x6F\x6E\x74\x72\x6F\x6C\x0C\x50\x61\x67\x65\x43\x6F\x6E\x74\x72\x6F\x6C\x32\x04\x4C\x65\x66\x74\x02\x00\x06\x48\x65\x69\x67\x68\x74\x02\x70\x03\x54\x6F\x70\x02\x20\x05\x57\x69\x64\x74\x68\x03\xE6\x01\x0A\x41\x63\x74\x69\x76\x65\x50\x61\x67\x65\x07\x09\x54\x61\x62\x53\x68\x65\x65\x74\x33\x05\x41\x6C\x69\x67\x6E\x07\x08\x61\x6C\x43\x6C\x69\x65\x6E\x74\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x49\x6E\x64\x65\x78\x02\x00\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x01\x0B\x54\x61\x62\x50\x6F\x73\x69\x74\x69\x6F\x6E\x07\x08\x74\x70\x42\x6F\x74\x74\x6F\x6D\x00\x09\x54\x54\x61\x62\x53\x68\x65\x65\x74\x09\x54\x61\x62\x53\x68\x65\x65\x74\x33\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x0C\xE5\xAE\xA2\xE6\x88\xB7\xE7\xAB\xAF\x54\x4C\x53\x0C\x43\x6C\x69\x65\x6E\x74\x48\x65\x69\x67\x68\x74\x02\x52\x0B\x43\x6C\x69\x65\x6E\x74\x57\x69\x64\x74\x68\x03\xDE\x01\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x06\x54\x50\x61\x6E\x65\x6C\x06\x50\x61\x6E\x65\x6C\x34\x04\x4C\x65\x66\x74\x02\x00\x06\x48\x65\x69\x67\x68\x74\x02\x52\x03\x54\x6F\x70\x02\x00\x05\x57\x69\x64\x74\x68\x03\xDE\x01\x05\x41\x6C\x69\x67\x6E\x07\x08\x61\x6C\x43\x6C\x69\x65\x6E\x74\x0A\x42\x65\x76\x65\x6C\x4F\x75\x74\x65\x72\x07\x06\x62\x76\x4E\x6F\x6E\x65\x0C\x43\x6C\x69\x65\x6E\x74\x48\x65\x69\x67\x68\x74\x02\x52\x0B\x43\x6C\x69\x65\x6E\x74\x57\x69\x64\x74\x68\x03\xDE\x01\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x00\x00\x06\x54\x4C\x61\x62\x65\x6C\x06\x4C\x61\x62\x65\x6C\x36\x04\x4C\x65\x66\x74\x02\x0D\x06\x48\x65\x69\x67\x68\x74\x02\x16\x03\x54\x6F\x70\x02\x33\x05\x57\x69\x64\x74\x68\x02\x56\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x10\x54\x4C\x53\x20\x4B\x65\x79\xE6\x96\x87\xE4\xBB\xB6\xEF\xBC\x9A\x06\x4C\x61\x79\x6F\x75\x74\x07\x08\x74\x6C\x43\x65\x6E\x74\x65\x72\x0B\x50\x61\x72\x65\x6E\x74\x43\x6F\x6C\x6F\x72\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x00\x05\x54\x45\x64\x69\x74\x11\x45\x64\x69\x74\x54\x4C\x53\x43\x6C\x69\x4B\x65\x79\x46\x69\x6C\x65\x04\x4C\x65\x66\x74\x02\x6C\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x02\x31\x05\x57\x69\x64\x74\x68\x03\x4A\x01\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x00\x00\x00\x06\x54\x4C\x61\x62\x65\x6C\x06\x4C\x61\x62\x65\x6C\x35\x04\x4C\x65\x66\x74\x02\x0D\x06\x48\x65\x69\x67\x68\x74\x02\x16\x03\x54\x6F\x70\x02\x0F\x05\x57\x69\x64\x74\x68\x02\x51\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x11\x54\x4C\x53\x20\x43\x65\x72\x74\xE6\x96\x87\xE4\xBB\xB6\xEF\xBC\x9A\x06\x4C\x61\x79\x6F\x75\x74\x07\x08\x74\x6C\x43\x65\x6E\x74\x65\x72\x0B\x50\x61\x72\x65\x6E\x74\x43\x6F\x6C\x6F\x72\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x00\x05\x54\x45\x64\x69\x74\x12\x45\x64\x69\x74\x54\x4C\x53\x43\x6C\x69\x43\x65\x72\x74\x46\x69\x6C\x65\x04\x4C\x65\x66\x74\x02\x6C\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x02\x0D\x05\x57\x69\x64\x74\x68\x03\x4A\x01\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x01\x00\x00\x07\x54\x42\x75\x74\x74\x6F\x6E\x0E\x42\x74\x6E\x43\x6C\x69\x43\x65\x72\x74\x4F\x70\x65\x6E\x04\x4C\x65\x66\x74\x03\xB8\x01\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x02\x0D\x05\x57\x69\x64\x74\x68\x02\x1B\x08\x42\x69\x64\x69\x4D\x6F\x64\x65\x07\x0D\x62\x64\x52\x69\x67\x68\x74\x54\x6F\x4C\x65\x66\x74\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x03\x2E\x2E\x2E\x0E\x50\x61\x72\x65\x6E\x74\x42\x69\x64\x69\x4D\x6F\x64\x65\x08\x07\x4F\x6E\x43\x6C\x69\x63\x6B\x07\x13\x42\x74\x6E\x43\x6C\x69\x43\x65\x72\x74\x4F\x70\x65\x6E\x43\x6C\x69\x63\x6B\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x02\x00\x00\x07\x54\x42\x75\x74\x74\x6F\x6E\x0D\x42\x74\x6E\x43\x6C\x69\x4B\x65\x79\x4F\x70\x65\x6E\x04\x4C\x65\x66\x74\x03\xB8\x01\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x02\x31\x05\x57\x69\x64\x74\x68\x02\x1B\x08\x42\x69\x64\x69\x4D\x6F\x64\x65\x07\x0D\x62\x64\x52\x69\x67\x68\x74\x54\x6F\x4C\x65\x66\x74\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x03\x2E\x2E\x2E\x0E\x50\x61\x72\x65\x6E\x74\x42\x69\x64\x69\x4D\x6F\x64\x65\x08\x07\x4F\x6E\x43\x6C\x69\x63\x6B\x07\x12\x42\x74\x6E\x43\x6C\x69\x4B\x65\x79\x4F\x70\x65\x6E\x43\x6C\x69\x63\x6B\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x03\x00\x00\x00\x00\x09\x54\x54\x61\x62\x53\x68\x65\x65\x74\x09\x54\x61\x62\x53\x68\x65\x65\x74\x34\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x0C\xE6\x9C\x8D\xE5\x8A\xA1\xE7\xAB\xAF\x54\x4C\x53\x0C\x43\x6C\x69\x65\x6E\x74\x48\x65\x69\x67\x68\x74\x02\x52\x0B\x43\x6C\x69\x65\x6E\x74\x57\x69\x64\x74\x68\x03\xDE\x01\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x06\x54\x50\x61\x6E\x65\x6C\x06\x50\x61\x6E\x65\x6C\x35\x04\x4C\x65\x66\x74\x02\x00\x06\x48\x65\x69\x67\x68\x74\x02\x52\x03\x54\x6F\x70\x02\x00\x05\x57\x69\x64\x74\x68\x03\xDE\x01\x05\x41\x6C\x69\x67\x6E\x07\x08\x61\x6C\x43\x6C\x69\x65\x6E\x74\x0A\x42\x65\x76\x65\x6C\x4F\x75\x74\x65\x72\x07\x06\x62\x76\x4E\x6F\x6E\x65\x0C\x43\x6C\x69\x65\x6E\x74\x48\x65\x69\x67\x68\x74\x02\x52\x0B\x43\x6C\x69\x65\x6E\x74\x57\x69\x64\x74\x68\x03\xDE\x01\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x00\x00\x06\x54\x4C\x61\x62\x65\x6C\x07\x4C\x61\x62\x65\x6C\x31\x30\x04\x4C\x65\x66\x74\x02\x0D\x06\x48\x65\x69\x67\x68\x74\x02\x16\x03\x54\x6F\x70\x02\x0F\x05\x57\x69\x64\x74\x68\x02\x51\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x11\x54\x4C\x53\x20\x43\x65\x72\x74\xE6\x96\x87\xE4\xBB\xB6\xEF\xBC\x9A\x06\x4C\x61\x79\x6F\x75\x74\x07\x08\x74\x6C\x43\x65\x6E\x74\x65\x72\x0B\x50\x61\x72\x65\x6E\x74\x43\x6F\x6C\x6F\x72\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x00\x05\x54\x45\x64\x69\x74\x12\x45\x64\x69\x74\x54\x4C\x53\x53\x76\x72\x43\x65\x72\x74\x46\x69\x6C\x65\x04\x4C\x65\x66\x74\x02\x6C\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x02\x0D\x05\x57\x69\x64\x74\x68\x03\x4A\x01\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x00\x00\x00\x07\x54\x42\x75\x74\x74\x6F\x6E\x0E\x42\x74\x6E\x53\x76\x72\x43\x65\x72\x74\x4F\x70\x65\x6E\x04\x4C\x65\x66\x74\x03\xB8\x01\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x02\x0D\x05\x57\x69\x64\x74\x68\x02\x1B\x08\x42\x69\x64\x69\x4D\x6F\x64\x65\x07\x0D\x62\x64\x52\x69\x67\x68\x74\x54\x6F\x4C\x65\x66\x74\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x03\x2E\x2E\x2E\x0E\x50\x61\x72\x65\x6E\x74\x42\x69\x64\x69\x4D\x6F\x64\x65\x08\x07\x4F\x6E\x43\x6C\x69\x63\x6B\x07\x13\x42\x74\x6E\x53\x76\x72\x43\x65\x72\x74\x4F\x70\x65\x6E\x43\x6C\x69\x63\x6B\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x01\x00\x00\x07\x54\x42\x75\x74\x74\x6F\x6E\x0D\x42\x74\x6E\x53\x76\x72\x4B\x65\x79\x4F\x70\x65\x6E\x04\x4C\x65\x66\x74\x03\xB8\x01\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x02\x31\x05\x57\x69\x64\x74\x68\x02\x1B\x08\x42\x69\x64\x69\x4D\x6F\x64\x65\x07\x0D\x62\x64\x52\x69\x67\x68\x74\x54\x6F\x4C\x65\x66\x74\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x03\x2E\x2E\x2E\x0E\x50\x61\x72\x65\x6E\x74\x42\x69\x64\x69\x4D\x6F\x64\x65\x08\x07\x4F\x6E\x43\x6C\x69\x63\x6B\x07\x12\x42\x74\x6E\x53\x76\x72\x4B\x65\x79\x4F\x70\x65\x6E\x43\x6C\x69\x63\x6B\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x02\x00\x00\x05\x54\x45\x64\x69\x74\x11\x45\x64\x69\x74\x54\x4C\x53\x53\x76\x72\x4B\x65\x79\x46\x69\x6C\x65\x04\x4C\x65\x66\x74\x02\x6C\x06\x48\x65\x69\x67\x68\x74\x02\x19\x03\x54\x6F\x70\x02\x31\x05\x57\x69\x64\x74\x68\x03\x4A\x01\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x03\x00\x00\x06\x54\x4C\x61\x62\x65\x6C\x07\x4C\x61\x62\x65\x6C\x31\x31\x04\x4C\x65\x66\x74\x02\x0D\x06\x48\x65\x69\x67\x68\x74\x02\x16\x03\x54\x6F\x70\x02\x33\x05\x57\x69\x64\x74\x68\x02\x56\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x10\x54\x4C\x53\x20\x4B\x65\x79\xE6\x96\x87\xE4\xBB\xB6\xEF\xBC\x9A\x06\x4C\x61\x79\x6F\x75\x74\x07\x08\x74\x6C\x43\x65\x6E\x74\x65\x72\x0B\x50\x61\x72\x65\x6E\x74\x43\x6F\x6C\x6F\x72\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x00\x00\x00\x00\x00\x09\x54\x47\x72\x6F\x75\x70\x42\x6F\x78\x0D\x47\x42\x41\x70\x70\x53\x65\x74\x74\x69\x6E\x67\x73\x04\x4C\x65\x66\x74\x02\x00\x06\x48\x65\x69\x67\x68\x74\x02\x33\x03\x54\x6F\x70\x02\x3A\x05\x57\x69\x64\x74\x68\x03\xEA\x01\x05\x41\x6C\x69\x67\x6E\x07\x05\x61\x6C\x54\x6F\x70\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x09\x41\x50\x50\xE8\xAE\xBE\xE7\xBD\xAE\x0C\x43\x6C\x69\x65\x6E\x74\x48\x65\x69\x67\x68\x74\x02\x1D\x0B\x43\x6C\x69\x65\x6E\x74\x57\x69\x64\x74\x68\x03\xE6\x01\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x03\x00\x09\x54\x43\x68\x65\x63\x6B\x42\x6F\x78\x10\x43\x68\x6B\x41\x75\x74\x6F\x52\x65\x63\x6F\x6E\x6E\x65\x63\x74\x04\x4C\x65\x66\x74\x02\x13\x06\x48\x65\x69\x67\x68\x74\x02\x15\x03\x54\x6F\x70\x02\x03\x05\x57\x69\x64\x74\x68\x03\xB2\x00\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x27\xE4\xB8\x8E\xE6\x9C\x8D\xE5\x8A\xA1\xE5\x99\xA8\xE8\xBF\x9E\xE6\x8E\xA5\xE6\x96\xAD\xE5\xBC\x80\xE5\x90\x8E\xE8\x87\xAA\xE5\x8A\xA8\xE9\x87\x8D\xE8\xBF\x9E\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x00\x00\x00\x06\x54\x4C\x61\x62\x65\x6C\x06\x4C\x61\x62\x65\x6C\x39\x04\x4C\x65\x66\x74\x03\xF3\x00\x06\x48\x65\x69\x67\x68\x74\x02\x16\x03\x54\x6F\x70\x02\x00\x05\x57\x69\x64\x74\x68\x02\x5A\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x15\xE6\x9C\x80\xE5\xA4\xA7\xE6\x97\xA5\xE5\xBF\x97\xE8\xA1\x8C\xE6\x95\xB0\xEF\xBC\x9A\x06\x4C\x61\x79\x6F\x75\x74\x07\x08\x74\x6C\x43\x65\x6E\x74\x65\x72\x0B\x50\x61\x72\x65\x6E\x74\x43\x6F\x6C\x6F\x72\x08\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x00\x09\x54\x53\x70\x69\x6E\x45\x64\x69\x74\x0E\x53\x70\x69\x6E\x4D\x61\x78\x4C\x6F\x67\x4C\x69\x6E\x65\x04\x4C\x65\x66\x74\x03\x46\x01\x06\x48\x65\x69\x67\x68\x74\x02\x16\x03\x54\x6F\x70\x02\x00\x05\x57\x69\x64\x74\x68\x03\x86\x00\x08\x41\x75\x74\x6F\x53\x69\x7A\x65\x08\x08\x4D\x61\x78\x56\x61\x6C\x75\x65\x04\x40\x0D\x03\x00\x08\x4D\x69\x6E\x56\x61\x6C\x75\x65\x02\x01\x08\x4F\x6E\x43\x68\x61\x6E\x67\x65\x07\x14\x53\x70\x69\x6E\x4D\x61\x78\x4C\x6F\x67\x4C\x69\x6E\x65\x43\x68\x61\x6E\x67\x65\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x01\x05\x56\x61\x6C\x75\x65\x03\x88\x13\x00\x00\x00\x00\x09\x54\x54\x61\x62\x53\x68\x65\x65\x74\x09\x54\x61\x62\x53\x68\x65\x65\x74\x32\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x06\xE6\x97\xA5\xE5\xBF\x97\x0C\x43\x6C\x69\x65\x6E\x74\x48\x65\x69\x67\x68\x74\x03\x6F\x02\x0B\x43\x6C\x69\x65\x6E\x74\x57\x69\x64\x74\x68\x03\x66\x02\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x00\x08\x54\x4C\x69\x73\x74\x42\x6F\x78\x07\x4C\x73\x74\x4C\x6F\x67\x73\x04\x4C\x65\x66\x74\x02\x00\x06\x48\x65\x69\x67\x68\x74\x03\x6A\x02\x03\x54\x6F\x70\x02\x00\x05\x57\x69\x64\x74\x68\x03\x66\x02\x05\x41\x6C\x69\x67\x6E\x07\x08\x61\x6C\x43\x6C\x69\x65\x6E\x74\x14\x42\x6F\x72\x64\x65\x72\x53\x70\x61\x63\x69\x6E\x67\x2E\x42\x6F\x74\x74\x6F\x6D\x02\x04\x0B\x46\x6F\x6E\x74\x2E\x48\x65\x69\x67\x68\x74\x02\xF4\x0A\x49\x74\x65\x6D\x48\x65\x69\x67\x68\x74\x02\x00\x0A\x50\x61\x72\x65\x6E\x74\x46\x6F\x6E\x74\x08\x05\x53\x74\x79\x6C\x65\x07\x10\x6C\x62\x4F\x77\x6E\x65\x72\x44\x72\x61\x77\x46\x69\x78\x65\x64\x08\x54\x61\x62\x4F\x72\x64\x65\x72\x02\x00\x00\x00\x00\x00\x00\x0B\x54\x53\x61\x76\x65\x44\x69\x61\x6C\x6F\x67\x0A\x44\x6C\x67\x53\x61\x76\x65\x43\x66\x67\x0A\x44\x65\x66\x61\x75\x6C\x74\x45\x78\x74\x06\x04\x2E\x63\x66\x67\x06\x46\x69\x6C\x74\x65\x72\x06\x12\xE9\x85\x8D\xE7\xBD\xAE\xE6\x96\x87\xE4\xBB\xB6\x7C\x2A\x2E\x63\x66\x67\x04\x6C\x65\x66\x74\x03\x8D\x01\x03\x74\x6F\x70\x03\x13\x01\x00\x00\x0B\x54\x4F\x70\x65\x6E\x44\x69\x61\x6C\x6F\x67\x07\x44\x6C\x67\x4F\x70\x65\x6E\x04\x6C\x65\x66\x74\x03\xB3\x01\x03\x74\x6F\x70\x03\x13\x01\x00\x00\x0B\x54\x41\x63\x74\x69\x6F\x6E\x4C\x69\x73\x74\x0B\x41\x63\x74\x69\x6F\x6E\x4C\x69\x73\x74\x31\x04\x6C\x65\x66\x74\x03\x66\x01\x03\x74\x6F\x70\x03\x13\x01\x00\x07\x54\x41\x63\x74\x69\x6F\x6E\x08\x41\x63\x74\x53\x74\x61\x72\x74\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x06\xE5\x90\xAF\xE5\x8A\xA8\x00\x00\x07\x54\x41\x63\x74\x69\x6F\x6E\x07\x41\x63\x74\x53\x74\x6F\x70\x07\x43\x61\x70\x74\x69\x6F\x6E\x06\x06\xE5\x81\x9C\xE6\xAD\xA2\x00\x00\x00\x09\x54\x54\x72\x61\x79\x49\x63\x6F\x6E\x09\x54\x72\x61\x79\x49\x63\x6F\x6E\x31\x07\x56\x69\x73\x69\x62\x6C\x65\x09\x04\x6C\x65\x66\x74\x03\x66\x01\x03\x74\x6F\x70\x03\xE6\x00\x00\x00\x00")
91 |
92 | // 注册Form资源
93 | var _ = vcl.RegisterFormResource(MainForm, &mainFormBytes)
94 |
--------------------------------------------------------------------------------
/MainFormImpl.go:
--------------------------------------------------------------------------------
1 | // +build gui
2 |
3 | package main
4 |
5 | import (
6 | "fmt"
7 | "math/rand"
8 | "runtime"
9 | "strings"
10 | "time"
11 |
12 | "errors"
13 |
14 | _ "github.com/ying32/govcl/pkgs/winappres"
15 | "github.com/ying32/govcl/vcl"
16 | "github.com/ying32/govcl/vcl/rtl"
17 | "github.com/ying32/govcl/vcl/types"
18 | "github.com/ying32/govcl/vcl/types/colors"
19 | "github.com/ying32/rproxy/librp"
20 | )
21 |
22 | const (
23 | randStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
24 | SERVER = 1
25 | CLIENT = 0
26 | )
27 |
28 | var CheckConfigError = errors.New("检查配置错误")
29 |
30 | //::private::
31 | type TMainFormFields struct {
32 | rpObj librp.IRPObject
33 | rpConfigFileName string
34 | running bool
35 | autoReboot bool
36 | appCfg *vcl.TIniFile
37 | modeIndex int32
38 | //isDarwin bool
39 | rpConfigLoaded bool
40 | }
41 |
42 | func (f *TMainForm) OnFormCreate(sender vcl.IObject) {
43 | rand.Seed(time.Now().Unix())
44 | //f.isDarwin = runtime.GOOS == "darwin"
45 |
46 | // 都是坑
47 | if runtime.GOOS == "linux" {
48 | f.SpinMaxLogLine.SetAutoSize(true)
49 | f.SpinCliHTTPPort.SetAutoSize(true)
50 | f.SpinSvrHTTPPort.SetAutoSize(true)
51 | f.SpinTCPPort.SetAutoSize(true)
52 | }
53 |
54 | f.ScreenCenter()
55 | f.PageControl1.SetActivePageIndex(0)
56 | f.PageControl2.SetActivePageIndex(0)
57 |
58 | f.running = false
59 | f.appCfg = vcl.NewIniFile(rtl.ExtractFilePath(vcl.Application.ExeName()) + "app.conf")
60 |
61 | if runtime.GOOS != "windows" {
62 | loadMainIconFromStream(f.TrayIcon1.Icon())
63 | }
64 |
65 | librp.IsGUI = true
66 | librp.LogGUICallback = f.logCallback
67 | f.loadAppConfig()
68 |
69 | }
70 |
71 | func (f *TMainForm) OnFormCloseQuery(sender vcl.IObject, canClose *bool) {
72 | if f.running {
73 | vcl.MessageDlg("当前服务正在运行中,请停止后再退出。", types.MtWarning, types.MbOK)
74 | *canClose = false
75 | }
76 | }
77 |
78 | func loadMainIconFromStream(outIcon *vcl.TIcon) {
79 | if outIcon.IsValid() {
80 | mem := vcl.NewMemoryStreamFromBytes(mainIconBytes)
81 | defer mem.Free()
82 | mem.SetPosition(0)
83 | outIcon.LoadFromStream(mem)
84 | }
85 | }
86 |
87 | func (f *TMainForm) OnFormDestroy(sender vcl.IObject) {
88 | if f.appCfg != nil {
89 | f.appCfg.Free()
90 | }
91 | if f.rpObj != nil {
92 | f.rpObj.Close()
93 | }
94 | }
95 |
96 | func (f *TMainForm) loadAppConfig() {
97 | f.rpConfigFileName = f.appCfg.ReadString("System", "RPConfigFileName", "")
98 | cfg := new(librp.TRProxyConfig)
99 | err := librp.LoadConfig(f.rpConfigFileName, cfg)
100 | if err == nil {
101 | librp.SetConfig(cfg)
102 | f.updateUIConfig(cfg)
103 | f.rpConfigLoaded = true
104 | librp.Log.I("已加载RP配置:" + f.rpConfigFileName)
105 | }
106 | f.autoReboot = f.appCfg.ReadBool("System", "AutoReboot", true)
107 | f.ChkAutoReconnect.SetChecked(f.autoReboot)
108 |
109 | f.modeIndex = f.appCfg.ReadInteger("System", "ModeIndex", 0)
110 | f.setRPMode(f.modeIndex)
111 |
112 | f.SpinMaxLogLine.SetValue(f.appCfg.ReadInteger("System", "MaxLogLine", 5000))
113 |
114 | f.updateCaption()
115 | }
116 |
117 | func (f *TMainForm) logCallback(msg string) {
118 | vcl.ThreadSync(func() {
119 | if f.LstLogs.Count() > f.SpinMaxLogLine.Value() {
120 | f.LstLogs.Clear()
121 | }
122 | f.LstLogs.Items().Add(msg)
123 | })
124 | }
125 |
126 | // 下面两个函数用于解决TRadioGroup在MacOS下bug问题
127 | func (f *TMainForm) getRPMode() int32 {
128 | return f.RGMode.ItemIndex()
129 | }
130 |
131 | func (f *TMainForm) setRPMode(idx int32) {
132 | f.RGMode.SetItemIndex(idx)
133 | }
134 |
135 | func (f *TMainForm) setTrayHint(text string) {
136 | //if f.isDarwin {
137 | // // darwin下出bug
138 | // return
139 | //}
140 | f.TrayIcon1.SetHint(text)
141 | }
142 |
143 | func (f *TMainForm) OnBtnRandKeyClick(sender vcl.IObject) {
144 | var randKey []byte
145 | for i := 0; i < 16; i++ {
146 | randKey = append(randKey, randStr[rand.Intn(len(randStr))])
147 | }
148 | f.EditVerifyKey.SetText(string(randKey))
149 | }
150 |
151 | func (f *TMainForm) OnBtnCAOpenClick(sender vcl.IObject) {
152 | if f.DlgOpen.Execute() {
153 | f.EditTLSCAFile.SetText(f.DlgOpen.FileName())
154 | }
155 | }
156 |
157 | func (f *TMainForm) OnBtnSaveCfgClick(sender vcl.IObject) {
158 | f.saveUIConfig()
159 | }
160 |
161 | func (f *TMainForm) updateUIConfig(cfg *librp.TRProxyConfig) {
162 | f.SpinTCPPort.SetValue(int32(cfg.TCPPort))
163 | f.EditVerifyKey.SetText(cfg.VerifyKey)
164 | f.ChkIsZip.SetChecked(cfg.IsZIP)
165 | f.ChkIsHttps.SetChecked(cfg.IsHTTPS)
166 | f.EditTLSCAFile.SetText(cfg.TLSCAFile)
167 |
168 | // client
169 | f.SpinCliHTTPPort.SetValue(int32(cfg.Client.HTTPPort))
170 | f.EditSvrAddr.SetText(cfg.Client.SvrAddr)
171 | f.EditTLSCliCertFile.SetText(cfg.Client.TLSCertFile)
172 | f.EditTLSCliKeyFile.SetText(cfg.Client.TLSKeyFile)
173 |
174 | // server
175 | f.SpinSvrHTTPPort.SetValue(int32(cfg.Server.HTTPPort))
176 | f.EditTLSSvrCertFile.SetText(cfg.Server.TLSCertFile)
177 | f.EditTLSSvrKeyFile.SetText(cfg.Server.TLSKeyFile)
178 | }
179 |
180 | func (f *TMainForm) checkConfig() error {
181 | if f.EditVerifyKey.Text() == "" {
182 | f.EditVerifyKey.SetFocus()
183 | vcl.ShowMessage("必须输入一个验证的key")
184 | return CheckConfigError
185 | }
186 | if f.EditSvrAddr.Text() == "" {
187 | f.EditSvrAddr.SetFocus()
188 | vcl.ShowMessage("要连接的服务器地址不能为空")
189 | return CheckConfigError
190 | }
191 | if f.ChkIsHttps.Checked() {
192 |
193 | if f.EditTLSCliCertFile.Text() == "" || f.EditTLSCliKeyFile.Text() == "" {
194 | vcl.ShowMessage("当为HTTPS时,客户端TLS证书不能为空。")
195 | return CheckConfigError
196 | }
197 |
198 | if f.EditTLSSvrCertFile.Text() == "" || f.EditTLSSvrKeyFile.Text() == "" {
199 | vcl.ShowMessage("当为HTTPS时,服务端TLS证书不能为空。")
200 | return CheckConfigError
201 | }
202 | }
203 | return nil
204 | }
205 |
206 | func (f *TMainForm) saveUIConfig() {
207 | if f.checkConfig() != nil {
208 | return
209 | }
210 | cfg := new(librp.TRProxyConfig)
211 |
212 | cfg.TCPPort = int(f.SpinTCPPort.Value())
213 | cfg.VerifyKey = f.EditVerifyKey.Text()
214 | cfg.IsZIP = f.ChkIsZip.Checked()
215 | cfg.IsHTTPS = f.ChkIsHttps.Checked()
216 | cfg.TLSCAFile = f.EditTLSCAFile.Text()
217 |
218 | // client
219 | cfg.Client.HTTPPort = int(f.SpinCliHTTPPort.Value())
220 | cfg.Client.SvrAddr = f.EditSvrAddr.Text()
221 | cfg.Client.TLSCertFile = f.EditTLSCliCertFile.Text()
222 | cfg.Client.TLSKeyFile = f.EditTLSCliKeyFile.Text()
223 |
224 | // server
225 | cfg.Server.HTTPPort = int(f.SpinSvrHTTPPort.Value())
226 | cfg.Server.TLSCertFile = f.EditTLSSvrCertFile.Text()
227 | cfg.Server.TLSKeyFile = f.EditTLSSvrKeyFile.Text()
228 |
229 | if !rtl.FileExists(f.rpConfigFileName) {
230 | if f.DlgSaveCfg.Execute() {
231 | f.rpConfigFileName = f.DlgSaveCfg.FileName()
232 | } else {
233 | librp.Log.I("取消保存RP配置")
234 | return
235 | }
236 | }
237 | librp.SetConfig(cfg)
238 | err := librp.SaveConfig(f.rpConfigFileName, cfg)
239 | if err != nil {
240 | librp.Log.I("P配置保存失败")
241 | return
242 | }
243 | f.rpConfigLoaded = true
244 | librp.Log.I("RP配置已保存:" + f.rpConfigFileName)
245 | }
246 |
247 | func (f *TMainForm) OnBtnLoadCfgClick(sender vcl.IObject) {
248 | if f.DlgOpen.Execute() {
249 | cfg := new(librp.TRProxyConfig)
250 | err := librp.LoadConfig(f.DlgOpen.FileName(), cfg)
251 | if err != nil {
252 | vcl.ShowMessage("载入RP配置失败:" + err.Error())
253 | } else {
254 | librp.SetConfig(cfg)
255 | f.rpConfigFileName = f.DlgOpen.FileName()
256 | f.updateUIConfig(cfg)
257 | // 载入即保存下当前的文件名
258 | f.appCfg.WriteString("System", "RPConfigFileName", f.rpConfigFileName)
259 | f.rpConfigLoaded = true
260 | librp.Log.I("已加载RP配置:" + f.rpConfigFileName)
261 | }
262 | }
263 | }
264 |
265 | func (f *TMainForm) OnChkAutoReconnectClick(sender vcl.IObject) {
266 | f.autoReboot = f.ChkAutoReconnect.Checked()
267 | f.appCfg.WriteBool("System", "AutoReboot", f.autoReboot)
268 | }
269 |
270 | func (f *TMainForm) runSvr() {
271 |
272 | switch f.modeIndex {
273 | case CLIENT:
274 | librp.Log.I("连接服务端...")
275 | for f.running {
276 | err := f.rpObj.Start()
277 | if err != nil {
278 | librp.Log.E("连接服务器错误:", err)
279 | librp.Log.I("5秒后重新连接...")
280 | for i := 0; i < 5; i++ {
281 | if !f.running {
282 | break
283 | }
284 | time.Sleep(time.Second * 1)
285 | }
286 | if !f.autoReboot {
287 | break
288 | }
289 | }
290 | }
291 | case SERVER:
292 | if f.running {
293 | err := f.rpObj.Start()
294 | if err != nil {
295 | librp.Log.E("启动服务端错误:", err)
296 | }
297 | }
298 | }
299 | librp.Log.I("已停止")
300 | vcl.ThreadSync(func() {
301 | f.BtnStop.Click()
302 | })
303 | }
304 |
305 | func (f *TMainForm) OnActStartExecute(sender vcl.IObject) {
306 |
307 | switch f.modeIndex {
308 | case CLIENT:
309 | f.rpObj = librp.NewRPClient()
310 | case SERVER:
311 | f.rpObj = librp.NewRPServer()
312 | }
313 | if f.rpObj == nil {
314 | vcl.ShowMessage("rproxy对象创建失败。")
315 | return
316 | }
317 |
318 | f.running = true
319 |
320 | f.LstLogs.Clear()
321 |
322 | str := f.Caption() + "\r\n"
323 | switch f.modeIndex {
324 | case CLIENT:
325 | s := fmt.Sprintln("客户端启动,连接服务器:", f.EditSvrAddr.Text(), ", 端口:", f.SpinTCPPort.Value())
326 | str += s
327 | librp.Log.I(s)
328 | if f.ChkIsHttps.Checked() {
329 | s = "转发至HTTP服务为HTTPS"
330 | str += s + "\r\n"
331 | librp.Log.I(s)
332 | }
333 | s = fmt.Sprintln("转发至本地HTTP(S)端口:", f.SpinCliHTTPPort.Value())
334 | str += s
335 | librp.Log.I(s)
336 |
337 | case SERVER:
338 | s := fmt.Sprintln("TCP服务端已启动,端口:", f.SpinTCPPort.Value())
339 | str += s
340 | librp.Log.I(s)
341 | if f.ChkIsHttps.Checked() {
342 | s = "当前HTTP服务为HTTPS"
343 | str += s + "\r\n"
344 | librp.Log.I(s)
345 | }
346 | s = fmt.Sprintln("HTTP(S)服务端已开启,端口:", f.SpinSvrHTTPPort.Value())
347 | str += s
348 | librp.Log.I(s)
349 | }
350 | f.setTrayHint(str)
351 |
352 | go f.runSvr()
353 |
354 | f.setControlState(false)
355 | }
356 |
357 | func (f *TMainForm) setControlState(state bool) {
358 | f.ChkIsHttps.SetEnabled(state)
359 | f.RGMode.SetEnabled(state)
360 |
361 | var i int32
362 | for i = 0; i < f.GBBase.ControlCount(); i++ {
363 | f.GBBase.Controls(i).SetEnabled(state)
364 | }
365 | for i = 0; i < f.GBTLS.ControlCount(); i++ {
366 | f.GBTLS.Controls(i).SetEnabled(state)
367 | }
368 | for i = 0; i < f.GBAppSettings.ControlCount(); i++ {
369 | f.GBAppSettings.Controls(i).SetEnabled(state)
370 | }
371 | }
372 |
373 | func (f *TMainForm) OnActStartUpdate(sender vcl.IObject) {
374 | vcl.AsAction(sender).SetEnabled(!f.running && f.rpConfigLoaded)
375 | }
376 |
377 | func (f *TMainForm) OnActStopExecute(sender vcl.IObject) {
378 | f.running = false
379 | f.setControlState(true)
380 |
381 | if f.rpObj != nil {
382 | f.rpObj.Close()
383 | }
384 | f.rpObj = nil
385 | f.updateCaption()
386 | }
387 |
388 | func (f *TMainForm) OnActStopUpdate(sender vcl.IObject) {
389 | vcl.AsAction(sender).SetEnabled(f.running && f.rpConfigLoaded)
390 | }
391 |
392 | func (f *TMainForm) OnBtnNewCfgClick(sender vcl.IObject) {
393 | librp.Log.I("新建配置")
394 | f.rpConfigFileName = ""
395 | f.rpConfigLoaded = false
396 | }
397 |
398 | func (f *TMainForm) updateCaption() {
399 | //if f.isDarwin {
400 | // // 唉macOS下liblcl.dylib有内部bug
401 | // m := []string{"客户端", "服务端"}
402 | // f.SetCaption("rproxy[" + m[f.modeIndex] + "]")
403 | //} else {
404 | f.SetCaption("rproxy[" + f.RGMode.Items().S(f.modeIndex) + "]")
405 | //}
406 | f.setTrayHint(f.Caption())
407 | }
408 |
409 | func (f *TMainForm) OnRGModeClick(sender vcl.IObject) {
410 | f.modeIndex = f.getRPMode()
411 | f.appCfg.WriteInteger("System", "ModeIndex", f.modeIndex)
412 | f.updateCaption()
413 | }
414 |
415 | func (f *TMainForm) OnLstLogsDrawItem(control vcl.IWinControl, index int32, aRect types.TRect, state types.TOwnerDrawState) {
416 | canvas := f.LstLogs.Canvas()
417 |
418 | canvas.Font().SetColor(colors.ClBlack)
419 | s := f.LstLogs.Items().S(index)
420 | if strings.HasPrefix(s, "[ERROR]") {
421 | canvas.Font().SetColor(colors.ClRed)
422 | } else if strings.HasPrefix(s, "[DEBUG]") {
423 | canvas.Font().SetColor(colors.ClGreen)
424 | } else if strings.HasPrefix(s, "[WARNING]") {
425 | canvas.Font().SetColor(colors.ClYellow)
426 | }
427 | canvas.Brush().SetColor(colors.ClWhite)
428 | canvas.FillRect(aRect)
429 | if state.In(types.OdSelected) {
430 | canvas.Font().SetColor(colors.ClBlue)
431 | }
432 | canvas.TextOut(aRect.Left, aRect.Top, s)
433 | }
434 |
435 | func (f *TMainForm) OnBtnCliCertOpenClick(sender vcl.IObject) {
436 | if f.DlgOpen.Execute() {
437 | f.EditTLSCliCertFile.SetText(f.DlgOpen.FileName())
438 | }
439 | }
440 |
441 | func (f *TMainForm) OnBtnCliKeyOpenClick(sender vcl.IObject) {
442 | if f.DlgOpen.Execute() {
443 | f.EditTLSCliKeyFile.SetText(f.DlgOpen.FileName())
444 | }
445 | }
446 |
447 | func (f *TMainForm) OnBtnSvrCertOpenClick(sender vcl.IObject) {
448 | if f.DlgOpen.Execute() {
449 | f.EditTLSSvrCertFile.SetText(f.DlgOpen.FileName())
450 | }
451 | }
452 |
453 | func (f *TMainForm) OnBtnSvrKeyOpenClick(sender vcl.IObject) {
454 | if f.DlgOpen.Execute() {
455 | f.EditTLSSvrKeyFile.SetText(f.DlgOpen.FileName())
456 | }
457 | }
458 |
459 | func (f *TMainForm) OnSpinMaxLogLineChange(sender vcl.IObject) {
460 | f.appCfg.WriteInteger("System", "MaxLogLine", f.SpinMaxLogLine.Value())
461 | }
462 |
463 | func (f *TMainForm) OnPageControl1Change(sender vcl.IObject) {
464 |
465 | }
466 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # rproxy
2 | ## 简单的反向代理用于内网穿透
3 |
4 | **特别注意,此工具只适合小文件类的访问测试,用来做做数据调试。当初也只是用于微信公众号开发,所以定位也是如此(注:目前代码中限制一个包最大为4M)**
5 |
6 | ### 前言
7 |
8 | 最近周末闲来无事,想起了做下微信公共号的开发,但微信限制只能80端口的,自己用的城中村的那种宽带,共用一个公网,没办法自己用路由做端口映射。自己的服务器在腾讯云上,每次都要编译完后用ftp上传再进行调试,非常的浪费时间。 一时间又不知道上哪找一个符合我的这种要求的工具,就索性自己构思了下,整个工作流程大致为:
9 |
10 | ### 工作原理
11 |
12 | 外部请求自己服务器上的HTTP服务端 -> 将数据传递给Socket服务器 -> Socket服务器将数据发送至已连接的Socket客户端 -> Socket客户端收到数据 -> 使用http请求本地http服务端 -> 本地http服务端处理相关后返回 -> Socket客户端将返回的数据发送至Socket服务端 -> Socket服务端解析出数据后原路返回至外部请求的HTTP
13 |
14 |
15 | ### 使用方法
16 |
17 | > 1、go get github.com/ying32/rproxy
18 | > 2、使用[构建命令](build)
19 | > 3、参照[命令行用例](#命令行用例)使用,或者使用[带GUI的客户端](#带GUI的客户端)
20 |
21 |
22 | ### 命令行说明
23 | ```bash
24 | --tcpport # Socket连接或者监听的端口。
25 | --httpport # 当mode为server时为服务端监听端口,当为mode为client时为转发至本地客户端的端口。
26 | --mode # 启动模式,可选为client、server,默认为client。
27 | --svraddr # 当mode为client时有效,为连接服务器的地址,不需要填写端口。
28 | --vkey # 客户端与服务端建立连接时校验的加密key,简单的。
29 | ### 以下参数为v0.6版本及之后的
30 | --ishttps # httpPort端口是否只用作HTTPS监听,默认为false。
31 | --tlscafile # 当ishttps为true时,所需的CA根证书文件。可为空,根据实际情况确定。
32 | --tlscertfile # 当ishttps为true时,所需求的TLS证书文件。
33 | --tlskeyfile # 当ishttps为true时,所需求的TLS密匙文件。
34 | --iszip # 是否开启zip压缩
35 | --cfgfile # 使用指定的配置文件中的参数,此时只有mode参数有效
36 | ```
37 |
38 | ### 用例
39 |
40 | #### 命令行用例
41 | * HTTP:
42 | ```bash
43 | ## ---- 从命令行加载主要参数 ----
44 | # 服务端
45 | rproxy --tcpport=8285 --httpport=8286 --mode="server" --vkey="DKibZF5TXvic1g3kY"
46 |
47 | # 客户端
48 | rproxy --tcpport=8285 --httpport=8080 --svraddr="127.0.0.1" --vkey="DKibZF5TXvic1g3kY"
49 |
50 | ## ---- 从配置文件加载主要参数 ----
51 | # 服务端
52 | rproxy --mode="server" --cfgfile="./conf/config.cfg"
53 |
54 | # 客户端
55 | rproxy --cfgfile="./conf/config.cfg"
56 | ```
57 |
58 | * HTTPS
59 | ```bash
60 | ## ---- 从命令行加载主要参数 ----
61 | # 服务端
62 | rproxy --tcpport=8285 --httpport=8286 --mode="server" --ishttps=true --tlscafile="./cert/ca.pem" --tlscertfile="./cert/server.pem" --tlskeyfile="./cert/server.key" --vkey="DKibZF5TXvic1g3kY"
63 |
64 | # 客户端
65 | rproxy --tcpport=8285 --httpport=8089 --svraddr="127.0.0.1" --ishttps=true --tlscafile="./cert/ca.pem" --tlscertfile="./cert/client.pem" --tlskeyfile="./cert/client.key" --vkey="DKibZF5TXvic1g3kY"
66 |
67 | ## ---- 从配置文件加载主要参数 ----
68 | # 服务端
69 | rproxy --mode="server" --cfgfile="./conf/confighttps.cfg"
70 |
71 | # 客户端
72 | rproxy --cfgfile="./conf/confighttps.cfg"
73 | ```
74 |
75 | #### 带GUI的客户端
76 | [查看截图](imgs)
77 |
78 | ### 操作系统支持
79 |
80 | 支持Windows、Linux、MacOSX等,无第三方依赖库。
81 |
82 | ### 二进制下载
83 | * v0.6带GUI https://github.com/ying32/rproxy/releases/tag/v0.6
84 | * v0.5 https://github.com/ying32/rproxy/releases/tag/v0.5
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/Sample/sample.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "crypto/tls"
5 | "crypto/x509"
6 | "fmt"
7 | "io/ioutil"
8 | "net/http"
9 | )
10 |
11 | func main() {
12 |
13 | //http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
14 | // fmt.Println("服务端请求数据了!")
15 | // fmt.Println(r)
16 | // w.Write([]byte("请求成功!"))
17 | //})
18 | fs := http.FileServer(http.Dir("C:\\"))
19 | http.Handle("/", http.StripPrefix("/", fs))
20 |
21 | go func() {
22 | fmt.Println("监听HTTPS 8089端口中...")
23 | certPool := x509.NewCertPool()
24 | caCertFileName := "../cert/ca.pem"
25 | caCrt, err := ioutil.ReadFile(caCertFileName)
26 | if err != nil {
27 | fmt.Println("读TLS根证书错误", err)
28 | return
29 | }
30 | ok := certPool.AppendCertsFromPEM(caCrt)
31 | if !ok {
32 | fmt.Println("CA根证书添加失败。")
33 | return
34 | }
35 | httpsSvr := &http.Server{
36 | Addr: ":8089",
37 | Handler: nil,
38 | TLSConfig: &tls.Config{
39 | ClientCAs: certPool,
40 | //ClientAuth: tls.RequireAndVerifyClientCert,
41 | },
42 | }
43 | err = httpsSvr.ListenAndServeTLS("../cert/server.pem", "../cert/server.key")
44 | if err != nil {
45 | fmt.Println("开启HTTPS服务端失败:", err)
46 | }
47 | }()
48 |
49 | fmt.Println("监听HTTP 8085端口中...")
50 | if err := http.ListenAndServe(":8085", nil); err != nil {
51 | fmt.Println("开启HTTP服血端失败:", err)
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/build/README.md:
--------------------------------------------------------------------------------
1 | ### 构建rproxy命令
2 |
3 | 如果要编译带界面的客户端需要加上 -tags gui 如下面的
4 | go build -i -ldflags="-H windowsgui" -tags gui -o rproxy_GUI.exe
5 |
6 | 构建带UI的客户端/服务端需要用到https://github.com/ying32/govcl 这个库。
7 |
8 | 自签HTTPS证书可使用go标准库中已经有的工具
9 | go\src\crypto\tls\generate_cert.go
10 |
11 | 最后面下面下载对应的govcl二进制:
12 | https://github.com/ying32/govcl/releases/download/v1.2.2/Librarys-1.2.2.zip
13 |
14 | 注:现rproxy中使用的二进制为govcl dev分支中的代码,相应的二进制需要自己编译。
--------------------------------------------------------------------------------
/build/build-darwin32-GUI.sh:
--------------------------------------------------------------------------------
1 | echo Compiling 32-bit rproxy GUI...
2 |
3 | # 根据你的实际情况修改这里
4 | export GOROOT=$HOME/godev/go
5 | export GOPATH=$HOME/godev/gosrc
6 | export PATH=$GOROOT/bin:$PATH
7 |
8 | export GOARCH=386
9 | export GOOS=darwin
10 | export CGO_ENABLED=1
11 | cd ../
12 | go build -tags gui -o rproxy_GUI
--------------------------------------------------------------------------------
/build/build-darwin32.sh:
--------------------------------------------------------------------------------
1 | echo Compiling 32-bit rproxy...
2 |
3 | # 根据你的实际情况修改这里
4 | export GOROOT=$HOME/godev/go
5 | export GOPATH=$HOME/godev/gosrc
6 | export PATH=$GOROOT/bin:$PATH
7 |
8 | export GOARCH=386
9 | export GOOS=darwin
10 | export CGO_ENABLED=0
11 | cd ../
12 | go build -o rproxy
--------------------------------------------------------------------------------
/build/build-darwin64-GUI.sh:
--------------------------------------------------------------------------------
1 | echo Compiling 64-bit rproxy GUI...
2 |
3 | # 根据你的实际情况修改这里
4 | export GOROOT=$HOME/godev/go
5 | export GOPATH=$HOME/godev/gosrc
6 | export PATH=$GOROOT/bin:$PATH
7 |
8 | export GOARCH=amd64
9 | export GOOS=darwin
10 | export CGO_ENABLED=1
11 | cd ../
12 | go build -tags gui -o rproxy_GUI
--------------------------------------------------------------------------------
/build/build-darwin64.sh:
--------------------------------------------------------------------------------
1 | echo Compiling 64-bit rproxy...
2 |
3 | # 根据你的实际情况修改这里
4 | export GOROOT=$HOME/godev/go
5 | export GOPATH=$HOME/godev/gosrc
6 | export PATH=$GOROOT/bin:$PATH
7 |
8 | export GOARCH=amd64
9 | export GOOS=darwin
10 | export CGO_ENABLED=0
11 | cd ../
12 | go build -o rproxy
--------------------------------------------------------------------------------
/build/build-linux32-GUI.sh:
--------------------------------------------------------------------------------
1 | echo Compiling 32-bit rproxy...
2 |
3 | # 根据你的实际情况修改这里
4 | export GOROOT=$HOME/go
5 | export PATH=$GOROOT/bin:$PATH
6 |
7 | export GOARCH=386
8 | export GOOS=linux
9 | export CGO_ENABLED=1
10 | cd ../
11 | go build -tags gui -o rproxy_GUI
--------------------------------------------------------------------------------
/build/build-linux32.sh:
--------------------------------------------------------------------------------
1 | echo Compiling 32-bit rproxy...
2 |
3 | # 根据你的实际情况修改这里
4 | export GOROOT=$HOME/go
5 | export PATH=$GOROOT/bin:$PATH
6 |
7 | export GOARCH=386
8 | export GOOS=linux
9 | export CGO_ENABLED=0
10 | cd ../
11 | go build -o rproxy
--------------------------------------------------------------------------------
/build/build-linux64-GUI.sh:
--------------------------------------------------------------------------------
1 | echo Compiling 64-bit rproxy GUI...
2 |
3 | # 根据你的实际情况修改这里
4 | export GOROOT=$HOME/go
5 | export PATH=$GOROOT/bin:$PATH
6 |
7 | export GOARCH=amd64
8 | export GOOS=linux
9 | export CGO_ENABLED=1
10 | cd ../
11 | go build -tags gui -o rproxy_GUI
--------------------------------------------------------------------------------
/build/build-linux64.sh:
--------------------------------------------------------------------------------
1 | echo Compiling 64-bit rproxy...
2 |
3 | # 根据你的实际情况修改这里
4 | export GOROOT=$HOME/go
5 | export PATH=$GOROOT/bin:$PATH
6 |
7 | export GOARCH=amd64
8 | export GOOS=linux
9 | export CGO_ENABLED=0
10 | cd ../
11 | go build -o rproxy
--------------------------------------------------------------------------------
/build/build-win32-GUI.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | echo Compiling 32-bit rproxy GUI...
3 |
4 | set GOARCH=386
5 | set GOOS=windows
6 | set CGO_ENABLED=0
7 | cd ../
8 | go build -i -ldflags="-H windowsgui" -tags gui -o rproxy_GUI.exe
9 | pause
--------------------------------------------------------------------------------
/build/build-win32.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | echo Compiling 32-bit rproxy...
3 |
4 | set GOARCH=386
5 | set GOOS=windows
6 | set CGO_ENABLED=0
7 | cd ../
8 | go build -o rproxy.exe
9 | pause
--------------------------------------------------------------------------------
/build/build-win64-GUI.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | echo Compiling 64-bit rproxy GUI...
3 |
4 | set GOARCH=amd64
5 | set GOOS=windows
6 | set CGO_ENABLED=0
7 | cd ../
8 | go build -i -ldflags="-H windowsgui" -tags gui -o rproxy_GUI.exe
9 | pause
--------------------------------------------------------------------------------
/build/build-win64.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 | echo Compiling 64-bit rproxy...
3 |
4 | set GOARCH=amd64
5 | set GOOS=windows
6 | set CGO_ENABLED=0
7 | cd ../
8 | go build -o rproxy.exe
9 | pause
--------------------------------------------------------------------------------
/build/rpbuild.go:
--------------------------------------------------------------------------------
1 | /*
2 | 这是一个自己用的构建工具,一个一个的构建太麻烦了。
3 | 这里主要整合编译,打包相应平台的
4 | */
5 |
6 | package main
7 |
8 | import (
9 | "archive/zip"
10 | "compress/flate"
11 | "fmt"
12 | "io"
13 | "io/ioutil"
14 | "os"
15 | "os/exec"
16 | "runtime"
17 | "strings"
18 | )
19 |
20 | func main() {
21 |
22 | switch runtime.GOOS {
23 | case "windows":
24 | windowsPkg()
25 |
26 | case "linux":
27 | linuxPkg()
28 |
29 | case "darwin":
30 | darwinPkg()
31 | }
32 | }
33 |
34 | func windowsPkg() {
35 |
36 | fmt.Println("编译rproxy-win64-GUI")
37 | if executeBash("build-win64-GUI.bat") == nil {
38 | fmt.Println("打包rproxy-win64-GUI")
39 | createZipFile("rproxy-win64-GUI.zip", true)
40 | }
41 | fmt.Println("------------------------------")
42 |
43 | fmt.Println("编译rproxy-win64")
44 | if executeBash("build-win64.bat") == nil {
45 | fmt.Println("打包rproxy-win64")
46 | createZipFile("rproxy-win64.zip", false)
47 | }
48 | fmt.Println("------------------------------")
49 |
50 | fmt.Println("编译rproxy-win32-GUI")
51 | if executeBash("build-win32-GUI.bat") == nil {
52 | fmt.Println("打包rproxy-win32-GUI")
53 | createZipFile("rproxy-win32-GUI.zip", true)
54 | }
55 | fmt.Println("------------------------------")
56 |
57 | fmt.Println("编译rproxy-win32")
58 | if executeBash("build-win32.bat") == nil {
59 | fmt.Println("打包rproxy-win32")
60 | createZipFile("rproxy-win32.zip", false)
61 | }
62 | }
63 |
64 | func linuxPkg() {
65 |
66 | fmt.Println("编译rproxy-linux64-GUI")
67 | if executeBash("build-linux64-GUI.sh") == nil {
68 | fmt.Println("打包rproxy-linux64-GUI")
69 | createZipFile("rproxy-linux64-GUI.zip", true)
70 | }
71 | fmt.Println("------------------------------")
72 |
73 | fmt.Println("编译rproxy-linux64")
74 | if executeBash("build-linux64.sh") == nil {
75 | fmt.Println("打包rproxy-linux64")
76 | createZipFile("rproxy-linux64.zip", false)
77 | }
78 | fmt.Println("------------------------------")
79 |
80 | fmt.Println("编译rproxy-linux32")
81 | if executeBash("build-linux32.sh") == nil {
82 | fmt.Println("打包rproxy-linux32")
83 | createZipFile("rproxy-linux32.zip", false)
84 | }
85 | }
86 |
87 | func darwinPkg() {
88 |
89 | fmt.Println("编译rproxy-darwin64-GUI")
90 | if executeBash("build-darwin64-GUI.sh") == nil {
91 | fmt.Println("打包rproxy-darwin64-GUI")
92 | createZipFile("rproxy-darwin64-GUI.zip", true, true)
93 | }
94 | fmt.Println("------------------------------")
95 |
96 | fmt.Println("编译rproxy-darwin64")
97 | if executeBash("build-darwin64.sh") == nil {
98 | fmt.Println("打包rproxy-darwin64")
99 | createZipFile("rproxy-darwin64.zip", false)
100 | }
101 | fmt.Println("------------------------------")
102 |
103 | //fmt.Println("编译rproxy-darwin32-GUI")
104 | //if executeBash("build-darwin32-GUI.sh") == nil {
105 | // fmt.Println("打包rproxy-darwin32-GUI")
106 | // createZipFile("rproxy-darwin32-GUI.zip", true, true)
107 | //}
108 | //fmt.Println("------------------------------")
109 |
110 | fmt.Println("编译rproxy-darwin32")
111 | if executeBash("build-darwin32.sh") == nil {
112 | fmt.Println("打包rproxy-darwin32")
113 | createZipFile("rproxy-darwin32.zip", false)
114 | }
115 | }
116 |
117 | func executeBash(fileName string) error {
118 | var cmd *exec.Cmd
119 | switch runtime.GOOS {
120 | case "windows":
121 | cmd = exec.Command("cmd.exe", "/c", fileName)
122 | default:
123 | cmd = exec.Command("sh", "./"+fileName)
124 | cmd.Stdout = os.Stdout
125 | cmd.Stderr = os.Stderr
126 | }
127 |
128 | err := cmd.Run()
129 | if err != nil {
130 | fmt.Println("执行错误:", err)
131 | }
132 | return err
133 | }
134 |
135 | func createZipFile(zipFileName string, isGUI bool, isDarwinApp ...bool) error {
136 | f, err := os.Create(zipFileName)
137 | if err != nil {
138 | return err
139 | }
140 | defer f.Close()
141 | zw := zip.NewWriter(f)
142 | defer zw.Close()
143 |
144 | zw.RegisterCompressor(zip.Deflate,
145 | func(out io.Writer) (io.WriteCloser, error) {
146 | return flate.NewWriter(out, flate.BestCompression)
147 | })
148 |
149 | compressFile := func(fileName, aliasName string) error {
150 | ff, err := os.Open(fileName)
151 | if err != nil {
152 | return err
153 | }
154 | defer ff.Close()
155 | info, _ := ff.Stat()
156 | header, err := zip.FileInfoHeader(info)
157 | header.Method = zip.Deflate
158 | if aliasName != "" {
159 | header.Name = aliasName
160 | } else {
161 | header.Name = info.Name()
162 | }
163 |
164 | wr, err := zw.CreateHeader(header)
165 | if err != nil {
166 | return err
167 | }
168 | _, err = io.Copy(wr, ff)
169 | if err != nil {
170 | return err
171 | }
172 | return nil
173 | }
174 |
175 | // 复制文档和配置
176 | compressFile("../README.md", "")
177 | compressFile("../conf/config.cfg", "conf/config.cfg")
178 | compressFile("../conf/confighttps.cfg", "conf/confighttps.cfg")
179 |
180 | // 复制可执行文件
181 | exeExt := ""
182 | if runtime.GOOS == "windows" {
183 | exeExt = ".exe"
184 | }
185 | fnSuffix := ""
186 | if isGUI {
187 | fnSuffix = "_GUI"
188 | }
189 |
190 | if len(isDarwinApp) == 0 {
191 | compressFile("../rproxy"+fnSuffix+exeExt, "rproxy"+exeExt)
192 | }
193 |
194 | // 复制动态链接库
195 | if isGUI {
196 | switch runtime.GOOS {
197 | case "windows":
198 | liblclPath := "F:\\Golang\\src\\github.com\\ying32\\govcl\\Librarys\\liblcl"
199 | if runtime.GOARCH == "386" {
200 | compressFile(liblclPath+"\\win32\\liblcl.dll", "")
201 | } else if runtime.GOARCH == "amd64" {
202 | compressFile(liblclPath+"\\win64\\liblcl.dll", "")
203 | }
204 | case "linux":
205 | if runtime.GOARCH == "amd64" {
206 | compressFile("/usr/lib/liblcl.so", "")
207 | }
208 | case "darwin":
209 |
210 | // 产生一个app
211 | pkgMacOSApp("../rproxy_GUI")
212 |
213 | compressFile("./rproxy.app/Contents/PkgInfo", "rproxy.app/Contents/PkgInfo")
214 | compressFile("./rproxy.app/Contents/Info.plist", "rproxy.app/Contents/Info.plist")
215 | compressFile("./rproxy.app/Contents/Resources/rproxy.icns", "rproxy.app/Contents/Resources/rproxy.icns")
216 | compressFile("./rproxy.app/Contents/MacOS/rproxy", "rproxy.app/Contents/MacOS/rproxy")
217 | compressFile("./rproxy.app/Contents/MacOS/liblcl.dylib", "rproxy.app/Contents/MacOS/liblcl.dylib")
218 |
219 | }
220 | }
221 |
222 | //zw.Flush()
223 |
224 | return nil
225 | }
226 |
227 | // --- macOS下的
228 |
229 | const (
230 | infoplist = `
231 |
232 |
(*>﹏<*)您要访问的页面目前无法使用
36 | 要不等一会再试试吧。。。
本服务由[rproxy]所提供。
38 | 39 | ` 40 | ) 41 | 42 | func initConfig() *TRProxyConfig { 43 | cfg := new(TRProxyConfig) 44 | if cfg.certPool == nil { 45 | cfg.certPool = x509.NewCertPool() 46 | } 47 | return cfg 48 | } 49 | 50 | // 设置配置文件 51 | func SetConfig(cfg *TRProxyConfig) { 52 | 53 | // 复制字段 54 | conf.TCPPort = cfg.TCPPort 55 | conf.VerifyKey = cfg.VerifyKey 56 | conf.IsHTTPS = cfg.IsHTTPS 57 | conf.TLSCAFile = cfg.TLSCAFile 58 | conf.IsZIP = cfg.IsZIP 59 | 60 | // server 61 | conf.Server.TLSCertFile = cfg.Server.TLSCertFile 62 | conf.Server.TLSKeyFile = cfg.Server.TLSKeyFile 63 | conf.Server.HTTPPort = cfg.Server.HTTPPort 64 | // client 65 | conf.Client.SvrAddr = cfg.Client.SvrAddr 66 | conf.Client.HTTPPort = cfg.Client.HTTPPort 67 | conf.Client.TLSCertFile = cfg.Client.TLSCertFile 68 | conf.Client.TLSKeyFile = cfg.Client.TLSKeyFile 69 | 70 | if conf.Client.TLSCertFile != "" && conf.Client.TLSKeyFile != "" { 71 | var err error 72 | conf.cliCert, err = tls.LoadX509KeyPair(conf.Client.TLSCertFile, conf.Client.TLSKeyFile) 73 | if err != nil { 74 | Log.E(err) 75 | } 76 | } else { 77 | conf.cliCert = tls.Certificate{} 78 | } 79 | // 初始KEY 80 | conf.verifyVal = sha1.Sum([]byte("I AM A KEY:" + conf.VerifyKey)) 81 | 82 | addRootCert() 83 | } 84 | 85 | func GetConfig() *TRProxyConfig { 86 | return conf 87 | } 88 | 89 | // 从 xxx.xxx.xxx.xxx:xxx格式中取出ip地址 90 | func IPStr(conn net.Conn) string { 91 | if conn == nil { 92 | return "" 93 | } 94 | ip := conn.RemoteAddr().String() 95 | i := strings.LastIndex(ip, ":") 96 | if i == -1 { 97 | return ip 98 | } 99 | return ip[:i] 100 | } 101 | 102 | // ExtractFilePath 提取文件名路径 103 | func ExtractFilePath(path string) string { 104 | filename := filepath.Base(path) 105 | return path[:len(path)-len(filename)] 106 | } 107 | 108 | // 添加CA根证书 109 | func addRootCert() { 110 | if !conf.IsHTTPS { 111 | return 112 | } 113 | if conf.TLSCAFile == "" { 114 | Log.E("CA根证书文件不存在。") 115 | } 116 | bs, err := ioutil.ReadFile(conf.TLSCAFile) 117 | if err != nil { 118 | Log.E(err) 119 | return 120 | } 121 | if ok := conf.certPool.AppendCertsFromPEM(bs); !ok { 122 | Log.E("添加CA根证书失败。") 123 | } 124 | } 125 | 126 | // zlib解压缩 127 | func ZlibUnCompress(input []byte) ([]byte, error) { 128 | var out bytes.Buffer 129 | r, err := zlib.NewReader(bytes.NewReader(input)) 130 | if err != nil { 131 | return nil, err 132 | } 133 | defer r.Close() 134 | _, err = io.Copy(&out, r) 135 | if err != nil { 136 | return nil, nil 137 | } 138 | return out.Bytes(), nil 139 | } 140 | 141 | // zlib压缩 142 | func ZlibCompress(input []byte) ([]byte, error) { 143 | var in bytes.Buffer 144 | w, err := zlib.NewWriterLevel(&in, zlib.BestCompression) 145 | if err != nil { 146 | return nil, err 147 | } 148 | _, err = w.Write(input) 149 | if err != nil { 150 | return nil, err 151 | } 152 | err = w.Close() 153 | if err != nil { 154 | return nil, err 155 | } 156 | return in.Bytes(), nil 157 | } 158 | -------------------------------------------------------------------------------- /librp/config.go: -------------------------------------------------------------------------------- 1 | package librp 2 | 3 | import ( 4 | "crypto/tls" 5 | "crypto/x509" 6 | "encoding/json" 7 | "io/ioutil" 8 | ) 9 | 10 | // 配置文件,一个json文件,可包含客户端和服务端配置 11 | type TRProxyConfig struct { 12 | // 不导出字段 13 | certPool *x509.CertPool // 这个字段不导出的,只内部使用 14 | cliCert tls.Certificate // 这个字段不导出的,只内部使用 15 | verifyVal [20]byte // 验证的值 16 | 17 | // 导出的字段 18 | TCPPort int `json:"tcpport"` // Socket连接或者监听的端口 19 | VerifyKey string `json:"vkey"` // 用作客户端与服务端连接时的校验 20 | IsHTTPS bool `json:"ishttps"` // httpPort端口是否只用作HTTPS监听 21 | TLSCAFile string `json:"tlscafile"` // 当ishttps为true时,所需的CA根证书文件 22 | IsZIP bool `json:"iszip"` // 是否开启zip压缩 23 | Server struct { 24 | HTTPPort int `json:"httpport"` // 当mode为server时为服务端监听端口,当为mode为client时为转发至本地客户端的端口 25 | TLSCertFile string `json:"tlscertfile"` // 当ishttps为true时,所需求的TLS证书文件 26 | TLSKeyFile string `json:"tlskeyfile"` // 当ishttps为true时,所需求的TLS密匙文件 27 | } `json:"server"` 28 | Client struct { 29 | SvrAddr string `json:"svraddr"` // 127.0.0.1", "当mode为client时有效,为连接服务器的地址 30 | HTTPPort int `json:"httpport"` // 当mode为server时为服务端监听端口,当为mode为client时为转发至本地客户端的端口 31 | TLSCertFile string `json:"tlscertfile"` // 当ishttps为true时,所需求的TLS证书文件 32 | TLSKeyFile string `json:"tlskeyfile"` // 当ishttps为true时,所需求的TLS密匙文件 33 | LocalAddr string `json:"localaddr"` // 转发至本地的地址,默认为127.0.0.1 34 | } `json:"client"` 35 | } 36 | 37 | func LoadConfig(fileName string, cfg *TRProxyConfig) error { 38 | bs, err := ioutil.ReadFile(fileName) 39 | if err != nil { 40 | return err 41 | } 42 | return json.Unmarshal(bs, cfg) 43 | } 44 | 45 | func SaveConfig(fileName string, cfg *TRProxyConfig) error { 46 | bs, err := json.MarshalIndent(cfg, "", "\t") 47 | if err != nil { 48 | return err 49 | } 50 | return ioutil.WriteFile(fileName, bs, 0666) 51 | } 52 | -------------------------------------------------------------------------------- /librp/log.go: -------------------------------------------------------------------------------- 1 | package librp 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | ) 8 | 9 | var ( 10 | DEBUG = false // 调试模式 输出 Log.D 字符 11 | IsGUI = false // 是否为GUI 12 | LogGUICallback func(msg string) // 当IsGUI=true时,不再输出到日志,使用当前回调函数 13 | ) 14 | 15 | type Logger struct { 16 | } 17 | 18 | var Log Logger 19 | 20 | var std = log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile) 21 | 22 | func (l Logger) println(calldepth int, flag string, v ...interface{}) { 23 | msg := fmt.Sprintf("[%s]: %s", flag, fmt.Sprint(v...)) 24 | if IsGUI { 25 | if LogGUICallback != nil { 26 | LogGUICallback(msg) 27 | } 28 | return 29 | } 30 | std.Output(calldepth, msg) 31 | } 32 | 33 | // 警告 34 | func (l Logger) W(v ...interface{}) { 35 | textYellow() 36 | l.println(3, "WARNING", v...) 37 | } 38 | 39 | // 错误 40 | func (l Logger) E(v ...interface{}) { 41 | textRed() 42 | l.println(3, "ERROR", v...) 43 | } 44 | 45 | func (l Logger) EF(v ...interface{}) { 46 | textRed() 47 | l.println(3, "ERROR", v...) 48 | // 非GUI模式下才退出,APP就挂了 49 | if !IsGUI { 50 | os.Exit(1) 51 | } 52 | } 53 | 54 | // 信息 55 | func (l Logger) I(v ...interface{}) { 56 | textDefault() 57 | l.println(3, "INFO", v...) 58 | } 59 | 60 | // 调试 61 | func (l Logger) D(v ...interface{}) { 62 | if DEBUG { 63 | textGreen() 64 | l.println(3, "DEBUG", v...) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /librp/log_posix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package librp 4 | 5 | import "fmt" 6 | 7 | func textColor(cr uint16) { 8 | if IsGUI { 9 | return 10 | } 11 | fmt.Print(fmt.Sprintf("\033[0;%dm", cr)) 12 | } 13 | func textRed() { 14 | textColor(31) 15 | } 16 | 17 | func textYellow() { 18 | textColor(33) 19 | } 20 | 21 | func textGreen() { 22 | textColor(32) 23 | } 24 | 25 | func textDefault() { 26 | textColor(37) 27 | } 28 | -------------------------------------------------------------------------------- /librp/log_windows.go: -------------------------------------------------------------------------------- 1 | package librp 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | const ( 8 | STD_OUTPUT_HANDLE = ^uint32(11) 9 | ) 10 | 11 | var ( 12 | kernel32 = syscall.NewLazyDLL("kernel32.dll") 13 | _GetStdHandle = kernel32.NewProc("GetStdHandle") 14 | _SetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") 15 | 16 | windowsConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE) 17 | ) 18 | 19 | func GetStdHandle(nStdHandle uint32) uintptr { 20 | ret, _, _ := _GetStdHandle.Call(uintptr(nStdHandle)) 21 | return ret 22 | } 23 | 24 | func SetConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool { 25 | ret, _, _ := _SetConsoleTextAttribute.Call(hConsoleOutput, uintptr(wAttributes)) 26 | return ret != 0 27 | } 28 | 29 | func textColor(cr uint16) { 30 | if IsGUI { 31 | return 32 | } 33 | if windowsConsoleHandle <= 0 { 34 | return 35 | } 36 | SetConsoleTextAttribute(windowsConsoleHandle, cr) 37 | } 38 | 39 | func textRed() { 40 | textColor(8 | 4) // 8 | 4 41 | } 42 | 43 | func textYellow() { 44 | textColor(8 | 6) //8|6 45 | } 46 | 47 | func textGreen() { 48 | textColor(8 | 2) 49 | } 50 | 51 | func textDefault() { 52 | textColor(7) // 7 53 | } 54 | -------------------------------------------------------------------------------- /librp/net.go: -------------------------------------------------------------------------------- 1 | package librp 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/binary" 7 | "errors" 8 | "fmt" 9 | "net" 10 | "net/http" 11 | "net/http/httputil" 12 | "net/url" 13 | "reflect" 14 | "strconv" 15 | "time" 16 | ) 17 | 18 | const ( 19 | PacketHead uint8 = 0x02 20 | PacketTail uint8 = 0x03 21 | 22 | PacketCmd1 uint16 = 1000 23 | PacketVerify uint16 = 1001 24 | PackageError uint16 = 1002 25 | 26 | PacketVersion uint16 = 0x02 27 | ) 28 | 29 | type TPacketHead struct { 30 | //Head uint8 31 | Version uint16 // 2 32 | Cmd uint16 // 2 33 | IsZip uint16 // 2 34 | DataLen uint32 // 4 35 | //Data []byte 36 | //Tail uint8 37 | } 38 | 39 | type IRPObject interface { 40 | Start() error 41 | Close() error 42 | } 43 | 44 | var ( 45 | // 封包头的结构长度 46 | PacketHeadLen = getPacketHeadSize() //8 47 | ) 48 | 49 | func getPacketHeadSize() (size uintptr) { 50 | t := reflect.TypeOf(TPacketHead{}) 51 | for i := 0; i < t.NumField(); i++ { 52 | size += t.Field(i).Type.Size() 53 | } 54 | return 55 | } 56 | 57 | // 编码数据 58 | func EncodeCmd(cmd uint16, data []byte) []byte { 59 | raw := bytes.NewBuffer([]byte{}) 60 | head := TPacketHead{} 61 | head.Version = PacketVersion 62 | head.Cmd = cmd 63 | head.IsZip = 0 64 | if conf.IsZIP { 65 | head.IsZip = 1 66 | 67 | // 压缩数据 68 | temp, err := ZlibCompress(data) 69 | if err != nil { 70 | Log.E(err) 71 | } else { 72 | Log.D("压缩,原长度:", len(data)) 73 | data = temp 74 | Log.D("压缩,现长度:", len(data)) 75 | } 76 | } 77 | head.DataLen = uint32(len(data)) 78 | 79 | binary.Write(raw, binary.LittleEndian, PacketHead) 80 | binary.Write(raw, binary.LittleEndian, head) 81 | raw.Write(data) 82 | binary.Write(raw, binary.LittleEndian, PacketTail) 83 | return raw.Bytes() 84 | } 85 | 86 | // DecodeHead 87 | func DecodeHead(data []byte) *TPacketHead { 88 | head := new(TPacketHead) 89 | raw := bytes.NewBuffer(data) 90 | binary.Read(raw, binary.LittleEndian, head) 91 | 92 | return head 93 | } 94 | 95 | // 编码验证包 96 | func EncodeVerify() []byte { 97 | return EncodeCmd(PacketVerify, conf.verifyVal[:]) 98 | } 99 | 100 | // 验证成功回写 101 | func EncodeVerifyOK() []byte { 102 | return EncodeCmd(PacketVerify, []byte("ok")) 103 | } 104 | 105 | // 验证失败回写 106 | func EncodeVerifyFailed() []byte { 107 | return EncodeCmd(PacketVerify, []byte("failed")) 108 | } 109 | 110 | // 将request 的处理 111 | func EncodeRequest(r *http.Request) ([]byte, error) { 112 | reqBytes, err := httputil.DumpRequest(r, true) 113 | if err != nil { 114 | return nil, err 115 | } 116 | return EncodeCmd(PacketCmd1, reqBytes), err 117 | } 118 | 119 | // 将字节转为request 120 | func DecodeRequest(data []byte, reqHost string, port int, isHttps bool) (*http.Request, error) { 121 | req, err := http.ReadRequest(bufio.NewReader(bytes.NewReader(data))) 122 | if err != nil { 123 | return nil, err 124 | } 125 | if reqHost == "" { 126 | reqHost = "127.0.0.1" 127 | } 128 | req.Host = reqHost 129 | scheme := "http" 130 | if isHttps { 131 | scheme = "https" 132 | if port != 443 { 133 | req.Host += ":" + strconv.Itoa(port) 134 | } 135 | } else { 136 | if port != 80 { 137 | req.Host += ":" + strconv.Itoa(port) 138 | } 139 | } 140 | req.URL, _ = url.Parse(fmt.Sprintf("%s://%s%s", scheme, req.Host, req.RequestURI)) 141 | req.RequestURI = "" 142 | 143 | return req, nil 144 | } 145 | 146 | // 将response转为字节 147 | func EncodeResponse(r *http.Response) ([]byte, error) { 148 | respBytes, err := httputil.DumpResponse(r, true) 149 | if err != nil { 150 | return nil, err 151 | } 152 | return EncodeCmd(PacketCmd1, respBytes), err 153 | } 154 | 155 | //// 将字节转为response 156 | func DecodeResponse(data []byte) (*http.Response, error) { 157 | 158 | resp, err := http.ReadResponse(bufio.NewReader(bytes.NewReader(data)), nil) 159 | if err != nil { 160 | return nil, err 161 | } 162 | return resp, nil 163 | } 164 | 165 | //-------------------------一些简化--------------------------- 166 | 167 | // 写错误 168 | func wError(conn net.Conn, err error) error { 169 | return wData(conn, EncodeCmd(PackageError, []byte(err.Error()))) 170 | } 171 | 172 | // 写数据 173 | func wData(conn net.Conn, data []byte) error { 174 | if conn == nil { 175 | return errors.New("连接无效。") 176 | } 177 | // 先不理写入的 178 | _, err := conn.Write(data) 179 | return err 180 | } 181 | 182 | // 读数据 183 | func rData(conn net.Conn, bLen int) ([]byte, error) { 184 | bsBuff := bytes.NewBuffer([]byte{}) 185 | bufLen := bLen 186 | for { 187 | if bsBuff.Len() >= bLen { 188 | break 189 | } 190 | buf := make([]byte, bufLen) 191 | nr, err := conn.Read(buf) 192 | if err != nil { 193 | return nil, err 194 | } 195 | bsBuff.Write(buf[:nr]) 196 | if nr == bLen { 197 | break 198 | } 199 | bufLen = bLen - bsBuff.Len() 200 | } 201 | return bsBuff.Bytes(), nil 202 | } 203 | 204 | // 读数据包 205 | func readPacket(conn net.Conn, fn func(cmd uint16, data []byte) error) error { 206 | if conn == nil { 207 | return errors.New("连接无效。") 208 | } 209 | byteFlag := make([]byte, 1) 210 | _, err := conn.Read(byteFlag) 211 | if err != nil { 212 | return err 213 | } 214 | // 检测包头,必须等于这个才 215 | if byteFlag[0] == PacketHead { 216 | headBuff := make([]byte, PacketHeadLen) 217 | _, err := conn.Read(headBuff) 218 | if err != nil { 219 | return err 220 | } 221 | head := DecodeHead(headBuff) 222 | if head.Version == PacketVersion { 223 | if head.DataLen > 0 || head.DataLen <= 1024*1024*4 { 224 | bodyData, err := rData(conn, int(head.DataLen)) 225 | if err != nil { 226 | return err 227 | } 228 | _, err = conn.Read(byteFlag) 229 | if err != nil { 230 | return err 231 | } 232 | if byteFlag[0] == PacketTail { 233 | // zip解压 234 | if head.IsZip == 1 { 235 | temp, err := ZlibUnCompress(bodyData) 236 | if err != nil { 237 | Log.E(err) 238 | } else { 239 | Log.D("解压缩,原长度:", len(bodyData)) 240 | bodyData = temp 241 | Log.D("解压缩,现长度:", len(bodyData)) 242 | } 243 | } 244 | return fn(head.Cmd, bodyData) 245 | } else { 246 | Log.E("包尾不正确") 247 | } 248 | } else { 249 | Log.E("数据太长,不给处理。") 250 | } 251 | } else { 252 | return errors.New("版本不一致") 253 | } 254 | } 255 | return nil 256 | } 257 | 258 | func keepALive(conn net.Conn) { 259 | if conn.(*net.TCPConn).SetKeepAlive(true) == nil { 260 | conn.(*net.TCPConn).SetKeepAlivePeriod(time.Duration(1 * time.Minute)) 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /librp/net_test.go: -------------------------------------------------------------------------------- 1 | package librp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestHttp(t *testing.T) { 8 | t.Log("Size:", getPacketHeadSize()) 9 | //GenDefConfig() 10 | } 11 | -------------------------------------------------------------------------------- /librp/server.go: -------------------------------------------------------------------------------- 1 | package librp 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "crypto/tls" 7 | "errors" 8 | "fmt" 9 | "io/ioutil" 10 | "net" 11 | "net/http" 12 | "strings" 13 | "sync" 14 | "time" 15 | ) 16 | 17 | type TRPServer struct { 18 | IRPObject 19 | listener net.Listener 20 | httpSvr *http.Server 21 | conn net.Conn 22 | sync.RWMutex 23 | running bool 24 | } 25 | 26 | func NewRPServer() *TRPServer { 27 | s := new(TRPServer) 28 | return s 29 | } 30 | 31 | func (s *TRPServer) Start() error { 32 | var err error 33 | s.listener, err = net.Listen("tcp", fmt.Sprintf(":%d", conf.TCPPort)) 34 | s.running = true 35 | if err != nil { 36 | return err 37 | } 38 | go s.httpServer() 39 | return s.tcpServer() 40 | } 41 | 42 | func (s *TRPServer) Close() error { 43 | s.running = false 44 | if s.conn != nil { 45 | s.conn.Close() 46 | s.conn = nil 47 | } 48 | if s.httpSvr != nil { 49 | ctx, _ := context.WithTimeout(context.Background(), time.Second*5) 50 | s.httpSvr.Shutdown(ctx) 51 | s.httpSvr.Close() 52 | s.httpSvr = nil 53 | } 54 | if s.listener != nil { 55 | err := s.listener.Close() 56 | s.listener = nil 57 | return err 58 | } 59 | return errors.New("TCP实例未创建!") 60 | } 61 | 62 | func (s *TRPServer) tcpServer() error { 63 | var err error 64 | for s.running { 65 | var conn net.Conn 66 | conn, err = s.listener.Accept() 67 | if err != nil { 68 | if strings.Contains(err.Error(), "use of closed network connection") { 69 | return nil 70 | } 71 | Log.E(err) 72 | continue 73 | } 74 | go s.cliProcess(conn) 75 | } 76 | return err 77 | } 78 | 79 | func badRequest(w http.ResponseWriter) { 80 | w.Header().Set("Content-Type", "text/html; charset=utf-8") 81 | w.Header().Set("X-Content-Type-Options", "nosniff") 82 | w.WriteHeader(http.StatusBadRequest) 83 | w.Write([]byte(errorHTML)) 84 | } 85 | 86 | type THTTPHandler struct { 87 | http.Handler 88 | l sync.RWMutex 89 | read func(w http.ResponseWriter) error 90 | write func(r *http.Request) error 91 | } 92 | 93 | func newHTTPHandler(l sync.RWMutex, read func(w http.ResponseWriter) error, write func(r *http.Request) error) *THTTPHandler { 94 | h := new(THTTPHandler) 95 | h.l = l 96 | h.read = read 97 | h.write = write 98 | return h 99 | } 100 | 101 | func (h *THTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 102 | h.l.Lock() 103 | defer h.l.Unlock() 104 | Log.I(r.Method + " " + r.RequestURI) 105 | err := h.write(r) 106 | if err != nil { 107 | badRequest(w) 108 | Log.E(err) 109 | return 110 | } 111 | err = h.read(w) 112 | if err != nil { 113 | badRequest(w) 114 | Log.E(err) 115 | return 116 | } 117 | } 118 | 119 | func (s *TRPServer) httpServer() { 120 | 121 | s.httpSvr = &http.Server{ 122 | Addr: fmt.Sprintf(":%d", conf.Server.HTTPPort), 123 | Handler: newHTTPHandler(s.RWMutex, s.read, s.write), 124 | TLSConfig: &tls.Config{ 125 | ClientCAs: conf.certPool, 126 | //ClientAuth: tls.RequireAndVerifyClientCert, 127 | }, 128 | } 129 | var err error 130 | if !conf.IsHTTPS { 131 | err = s.httpSvr.ListenAndServe() 132 | } else { 133 | 134 | err = s.httpSvr.ListenAndServeTLS(conf.Server.TLSCertFile, conf.Server.TLSKeyFile) 135 | } 136 | if err != nil && err != http.ErrServerClosed { 137 | Log.EF(err) 138 | } else { 139 | Log.I("HTTP服务已关闭。") 140 | } 141 | } 142 | 143 | func (s *TRPServer) cliProcess(conn net.Conn) error { 144 | // 客户端没有在连接成功后5秒内发送数据则超时 145 | conn.SetReadDeadline(time.Now().Add(5 * time.Second)) 146 | err := readPacket(conn, func(cmd uint16, data []byte) error { 147 | conn.SetReadDeadline(time.Time{}) 148 | if cmd == PacketVerify { 149 | if bytes.Compare(data, conf.verifyVal[:]) != 0 { 150 | return errors.New("首次连接校验证失败。") 151 | } 152 | } else { 153 | return errors.New("首次请求命令不正确。") 154 | } 155 | return nil 156 | }) 157 | if err != nil { 158 | Log.W("当前客户端连接校验错误,关闭此客户端。") 159 | conn.Write(EncodeVerifyFailed()) 160 | conn.Close() 161 | return err 162 | } 163 | // 检测上次已连接的客户端,尝试断开 164 | if s.conn != nil { 165 | Log.W("服务端已有客户端连接!断开之前的:", IPStr(conn)) 166 | s.conn.Close() 167 | s.conn = nil 168 | } 169 | if _, err := conn.Write(EncodeVerifyOK()); err != nil { 170 | return err 171 | } 172 | Log.I("连接新的客户端:", IPStr(conn)) 173 | s.conn = conn 174 | keepALive(s.conn) 175 | return nil 176 | } 177 | 178 | func (s *TRPServer) write(r *http.Request) error { 179 | if s.conn == nil { 180 | return errors.New("客户端未连接。") 181 | } 182 | reqBytes, err := EncodeRequest(r) 183 | if err != nil { 184 | return err 185 | } 186 | return wData(s.conn, reqBytes) 187 | } 188 | 189 | func (s *TRPServer) read(w http.ResponseWriter) error { 190 | return readPacket(s.conn, func(cmd uint16, data []byte) error { 191 | switch cmd { 192 | case PacketCmd1: 193 | resp, err := DecodeResponse(data) 194 | if err != nil { 195 | return err 196 | } 197 | bodyBytes, err := ioutil.ReadAll(resp.Body) 198 | if err != nil { 199 | return err 200 | } 201 | for k, v := range resp.Header { 202 | for _, v2 := range v { 203 | w.Header().Set(k, v2) 204 | } 205 | } 206 | w.WriteHeader(resp.StatusCode) 207 | w.Write(bodyBytes) 208 | 209 | case PackageError: 210 | return errors.New(string(data)) 211 | } 212 | 213 | return nil 214 | }) 215 | } 216 | -------------------------------------------------------------------------------- /lzUI/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | res2go -outmain false -outpath "../" -pause "ew" -outres false 3 | echo // +build gui>> $ 4 | echo.>> $ 5 | type "..\MainForm.go" >> $ 6 | move $ "..\MainForm.go" -------------------------------------------------------------------------------- /lzUI/lzUI.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ying32/rproxy/6d385c87b5d6799e62c0f07ec08701d6d3aa2cdb/lzUI/lzUI.ico -------------------------------------------------------------------------------- /lzUI/lzUI.lpi: -------------------------------------------------------------------------------- 1 | 2 |