mirror of https://github.com/ehang-io/nps
版本重构,加web管理方式
parent
974f605ff6
commit
abc30a9ad1
|
|
@ -0,0 +1,6 @@
|
|||
appname = httpMonitor
|
||||
httpport = 8080
|
||||
runmode = dev
|
||||
password=123
|
||||
hostPort=8028
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
a.proxy.com,127.0.0.1:81,o2430bnq22jgnmcl
|
||||
b.proxy.com,127.0.0.1:82,o2430bnq22jgnmcl
|
||||
a.o.com,10.1.50.203:80,o2430bnq22jgnmcl
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
8021,tunnelServer,10.1.50.203:80,um80bbyvcaiw7n17,,,,1
|
||||
8001,tunnelServer,10.1.50.101:22,h8vcpciko9lczxmb,,,gzip,0
|
||||
8022,tunnelServer,10.1.50.196:4000,vcerzo01q16cdn9n,,,snappy,1
|
||||
8002,udpServer,10.1.50.102:53,zcbsx5kp53cg5yqv,,,,0
|
||||
8003,sock5Server,,9mq856pqq3jq05qv,,,snappy,1
|
||||
8010,sock5Server,,0aj2xfec0mbkann5,test,test,gzip,0
|
||||
8004,httpProxyServer,,y72zj8t074lslln0,,,gzip,0
|
||||
0,hostServer,,o2430bnq22jgnmcl,,,,1
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/cnlh/easyProxy/lib"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type BaseController struct {
|
||||
beego.Controller
|
||||
controllerName string
|
||||
actionName string
|
||||
}
|
||||
|
||||
//初始化参数
|
||||
func (s *BaseController) Prepare() {
|
||||
controllerName, actionName := s.GetControllerAndAction()
|
||||
s.controllerName = strings.ToLower(controllerName[0 : len(controllerName)-10])
|
||||
s.actionName = strings.ToLower(actionName)
|
||||
if s.GetSession("auth") != true {
|
||||
s.Redirect("/login/index", 302)
|
||||
}
|
||||
}
|
||||
|
||||
//加载模板
|
||||
func (s *BaseController) display(tpl ...string) {
|
||||
var tplname string
|
||||
if len(tpl) > 0 {
|
||||
tplname = strings.Join([]string{tpl[0], "html"}, ".")
|
||||
} else {
|
||||
tplname = s.controllerName + "/" + s.actionName + ".html"
|
||||
}
|
||||
s.Data["menu"] = s.actionName
|
||||
ip := s.Ctx.Request.Host
|
||||
s.Data["ip"] = lib.Gethostbyname(ip[0:strings.LastIndex(ip, ":")])
|
||||
s.Data["p"] = *lib.TcpPort
|
||||
s.Data["proxyPort"] = beego.AppConfig.String("hostPort")
|
||||
s.Layout = "public/layout.html"
|
||||
s.TplName = tplname
|
||||
}
|
||||
|
||||
//错误
|
||||
func (s *BaseController) error() {
|
||||
s.Layout = "public/layout.html"
|
||||
s.TplName = "public/error.html"
|
||||
}
|
||||
|
||||
//去掉没有err返回值的int
|
||||
func (s *BaseController) GetIntNoErr(key string, def ...int) int {
|
||||
strv := s.Ctx.Input.Query(key)
|
||||
if len(strv) == 0 && len(def) > 0 {
|
||||
return def[0]
|
||||
}
|
||||
val, _ := strconv.Atoi(strv)
|
||||
return val
|
||||
}
|
||||
|
||||
//获取去掉错误的bool值
|
||||
func (s *BaseController) GetBoolNoErr(key string, def ...bool) bool {
|
||||
strv := s.Ctx.Input.Query(key)
|
||||
if len(strv) == 0 && len(def) > 0 {
|
||||
return def[0]
|
||||
}
|
||||
val, _ := strconv.ParseBool(strv)
|
||||
return val
|
||||
}
|
||||
|
||||
//ajax正确返回
|
||||
func (s *BaseController) AjaxOk(str string) {
|
||||
s.Data["json"] = ajax(str, 1)
|
||||
s.ServeJSON()
|
||||
s.StopRun()
|
||||
}
|
||||
|
||||
//ajax错误返回
|
||||
func (s *BaseController) AjaxErr(str string) {
|
||||
s.Data["json"] = ajax(str, 0)
|
||||
s.ServeJSON()
|
||||
s.StopRun()
|
||||
}
|
||||
|
||||
//组装ajax
|
||||
func ajax(str string, status int) (map[string]interface{}) {
|
||||
json := make(map[string]interface{})
|
||||
json["status"] = status
|
||||
json["msg"] = str
|
||||
return json
|
||||
}
|
||||
|
||||
//ajax table返回
|
||||
func (s *BaseController) AjaxTable(list interface{}, cnt int, recordsTotal int) {
|
||||
json := make(map[string]interface{})
|
||||
json["data"] = list
|
||||
json["draw"] = s.GetIntNoErr("draw")
|
||||
json["err"] = ""
|
||||
json["recordsTotal"] = recordsTotal
|
||||
json["recordsFiltered"] = cnt
|
||||
s.Data["json"] = json
|
||||
s.ServeJSON()
|
||||
s.StopRun()
|
||||
}
|
||||
|
||||
//ajax table参数
|
||||
func (s *BaseController) GetAjaxParams() (start, limit int) {
|
||||
s.Ctx.Input.Bind(&start, "start")
|
||||
s.Ctx.Input.Bind(&limit, "length")
|
||||
return
|
||||
}
|
||||
|
||||
func (s *BaseController) SetInfo(name string) {
|
||||
s.Data["name"] = name
|
||||
}
|
||||
|
||||
func (s *BaseController) SetType(name string) {
|
||||
s.Data["type"] = name
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/cnlh/easyProxy/lib"
|
||||
)
|
||||
|
||||
type IndexController struct {
|
||||
BaseController
|
||||
}
|
||||
|
||||
func (s *IndexController) Index() {
|
||||
s.SetInfo("使用说明")
|
||||
s.display("index/index")
|
||||
}
|
||||
|
||||
func (s *IndexController) Tcp() {
|
||||
s.SetInfo("tcp隧道管理")
|
||||
s.SetType("tunnelServer")
|
||||
s.display("index/list")
|
||||
}
|
||||
|
||||
func (s *IndexController) Udp() {
|
||||
s.SetInfo("udp隧道管理")
|
||||
s.SetType("udpServer")
|
||||
s.display("index/list")
|
||||
}
|
||||
|
||||
func (s *IndexController) Socks5() {
|
||||
s.SetInfo("socks5管理")
|
||||
s.SetType("sock5Server")
|
||||
s.display("index/list")
|
||||
}
|
||||
|
||||
func (s *IndexController) Http() {
|
||||
s.SetInfo("http代理管理")
|
||||
s.SetType("httpProxyServer")
|
||||
s.display("index/list")
|
||||
}
|
||||
|
||||
func (s *IndexController) Host() {
|
||||
s.SetInfo("host模式管理")
|
||||
s.SetType("hostServer")
|
||||
s.display("index/list")
|
||||
}
|
||||
|
||||
func (s *IndexController) GetTaskList() {
|
||||
start, length := s.GetAjaxParams()
|
||||
taskType := s.GetString("type")
|
||||
list, cnt := lib.CsvDb.GetTaskList(start, length, taskType)
|
||||
s.AjaxTable(list, cnt, cnt)
|
||||
}
|
||||
|
||||
func (s *IndexController) Add() {
|
||||
if s.Ctx.Request.Method == "GET" {
|
||||
s.Data["type"] = s.GetString("type")
|
||||
s.SetInfo("新增")
|
||||
s.display()
|
||||
} else {
|
||||
t := &lib.TaskList{
|
||||
TcpPort: s.GetIntNoErr("port"),
|
||||
Mode: s.GetString("type"),
|
||||
Target: s.GetString("target"),
|
||||
VerifyKey: lib.GetRandomString(16),
|
||||
U: s.GetString("u"),
|
||||
P: s.GetString("p"),
|
||||
Compress: s.GetString("compress"),
|
||||
IsRun: 0,
|
||||
}
|
||||
lib.CsvDb.NewTask(t)
|
||||
if err := lib.AddTask(t); err != nil {
|
||||
s.AjaxErr(err.Error())
|
||||
} else {
|
||||
s.AjaxOk("添加成功")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IndexController) Edit() {
|
||||
if s.Ctx.Request.Method == "GET" {
|
||||
vKey := s.GetString("vKey")
|
||||
if t, err := lib.CsvDb.GetTask(vKey); err != nil {
|
||||
s.error()
|
||||
} else {
|
||||
s.Data["t"] = t
|
||||
}
|
||||
s.SetInfo("修改")
|
||||
s.display()
|
||||
} else {
|
||||
vKey := s.GetString("vKey")
|
||||
if t, err := lib.CsvDb.GetTask(vKey); err != nil {
|
||||
s.error()
|
||||
} else {
|
||||
t.TcpPort = s.GetIntNoErr("port")
|
||||
t.Mode = s.GetString("type")
|
||||
t.Target = s.GetString("target")
|
||||
t.U = s.GetString("u")
|
||||
t.P = s.GetString("p")
|
||||
t.Compress = s.GetString("compress")
|
||||
lib.CsvDb.UpdateTask(t)
|
||||
lib.StopServer(t.VerifyKey)
|
||||
lib.StartTask(t.VerifyKey)
|
||||
}
|
||||
s.AjaxOk("修改成功")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IndexController) Stop() {
|
||||
vKey := s.GetString("vKey")
|
||||
if err := lib.StopServer(vKey); err != nil {
|
||||
s.AjaxErr("停止失败")
|
||||
}
|
||||
s.AjaxOk("停止成功")
|
||||
}
|
||||
func (s *IndexController) Del() {
|
||||
vKey := s.GetString("vKey")
|
||||
if err := lib.DelTask(vKey); err != nil {
|
||||
s.AjaxErr("删除失败")
|
||||
}
|
||||
s.AjaxOk("删除成功")
|
||||
}
|
||||
|
||||
func (s *IndexController) Start() {
|
||||
vKey := s.GetString("vKey")
|
||||
if err := lib.StartTask(vKey); err != nil {
|
||||
s.AjaxErr("开启失败")
|
||||
}
|
||||
s.AjaxOk("开启成功")
|
||||
}
|
||||
|
||||
func (s *IndexController) HostList() {
|
||||
if s.Ctx.Request.Method == "GET" {
|
||||
s.Data["vkey"] = s.GetString("vkey")
|
||||
s.SetInfo("域名列表")
|
||||
s.display("index/hlist")
|
||||
} else {
|
||||
start, length := s.GetAjaxParams()
|
||||
vkey := s.GetString("vkey")
|
||||
list, cnt := lib.CsvDb.GetHostList(start, length, vkey)
|
||||
s.AjaxTable(list, cnt, cnt)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IndexController) DelHost() {
|
||||
host := s.GetString("host")
|
||||
if err := lib.CsvDb.DelHost(host); err != nil {
|
||||
s.AjaxErr("删除失败")
|
||||
}
|
||||
s.AjaxOk("删除成功")
|
||||
}
|
||||
|
||||
func (s *IndexController) AddHost() {
|
||||
if s.Ctx.Request.Method == "GET" {
|
||||
s.Data["vkey"] = s.GetString("vkey")
|
||||
s.SetInfo("新增")
|
||||
s.display("index/hadd")
|
||||
} else {
|
||||
h := &lib.HostList{
|
||||
Vkey: s.GetString("vkey"),
|
||||
Host: s.GetString("host"),
|
||||
Target: s.GetString("target"),
|
||||
}
|
||||
lib.CsvDb.NewHost(h)
|
||||
s.AjaxOk("添加成功")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
type LoginController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func (self *LoginController) Index() {
|
||||
self.TplName = "login/index.html"
|
||||
}
|
||||
func (self *LoginController) Verify() {
|
||||
if self.GetString("psd") == beego.AppConfig.String("password") {
|
||||
self.SetSession("auth", true)
|
||||
self.Data["json"] = map[string]interface{}{"status": 1, "msg": "验证成功"}
|
||||
self.ServeJSON()
|
||||
} else {
|
||||
self.Data["json"] = map[string]interface{}{"status": 0, "msg": "验证失败"}
|
||||
self.ServeJSON()
|
||||
}
|
||||
}
|
||||
func (self *LoginController) Out() {
|
||||
self.SetSession("auth", false)
|
||||
self.Redirect("/login/index", 302)
|
||||
}
|
||||
121
http.go
121
http.go
|
|
@ -1,121 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
http.ReadRequest()
|
||||
http.ReadResponse()
|
||||
httputil.DumpRequest()
|
||||
httputil.DumpResponse()
|
||||
*/
|
||||
|
||||
// 将request 的处理
|
||||
func EncodeRequest(r *http.Request) ([]byte, error) {
|
||||
raw := bytes.NewBuffer([]byte{})
|
||||
// 写签名
|
||||
binary.Write(raw, binary.LittleEndian, []byte("sign"))
|
||||
reqBytes, err := httputil.DumpRequest(r, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 写body数据长度 + 1
|
||||
binary.Write(raw, binary.LittleEndian, int32(len(reqBytes)+1))
|
||||
// 判断是否为http或者https的标识1字节
|
||||
binary.Write(raw, binary.LittleEndian, bool(r.URL.Scheme == "https"))
|
||||
if err := binary.Write(raw, binary.LittleEndian, reqBytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return raw.Bytes(), nil
|
||||
}
|
||||
|
||||
// 将字节转为request
|
||||
func DecodeRequest(data []byte) (*http.Request, error) {
|
||||
if len(data) <= 100 {
|
||||
return nil, errors.New("待解码的字节长度太小")
|
||||
}
|
||||
req, err := http.ReadRequest(bufio.NewReader(bytes.NewReader(data[1:])))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
str := strings.Split(req.Host, ":")
|
||||
req.Host, err = getHost(str[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scheme := "http"
|
||||
if data[0] == 1 {
|
||||
scheme = "https"
|
||||
}
|
||||
req.URL, _ = url.Parse(fmt.Sprintf("%s://%s%s", scheme, req.Host, req.RequestURI))
|
||||
req.RequestURI = ""
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
//// 将response转为字节
|
||||
func EncodeResponse(r *http.Response) ([]byte, error) {
|
||||
raw := bytes.NewBuffer([]byte{})
|
||||
binary.Write(raw, binary.LittleEndian, []byte("sign"))
|
||||
respBytes, err := httputil.DumpResponse(r, true)
|
||||
if config.Replace == 1 {
|
||||
respBytes = replaceHost(respBytes)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
zw := gzip.NewWriter(&buf)
|
||||
zw.Write(respBytes)
|
||||
zw.Close()
|
||||
binary.Write(raw, binary.LittleEndian, int32(len(buf.Bytes())))
|
||||
if err := binary.Write(raw, binary.LittleEndian, buf.Bytes()); err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
return raw.Bytes(), nil
|
||||
}
|
||||
|
||||
//// 将字节转为response
|
||||
func DecodeResponse(data []byte) (*http.Response, error) {
|
||||
zr, err := gzip.NewReader(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer zr.Close()
|
||||
resp, err := http.ReadResponse(bufio.NewReader(zr), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func getHost(str string) (string, error) {
|
||||
for _, v := range config.SiteList {
|
||||
if v.Host == str {
|
||||
return v.Url + ":" + strconv.Itoa(v.Port), nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("没有找到解析的的host!")
|
||||
}
|
||||
|
||||
func replaceHost(resp []byte) []byte {
|
||||
str := string(resp)
|
||||
for _, v := range config.SiteList {
|
||||
str = strings.Replace(str, v.Url+":"+strconv.Itoa(v.Port), v.Host, -1)
|
||||
str = strings.Replace(str, v.Url, v.Host, -1)
|
||||
}
|
||||
return []byte(str)
|
||||
}
|
||||
51
json.go
51
json.go
|
|
@ -1,51 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
//定义配置文件解析后的结构
|
||||
type Server struct {
|
||||
Ip string
|
||||
Port int
|
||||
Tcp int
|
||||
Vkey string
|
||||
Num int
|
||||
}
|
||||
|
||||
type Site struct {
|
||||
Host string
|
||||
Url string
|
||||
Port int
|
||||
}
|
||||
type Config struct {
|
||||
Server Server
|
||||
SiteList []Site
|
||||
Replace int
|
||||
}
|
||||
type JsonStruct struct {
|
||||
}
|
||||
|
||||
func NewJsonStruct() *JsonStruct {
|
||||
return &JsonStruct{}
|
||||
}
|
||||
func (jst *JsonStruct) Load(filename string) (Config, error) {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
config := Config{}
|
||||
if err != nil {
|
||||
return config, errors.New("配置文件打开错误")
|
||||
}
|
||||
err = json.Unmarshal(data, &config)
|
||||
if err != nil {
|
||||
return config, errors.New("配置文件解析错误")
|
||||
}
|
||||
if config.Server.Tcp <= 0 || config.Server.Tcp >= 65536 {
|
||||
return config, errors.New("请输入正确的tcp端口")
|
||||
}
|
||||
if config.Server.Vkey == "" {
|
||||
return config, errors.New("密钥不能为空!")
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package lib
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
|
@ -13,12 +13,14 @@ type TRPClient struct {
|
|||
svrAddr string
|
||||
tcpNum int
|
||||
sync.Mutex
|
||||
vKey string
|
||||
}
|
||||
|
||||
func NewRPClient(svraddr string, tcpNum int) *TRPClient {
|
||||
func NewRPClient(svraddr string, tcpNum int, vKey string) *TRPClient {
|
||||
c := new(TRPClient)
|
||||
c.svrAddr = svraddr
|
||||
c.tcpNum = tcpNum
|
||||
c.vKey = vKey
|
||||
return c
|
||||
}
|
||||
|
||||
|
|
@ -26,9 +28,6 @@ func (s *TRPClient) Start() error {
|
|||
for i := 0; i < s.tcpNum; i++ {
|
||||
go s.newConn()
|
||||
}
|
||||
for {
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -49,7 +48,7 @@ func (s *TRPClient) newConn() error {
|
|||
|
||||
func (s *TRPClient) process(c *Conn) error {
|
||||
c.SetAlive()
|
||||
if _, err := c.Write(getverifyval()); err != nil {
|
||||
if _, err := c.Write([]byte(getverifyval(s.vKey))); err != nil {
|
||||
return err
|
||||
}
|
||||
c.wMain()
|
||||
|
|
@ -63,20 +62,20 @@ func (s *TRPClient) process(c *Conn) error {
|
|||
}
|
||||
switch flags {
|
||||
case VERIFY_EER:
|
||||
log.Fatal("vkey不正确,请检查配置文件")
|
||||
log.Fatalln("vkey:", s.vKey, "不正确,服务端拒绝连接,请检查")
|
||||
case RES_SIGN: //代理请求模式
|
||||
if err := s.dealHttp(c); err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
case WORK_CHAN: //隧道模式,每次开启10个,加快连接速度
|
||||
for i := 0; i < 100; i++ {
|
||||
for i := 0; i < 10; i++ {
|
||||
go s.dealChan()
|
||||
}
|
||||
case RES_MSG:
|
||||
log.Println("服务端返回错误。")
|
||||
default:
|
||||
log.Println("无法解析该错误。")
|
||||
log.Println("无法解析该错误。", flags)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
|
@ -90,7 +89,7 @@ func (s *TRPClient) dealChan() error {
|
|||
return err
|
||||
}
|
||||
//验证
|
||||
if _, err := conn.Write(getverifyval()); err != nil {
|
||||
if _, err := conn.Write([]byte(getverifyval(s.vKey))); err != nil {
|
||||
return err
|
||||
}
|
||||
//默认长连接保持
|
||||
|
|
@ -99,7 +98,7 @@ func (s *TRPClient) dealChan() error {
|
|||
//写标志
|
||||
c.wChan()
|
||||
//获取连接的host type(tcp or udp)
|
||||
typeStr, host, err := c.GetHostFromConn()
|
||||
typeStr, host, en, de, err := c.GetHostFromConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -109,15 +108,16 @@ func (s *TRPClient) dealChan() error {
|
|||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
go relay(NewConn(server), c, DataDecode)
|
||||
relay(c, NewConn(server), DataEncode)
|
||||
go relay(NewConn(server), c, de)
|
||||
relay(c, NewConn(server), en)
|
||||
return nil
|
||||
}
|
||||
|
||||
//http模式处理
|
||||
func (s *TRPClient) dealHttp(c *Conn) error {
|
||||
buf := make([]byte, 1024*32)
|
||||
n, err := c.ReadFromCompress(buf, DataDecode)
|
||||
en, de := c.GetCompressTypeFromConn()
|
||||
n, err := c.ReadFromCompress(buf, de)
|
||||
if err != nil {
|
||||
c.wError()
|
||||
return err
|
||||
|
|
@ -133,7 +133,7 @@ func (s *TRPClient) dealHttp(c *Conn) error {
|
|||
return err
|
||||
}
|
||||
c.wSign()
|
||||
n, err = c.WriteCompress(respBytes, DataEncode)
|
||||
n, err = c.WriteCompress(respBytes, en)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
package lib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/binary"
|
||||
|
|
@ -10,12 +11,63 @@ import (
|
|||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SnappyConn struct {
|
||||
w *snappy.Writer
|
||||
r *snappy.Reader
|
||||
}
|
||||
|
||||
func NewSnappyConn(conn net.Conn) *SnappyConn {
|
||||
c := new(SnappyConn)
|
||||
c.w = snappy.NewBufferedWriter(conn)
|
||||
c.r = snappy.NewReader(conn)
|
||||
return c
|
||||
}
|
||||
|
||||
func (s *SnappyConn) Write(b []byte) (n int, err error) {
|
||||
if n, err = s.w.Write(b); err != nil {
|
||||
return
|
||||
}
|
||||
err = s.w.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
func (s *SnappyConn) Read(b []byte) (n int, err error) {
|
||||
return s.r.Read(b)
|
||||
}
|
||||
|
||||
type GzipConn struct {
|
||||
w *gzip.Writer
|
||||
r *gzip.Reader
|
||||
}
|
||||
|
||||
func NewGzipConn(conn net.Conn) *GzipConn {
|
||||
c := new(GzipConn)
|
||||
c.w = gzip.NewWriter(conn)
|
||||
c.r, err = gzip.NewReader(conn)
|
||||
return c
|
||||
}
|
||||
|
||||
func (s *GzipConn) Write(b []byte) (n int, err error) {
|
||||
if n, err = s.w.Write(b); err != nil || err == io.EOF {
|
||||
err = s.w.Flush()
|
||||
s.w.Close()
|
||||
return
|
||||
}
|
||||
err = s.w.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
func (s *GzipConn) Read(b []byte) (n int, err error) {
|
||||
return s.r.Read(b)
|
||||
}
|
||||
|
||||
type Conn struct {
|
||||
conn net.Conn
|
||||
}
|
||||
|
|
@ -32,7 +84,7 @@ func (s *Conn) ReadLen(len int) ([]byte, error) {
|
|||
buff := make([]byte, 1024)
|
||||
c := 0
|
||||
for {
|
||||
clen, err := s.conn.Read(buff)
|
||||
clen, err := s.Read(buff)
|
||||
if err != nil && err != io.EOF {
|
||||
return raw, err
|
||||
}
|
||||
|
|
@ -64,7 +116,6 @@ func (s *Conn) GetLen() (int, error) {
|
|||
//写入长度
|
||||
func (s *Conn) WriteLen(buf []byte) (int, error) {
|
||||
raw := bytes.NewBuffer([]byte{})
|
||||
|
||||
if err := binary.Write(raw, binary.LittleEndian, int32(len(buf))); err != nil {
|
||||
log.Println(err)
|
||||
return 0, err
|
||||
|
|
@ -79,23 +130,22 @@ func (s *Conn) WriteLen(buf []byte) (int, error) {
|
|||
//读取flag
|
||||
func (s *Conn) ReadFlag() (string, error) {
|
||||
val := make([]byte, 4)
|
||||
_, err := s.conn.Read(val)
|
||||
_, err := s.Read(val)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(val), err
|
||||
}
|
||||
|
||||
//读取host
|
||||
func (s *Conn) GetHostFromConn() (typeStr string, host string, err error) {
|
||||
//读取host 连接地址 压缩类型
|
||||
func (s *Conn) GetHostFromConn() (typeStr string, host string, en, de int, err error) {
|
||||
retry:
|
||||
ltype := make([]byte, 3)
|
||||
_, err = s.Read(ltype)
|
||||
if err != nil {
|
||||
if _, err = s.Read(ltype); err != nil {
|
||||
return
|
||||
}
|
||||
typeStr = string(ltype)
|
||||
if typeStr == TEST_FLAG {
|
||||
if typeStr = string(ltype); typeStr == TEST_FLAG {
|
||||
en, de = s.GetCompressTypeFromConn()
|
||||
goto retry
|
||||
}
|
||||
len, err := s.GetLen()
|
||||
|
|
@ -103,15 +153,14 @@ retry:
|
|||
return
|
||||
}
|
||||
hostByte := make([]byte, len)
|
||||
_, err = s.conn.Read(hostByte)
|
||||
if err != nil {
|
||||
if _, err = s.Read(hostByte); err != nil {
|
||||
return
|
||||
}
|
||||
host = string(hostByte)
|
||||
return
|
||||
}
|
||||
|
||||
//写tcp host
|
||||
//写连接类型 和 host地址
|
||||
func (s *Conn) WriteHost(ltype string, host string) (int, error) {
|
||||
raw := bytes.NewBuffer([]byte{})
|
||||
binary.Write(raw, binary.LittleEndian, []byte(ltype))
|
||||
|
|
@ -130,45 +179,34 @@ func (s *Conn) SetAlive() {
|
|||
|
||||
//从tcp报文中解析出host
|
||||
func (s *Conn) GetHost() (method, address string, rb []byte, err error) {
|
||||
var b [2048]byte
|
||||
var b [32 * 1024]byte
|
||||
var n int
|
||||
var host string
|
||||
if n, err = s.Read(b[:]); err != nil {
|
||||
return
|
||||
}
|
||||
rb = b[:n]
|
||||
//TODO:某些不规范报文可能会有问题
|
||||
fmt.Sscanf(string(b[:n]), "%s", &method)
|
||||
reg, err := regexp.Compile(`(\w+:\/\/)([^/:]+)(:\d*)?`)
|
||||
r, err := http.ReadRequest(bufio.NewReader(bytes.NewReader(rb)))
|
||||
if err != nil {
|
||||
log.Println("解析host出错:", err)
|
||||
return
|
||||
}
|
||||
host = string(reg.Find(b[:]))
|
||||
hostPortURL, err := url.Parse(host)
|
||||
hostPortURL, err := url.Parse(r.Host)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if hostPortURL.Opaque == "443" { //https访问
|
||||
address = hostPortURL.Scheme + ":443"
|
||||
address = r.Host + ":443"
|
||||
} else { //http访问
|
||||
if strings.Index(hostPortURL.Host, ":") == -1 { //host不带端口, 默认80
|
||||
address = hostPortURL.Host + ":80"
|
||||
address = r.Host + ":80"
|
||||
} else {
|
||||
address = hostPortURL.Host
|
||||
address = r.Host
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Conn) Close() error {
|
||||
return s.conn.Close()
|
||||
}
|
||||
func (s *Conn) Write(b []byte) (int, error) {
|
||||
return s.conn.Write(b)
|
||||
}
|
||||
func (s *Conn) Read(b []byte) (int, error) {
|
||||
return s.conn.Read(b)
|
||||
}
|
||||
//压缩方式读
|
||||
func (s *Conn) ReadFromCompress(b []byte, compress int) (int, error) {
|
||||
switch compress {
|
||||
case COMPRESS_GZIP_DECODE:
|
||||
|
|
@ -186,6 +224,7 @@ func (s *Conn) ReadFromCompress(b []byte, compress int) (int, error) {
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
//压缩方式写
|
||||
func (s *Conn) WriteCompress(b []byte, compress int) (n int, err error) {
|
||||
switch compress {
|
||||
case COMPRESS_GZIP_ENCODE:
|
||||
|
|
@ -193,32 +232,61 @@ func (s *Conn) WriteCompress(b []byte, compress int) (n int, err error) {
|
|||
if n, err = w.Write(b); err == nil {
|
||||
w.Flush()
|
||||
}
|
||||
err = w.Close()
|
||||
case COMPRESS_SNAPY_ENCODE:
|
||||
w := snappy.NewBufferedWriter(s)
|
||||
if n, err = w.Write(b); err == nil {
|
||||
w.Flush()
|
||||
}
|
||||
err = w.Close()
|
||||
case COMPRESS_NONE:
|
||||
n, err = s.Write(b)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Conn) wError() (int, error) {
|
||||
return s.conn.Write([]byte(RES_MSG))
|
||||
//写压缩方式
|
||||
func (s *Conn) WriteCompressType(en, de int) {
|
||||
s.Write([]byte(strconv.Itoa(en) + strconv.Itoa(de)))
|
||||
}
|
||||
|
||||
//获取压缩方式
|
||||
func (s *Conn) GetCompressTypeFromConn() (en, de int) {
|
||||
buf := make([]byte, 2)
|
||||
s.Read(buf)
|
||||
en, _ = strconv.Atoi(string(buf[0]))
|
||||
de, _ = strconv.Atoi(string(buf[1]))
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Conn) Close() error {
|
||||
return s.conn.Close()
|
||||
}
|
||||
|
||||
func (s *Conn) Write(b []byte) (int, error) {
|
||||
return s.conn.Write(b)
|
||||
}
|
||||
|
||||
func (s *Conn) Read(b []byte) (int, error) {
|
||||
return s.conn.Read(b)
|
||||
}
|
||||
|
||||
func (s *Conn) wError() (int, error) {
|
||||
return s.Write([]byte(RES_MSG))
|
||||
}
|
||||
|
||||
func (s *Conn) wSign() (int, error) {
|
||||
return s.conn.Write([]byte(RES_SIGN))
|
||||
return s.Write([]byte(RES_SIGN))
|
||||
}
|
||||
|
||||
func (s *Conn) wMain() (int, error) {
|
||||
return s.conn.Write([]byte(WORK_MAIN))
|
||||
return s.Write([]byte(WORK_MAIN))
|
||||
}
|
||||
|
||||
func (s *Conn) wChan() (int, error) {
|
||||
return s.conn.Write([]byte(WORK_CHAN))
|
||||
return s.Write([]byte(WORK_CHAN))
|
||||
}
|
||||
|
||||
func (s *Conn) wTest() (int, error) {
|
||||
return s.conn.Write([]byte(TEST_FLAG))
|
||||
return s.Write([]byte(TEST_FLAG))
|
||||
}
|
||||
|
|
@ -0,0 +1,285 @@
|
|||
package lib
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type TaskList struct {
|
||||
TcpPort int //服务端与客户端通信端口
|
||||
Mode string //启动方式
|
||||
Target string //目标
|
||||
VerifyKey string //flag
|
||||
U string //socks5验证用户名
|
||||
P string //socks5验证密码
|
||||
Compress string //压缩方式
|
||||
Start int //是否开启
|
||||
IsRun int //是否在运行
|
||||
ClientStatus int //客户端状态
|
||||
}
|
||||
|
||||
type HostList struct {
|
||||
Vkey string //服务端与客户端通信端口
|
||||
Host string //启动方式
|
||||
Target string //目标
|
||||
}
|
||||
|
||||
func NewCsv(path string, bridge *Tunnel, runList map[string]interface{}) *Csv {
|
||||
c := new(Csv)
|
||||
c.Path = path
|
||||
c.Bridge = bridge
|
||||
c.RunList = runList
|
||||
return c
|
||||
}
|
||||
|
||||
type Csv struct {
|
||||
Tasks []*TaskList
|
||||
Path string
|
||||
Bridge *Tunnel
|
||||
RunList map[string]interface{}
|
||||
Hosts []*HostList //域名列表
|
||||
}
|
||||
|
||||
func (s *Csv) Init() {
|
||||
s.LoadTaskFromCsv()
|
||||
s.LoadHostFromCsv()
|
||||
}
|
||||
|
||||
func (s *Csv) StoreTasksToCsv() {
|
||||
// 创建文件
|
||||
csvFile, err := os.Create(s.Path + "tasks.csv")
|
||||
if err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
defer csvFile.Close()
|
||||
writer := csv.NewWriter(csvFile)
|
||||
for _, task := range s.Tasks {
|
||||
record := []string{
|
||||
strconv.Itoa(task.TcpPort),
|
||||
task.Mode,
|
||||
task.Target,
|
||||
task.VerifyKey,
|
||||
task.U,
|
||||
task.P,
|
||||
task.Compress,
|
||||
strconv.Itoa(task.Start),
|
||||
}
|
||||
err := writer.Write(record)
|
||||
if err != nil {
|
||||
log.Fatalf(err.Error())
|
||||
}
|
||||
}
|
||||
writer.Flush()
|
||||
}
|
||||
|
||||
func (s *Csv) LoadTaskFromCsv() {
|
||||
// 打开文件
|
||||
file, err := os.Open(s.Path + "tasks.csv")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 获取csv的reader
|
||||
reader := csv.NewReader(file)
|
||||
|
||||
// 设置FieldsPerRecord为-1
|
||||
reader.FieldsPerRecord = -1
|
||||
|
||||
// 读取文件中所有行保存到slice中
|
||||
records, err := reader.ReadAll()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var tasks []*TaskList
|
||||
// 将每一行数据保存到内存slice中
|
||||
for _, item := range records {
|
||||
tcpPort, _ := strconv.Atoi(item[0])
|
||||
Start, _ := strconv.Atoi(item[7])
|
||||
post := &TaskList{
|
||||
TcpPort: tcpPort,
|
||||
Mode: item[1],
|
||||
Target: item[2],
|
||||
VerifyKey: item[3],
|
||||
U: item[4],
|
||||
P: item[5],
|
||||
Compress: item[6],
|
||||
Start: Start,
|
||||
}
|
||||
tasks = append(tasks, post)
|
||||
}
|
||||
s.Tasks = tasks
|
||||
}
|
||||
|
||||
func (s *Csv) StoreHostToCsv() {
|
||||
// 创建文件
|
||||
csvFile, err := os.Create(s.Path + "hosts.csv")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer csvFile.Close()
|
||||
// 获取csv的Writer
|
||||
writer := csv.NewWriter(csvFile)
|
||||
// 将map中的Post转换成slice,因为csv的Write需要slice参数
|
||||
// 并写入csv文件
|
||||
for _, host := range s.Hosts {
|
||||
record := []string{
|
||||
host.Host,
|
||||
host.Target,
|
||||
host.Vkey,
|
||||
}
|
||||
err1 := writer.Write(record)
|
||||
if err1 != nil {
|
||||
panic(err1)
|
||||
}
|
||||
}
|
||||
// 确保所有内存数据刷到csv文件
|
||||
writer.Flush()
|
||||
}
|
||||
|
||||
func (s *Csv) LoadHostFromCsv() {
|
||||
// 打开文件
|
||||
file, err := os.Open(s.Path + "hosts.csv")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 获取csv的reader
|
||||
reader := csv.NewReader(file)
|
||||
|
||||
// 设置FieldsPerRecord为-1
|
||||
reader.FieldsPerRecord = -1
|
||||
|
||||
// 读取文件中所有行保存到slice中
|
||||
records, err := reader.ReadAll()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var hosts []*HostList
|
||||
// 将每一行数据保存到内存slice中
|
||||
for _, item := range records {
|
||||
post := &HostList{
|
||||
Vkey: item[2],
|
||||
Host: item[0],
|
||||
Target: item[1],
|
||||
}
|
||||
hosts = append(hosts, post)
|
||||
}
|
||||
s.Hosts = hosts
|
||||
}
|
||||
|
||||
func (s *Csv) GetTaskList(start, length int, typeVal string) ([]*TaskList, int) {
|
||||
list := make([]*TaskList, 0)
|
||||
var cnt int
|
||||
for _, v := range s.Tasks {
|
||||
if v.Mode != typeVal {
|
||||
continue
|
||||
}
|
||||
cnt++
|
||||
if start--; start < 0 {
|
||||
if length--; length > 0 {
|
||||
if _, ok := s.RunList[v.VerifyKey]; ok {
|
||||
v.IsRun = 1
|
||||
} else {
|
||||
v.IsRun = 0
|
||||
}
|
||||
if s, ok := s.Bridge.signalList[getverifyval(v.VerifyKey)]; ok {
|
||||
if s.Len() > 0 {
|
||||
v.ClientStatus = 1
|
||||
} else {
|
||||
v.ClientStatus = 0
|
||||
}
|
||||
} else {
|
||||
v.ClientStatus = 0
|
||||
}
|
||||
list = append(list, v)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return list, cnt
|
||||
}
|
||||
|
||||
func (s *Csv) NewTask(t *TaskList) {
|
||||
s.Tasks = append(s.Tasks, t)
|
||||
s.StoreTasksToCsv()
|
||||
}
|
||||
|
||||
func (s *Csv) UpdateTask(t *TaskList) error {
|
||||
for k, v := range s.Tasks {
|
||||
if v.VerifyKey == t.VerifyKey {
|
||||
s.Tasks = append(s.Tasks[:k], s.Tasks[k+1:]...)
|
||||
s.Tasks = append(s.Tasks, t)
|
||||
s.StoreTasksToCsv()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
//TODO:待测试
|
||||
return errors.New("不存在")
|
||||
}
|
||||
|
||||
func (s *Csv) AddRunList(vKey string, svr interface{}) {
|
||||
s.RunList[vKey] = svr
|
||||
}
|
||||
|
||||
func (s *Csv) DelRunList(vKey string) {
|
||||
delete(s.RunList, vKey)
|
||||
}
|
||||
|
||||
func (s *Csv) DelTask(vKey string) error {
|
||||
for k, v := range s.Tasks {
|
||||
if v.VerifyKey == vKey {
|
||||
s.Tasks = append(s.Tasks[:k], s.Tasks[k+1:]...)
|
||||
s.StoreTasksToCsv()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("不存在")
|
||||
}
|
||||
|
||||
func (s *Csv) GetTask(vKey string) (v *TaskList, err error) {
|
||||
for _, v = range s.Tasks {
|
||||
if v.VerifyKey == vKey {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = errors.New("未找到")
|
||||
return
|
||||
}
|
||||
|
||||
func (s *Csv) DelHost(host string) error {
|
||||
for k, v := range s.Hosts {
|
||||
if v.Host == host {
|
||||
s.Hosts = append(s.Hosts[:k], s.Hosts[k+1:]...)
|
||||
s.StoreHostToCsv()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("不存在")
|
||||
}
|
||||
|
||||
func (s *Csv) NewHost(t *HostList) {
|
||||
s.Hosts = append(s.Hosts, t)
|
||||
s.StoreHostToCsv()
|
||||
|
||||
}
|
||||
|
||||
func (s *Csv) GetHostList(start, length int, vKey string) ([]*HostList, int) {
|
||||
list := make([]*HostList, 0)
|
||||
var cnt int
|
||||
for _, v := range s.Hosts {
|
||||
if v.Vkey == vKey {
|
||||
cnt++
|
||||
if start--; start < 0 {
|
||||
if length--; length > 0 {
|
||||
list = append(list, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return list, cnt
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
package lib
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
configPath = flag.String("config", "config.json", "配置文件路径")
|
||||
TcpPort = flag.Int("tcpport", 8284, "客户端与服务端通信端口")
|
||||
httpPort = flag.Int("httpport", 8024, "对外监听的端口")
|
||||
rpMode = flag.String("mode", "client", "启动模式")
|
||||
tunnelTarget = flag.String("target", "10.1.50.203:80", "远程目标")
|
||||
verifyKey = flag.String("vkey", "", "验证密钥")
|
||||
u = flag.String("u", "", "socks5验证用户名")
|
||||
p = flag.String("p", "", "socks5验证密码")
|
||||
compress = flag.String("compress", "", "数据压缩方式(gzip|snappy)")
|
||||
serverAddr = flag.String("server", "", "服务器地址ip:端口")
|
||||
config Config
|
||||
err error
|
||||
RunList map[string]interface{} //运行中的任务
|
||||
bridge *Tunnel
|
||||
CsvDb *Csv
|
||||
)
|
||||
|
||||
func init() {
|
||||
RunList = make(map[string]interface{})
|
||||
}
|
||||
|
||||
func InitMode() {
|
||||
flag.Parse()
|
||||
de, en := getCompressType(*compress)
|
||||
if *rpMode == "client" {
|
||||
JsonParse := NewJsonStruct()
|
||||
if config, err = JsonParse.Load(*configPath); err != nil {
|
||||
log.Println("配置文件加载失败")
|
||||
}
|
||||
stop := make(chan int)
|
||||
for _, v := range strings.Split(*verifyKey, ",") {
|
||||
log.Println("客户端启动,连接:", *serverAddr, " 验证令牌:", v)
|
||||
go NewRPClient(*serverAddr, 2, v).Start()
|
||||
}
|
||||
<-stop
|
||||
} else {
|
||||
bridge = newTunnel(*TcpPort)
|
||||
if err := bridge.StartTunnel(); err != nil {
|
||||
log.Fatalln("服务端开启失败", err)
|
||||
}
|
||||
log.Println("服务端启动,监听tcp服务端端口:", *TcpPort)
|
||||
if svr := newMode(*rpMode, bridge, *httpPort, *tunnelTarget, *u, *p, en, de, *verifyKey); svr != nil {
|
||||
reflect.ValueOf(svr).MethodByName("Start").Call(nil)
|
||||
} else {
|
||||
log.Fatalln("启动模式不正确")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//从csv文件中恢复任务
|
||||
func InitFromCsv() {
|
||||
for _, v := range CsvDb.Tasks {
|
||||
if v.Start == 1 {
|
||||
log.Println(""+
|
||||
"启动模式:", v.Mode, "监听端口:", v.TcpPort, "客户端令牌:", v.VerifyKey)
|
||||
AddTask(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newMode(mode string, bridge *Tunnel, httpPort int, tunnelTarget string, u string, p string, enCompress int, deCompress int, vkey string) interface{} {
|
||||
switch mode {
|
||||
case "httpServer":
|
||||
return NewHttpModeServer(httpPort, bridge, enCompress, deCompress, vkey)
|
||||
case "tunnelServer":
|
||||
return NewTunnelModeServer(httpPort, tunnelTarget, ProcessTunnel, bridge, enCompress, deCompress, vkey)
|
||||
case "sock5Server":
|
||||
return NewSock5ModeServer(httpPort, u, p, bridge, enCompress, deCompress, vkey)
|
||||
case "httpProxyServer":
|
||||
return NewTunnelModeServer(httpPort, tunnelTarget, ProcessHttp, bridge, enCompress, deCompress, vkey)
|
||||
case "udpServer":
|
||||
return NewUdpModeServer(httpPort, tunnelTarget, bridge, enCompress, deCompress, vkey)
|
||||
case "webServer":
|
||||
InitCsvDb()
|
||||
return NewWebServer(bridge)
|
||||
case "hostServer":
|
||||
return NewHostServer()
|
||||
case "httpHostServer":
|
||||
return NewTunnelModeServer(httpPort, tunnelTarget, ProcessHost, bridge, enCompress, deCompress, vkey)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func StopServer(cFlag string) error {
|
||||
if v, ok := RunList[cFlag]; ok {
|
||||
reflect.ValueOf(v).MethodByName("Close").Call(nil)
|
||||
delete(RunList, cFlag)
|
||||
if t := bridge.signalList[getverifyval(cFlag)]; t != nil {
|
||||
if *verifyKey == "" { //多客户端模式重启相关隧道
|
||||
for {
|
||||
if t.Len() <= 0 {
|
||||
break
|
||||
}
|
||||
t.Pop().Close()
|
||||
}
|
||||
delete(bridge.signalList, getverifyval(cFlag))
|
||||
delete(bridge.tunnelList, getverifyval(cFlag))
|
||||
}
|
||||
}
|
||||
if t, err := CsvDb.GetTask(cFlag); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.Start = 0
|
||||
CsvDb.UpdateTask(t)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return errors.New("未在运行中")
|
||||
}
|
||||
|
||||
func AddTask(t *TaskList) error {
|
||||
de, en := getCompressType(t.Compress)
|
||||
if svr := newMode(t.Mode, bridge, t.TcpPort, t.Target, t.U, t.P, en, de, t.VerifyKey); svr != nil {
|
||||
RunList[t.VerifyKey] = svr
|
||||
go func() {
|
||||
err := reflect.ValueOf(svr).MethodByName("Start").Call(nil)[0]
|
||||
if err.Interface() != nil {
|
||||
log.Println("客户端", t.VerifyKey, "启动失败,错误:", err)
|
||||
delete(RunList, t.VerifyKey)
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
return errors.New("启动模式不正确")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartTask(vKey string) error {
|
||||
if t, err := CsvDb.GetTask(vKey); err != nil {
|
||||
return err
|
||||
} else {
|
||||
AddTask(t)
|
||||
t.Start = 1
|
||||
CsvDb.UpdateTask(t)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DelTask(vKey string) error {
|
||||
if err := StopServer(vKey); err != nil {
|
||||
return err
|
||||
}
|
||||
return CsvDb.DelTask(vKey)
|
||||
}
|
||||
|
||||
func InitCsvDb() *Csv {
|
||||
var once sync.Once
|
||||
once.Do(func() {
|
||||
CsvDb = NewCsv("./conf/", bridge, RunList)
|
||||
CsvDb.Init()
|
||||
})
|
||||
return CsvDb
|
||||
}
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
package lib
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/astaxie/beego"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
VERIFY_EER = "vkey"
|
||||
WORK_MAIN = "main"
|
||||
WORK_CHAN = "chan"
|
||||
RES_SIGN = "sign"
|
||||
RES_MSG = "msg0"
|
||||
TEST_FLAG = "tst"
|
||||
CONN_TCP = "tcp"
|
||||
CONN_UDP = "udp"
|
||||
)
|
||||
|
||||
type HttpModeServer struct {
|
||||
bridge *Tunnel
|
||||
httpPort int
|
||||
enCompress int
|
||||
deCompress int
|
||||
vKey string
|
||||
}
|
||||
|
||||
func NewHttpModeServer(httpPort int, bridge *Tunnel, enCompress int, deCompress int, vKey string) *HttpModeServer {
|
||||
s := new(HttpModeServer)
|
||||
s.bridge = bridge
|
||||
s.httpPort = httpPort
|
||||
s.enCompress = enCompress
|
||||
s.deCompress = deCompress
|
||||
s.vKey = vKey
|
||||
return s
|
||||
}
|
||||
|
||||
//开始
|
||||
func (s *HttpModeServer) Start() {
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
retry:
|
||||
err, conn := s.bridge.GetSignal(getverifyval(s.vKey))
|
||||
if err != nil {
|
||||
BadRequest(w)
|
||||
}
|
||||
if err := s.writeRequest(r, conn); err != nil {
|
||||
log.Println(err)
|
||||
conn.Close()
|
||||
goto retry
|
||||
return
|
||||
}
|
||||
err = s.writeResponse(w, conn)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
conn.Close()
|
||||
goto retry
|
||||
return
|
||||
}
|
||||
s.bridge.ReturnSignal(conn, getverifyval(s.vKey))
|
||||
})
|
||||
log.Fatalln(http.ListenAndServe(fmt.Sprintf(":%d", s.httpPort), nil))
|
||||
}
|
||||
|
||||
//req转为bytes发送给client端
|
||||
func (s *HttpModeServer) writeRequest(r *http.Request, conn *Conn) error {
|
||||
raw, err := EncodeRequest(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn.wSign()
|
||||
conn.WriteCompressType(s.enCompress, s.deCompress)
|
||||
c, err := conn.WriteCompress(raw, s.enCompress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c != len(raw) {
|
||||
return errors.New("写出长度与字节长度不一致。")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//从client读取出Response
|
||||
func (s *HttpModeServer) writeResponse(w http.ResponseWriter, c *Conn) error {
|
||||
flags, err := c.ReadFlag()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch flags {
|
||||
case RES_SIGN:
|
||||
buf := make([]byte, 1024*32)
|
||||
n, err := c.ReadFromCompress(buf, s.deCompress)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := DecodeResponse(buf[:n])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range resp.Header {
|
||||
for _, v2 := range v {
|
||||
w.Header().Set(k, v2)
|
||||
}
|
||||
}
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
w.Write(bodyBytes)
|
||||
case RES_MSG:
|
||||
BadRequest(w)
|
||||
return errors.New("客户端请求出错")
|
||||
default:
|
||||
BadRequest(w)
|
||||
return errors.New("无法解析此错误")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type process func(c *Conn, s *TunnelModeServer) error
|
||||
|
||||
type TunnelModeServer struct {
|
||||
httpPort int
|
||||
tunnelTarget string
|
||||
process process
|
||||
bridge *Tunnel
|
||||
listener *net.TCPListener
|
||||
enCompress int
|
||||
deCompress int
|
||||
vKey string
|
||||
}
|
||||
|
||||
func NewTunnelModeServer(httpPort int, tunnelTarget string, process process, bridge *Tunnel, enCompress int, deCompress int, vKey string) *TunnelModeServer {
|
||||
s := new(TunnelModeServer)
|
||||
s.httpPort = httpPort
|
||||
s.bridge = bridge
|
||||
s.tunnelTarget = tunnelTarget
|
||||
s.process = process
|
||||
s.enCompress = enCompress
|
||||
s.deCompress = deCompress
|
||||
s.vKey = vKey
|
||||
return s
|
||||
}
|
||||
|
||||
//开始
|
||||
func (s *TunnelModeServer) Start() error {
|
||||
s.listener, err = net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), s.httpPort, ""})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for {
|
||||
conn, err := s.listener.AcceptTCP()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "use of closed network connection") {
|
||||
break
|
||||
}
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
go s.process(NewConn(conn), s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *TunnelModeServer) Close() error {
|
||||
return s.listener.Close()
|
||||
}
|
||||
|
||||
//tcp隧道模式
|
||||
func ProcessTunnel(c *Conn, s *TunnelModeServer) error {
|
||||
link := s.bridge.GetTunnel(getverifyval(s.vKey), s.enCompress, s.deCompress)
|
||||
if _, err := link.WriteHost(CONN_TCP, s.tunnelTarget); err != nil {
|
||||
link.Close()
|
||||
c.Close()
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
go relay(link, c, s.enCompress)
|
||||
relay(c, link, s.deCompress)
|
||||
return nil
|
||||
}
|
||||
|
||||
//http代理模式
|
||||
func ProcessHttp(c *Conn, s *TunnelModeServer) error {
|
||||
method, addr, rb, err := c.GetHost()
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return err
|
||||
}
|
||||
link := s.bridge.GetTunnel(getverifyval(s.vKey), s.enCompress, s.deCompress)
|
||||
if _, err := link.WriteHost(CONN_TCP, addr); err != nil {
|
||||
c.Close()
|
||||
link.Close()
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
if method == "CONNECT" {
|
||||
fmt.Fprint(c, "HTTP/1.1 200 Connection established\r\n")
|
||||
} else {
|
||||
link.WriteCompress(rb, s.enCompress)
|
||||
}
|
||||
go relay(link, c, s.enCompress)
|
||||
relay(c, link, s.deCompress)
|
||||
return nil
|
||||
}
|
||||
|
||||
//多客户端域名代理
|
||||
func ProcessHost(c *Conn, s *TunnelModeServer) error {
|
||||
method, addr, rb, err := c.GetHost()
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return err
|
||||
}
|
||||
host, task, err := getKeyByHost(addr)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return err
|
||||
}
|
||||
de, en := getCompressType(task.Compress)
|
||||
link := s.bridge.GetTunnel(getverifyval(host.Vkey), en, de)
|
||||
if _, err := link.WriteHost(CONN_TCP, host.Target); err != nil {
|
||||
c.Close()
|
||||
link.Close()
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
if method == "CONNECT" {
|
||||
fmt.Fprint(c, "HTTP/1.1 200 Connection established\r\n")
|
||||
} else {
|
||||
link.WriteCompress(rb, en)
|
||||
}
|
||||
go relay(link, c, en)
|
||||
relay(c, link, de)
|
||||
return nil
|
||||
}
|
||||
|
||||
//web管理方式
|
||||
type WebServer struct {
|
||||
bridge *Tunnel
|
||||
}
|
||||
|
||||
//开始
|
||||
func (s *WebServer) Start() {
|
||||
InitFromCsv()
|
||||
p, _ := beego.AppConfig.Int("hostPort")
|
||||
t := &TaskList{
|
||||
TcpPort: p,
|
||||
Mode: "httpHostServer",
|
||||
Target: "",
|
||||
VerifyKey: "",
|
||||
U: "",
|
||||
P: "",
|
||||
Compress: "",
|
||||
Start: 1,
|
||||
IsRun: 0,
|
||||
ClientStatus: 0,
|
||||
}
|
||||
AddTask(t)
|
||||
beego.BConfig.WebConfig.Session.SessionOn = true
|
||||
log.Println("web管理启动,访问端口为",beego.AppConfig.String("httpport"))
|
||||
beego.Run()
|
||||
}
|
||||
|
||||
func NewWebServer(bridge *Tunnel) *WebServer {
|
||||
s := new(WebServer)
|
||||
s.bridge = bridge
|
||||
return s
|
||||
}
|
||||
|
||||
//host
|
||||
type HostServer struct {
|
||||
}
|
||||
|
||||
//开始
|
||||
func (s *HostServer) Start() error {
|
||||
return nil
|
||||
}
|
||||
func NewHostServer() *HostServer {
|
||||
s := new(HostServer)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *HostServer) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package main
|
||||
package lib
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -43,11 +44,15 @@ const (
|
|||
)
|
||||
|
||||
type Sock5ModeServer struct {
|
||||
Tunnel
|
||||
httpPort int
|
||||
u string //用户名
|
||||
p string //密码
|
||||
isVerify bool
|
||||
bridge *Tunnel
|
||||
httpPort int
|
||||
u string //用户名
|
||||
p string //密码
|
||||
enCompress int
|
||||
deCompress int
|
||||
isVerify bool
|
||||
listener net.Listener
|
||||
vKey string
|
||||
}
|
||||
|
||||
func (s *Sock5ModeServer) handleRequest(c net.Conn) {
|
||||
|
|
@ -131,7 +136,7 @@ func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) (proxyConn *Conn,
|
|||
binary.Read(c, binary.BigEndian, &port)
|
||||
// connect to host
|
||||
addr := net.JoinHostPort(host, strconv.Itoa(int(port)))
|
||||
client := s.GetTunnel()
|
||||
client := s.bridge.GetTunnel(getverifyval(s.vKey),s.enCompress,s.deCompress)
|
||||
s.sendReply(c, succeeded)
|
||||
var ltype string
|
||||
if command == associateMethod {
|
||||
|
|
@ -149,8 +154,8 @@ func (s *Sock5ModeServer) handleConnect(c net.Conn) {
|
|||
log.Println(err)
|
||||
c.Close()
|
||||
} else {
|
||||
go relay(proxyConn, NewConn(c), DataEncode)
|
||||
go relay(NewConn(c), proxyConn, DataDecode)
|
||||
go relay(proxyConn, NewConn(c), s.enCompress)
|
||||
go relay(NewConn(c), proxyConn, s.deCompress)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -182,8 +187,8 @@ func (s *Sock5ModeServer) handleUDP(c net.Conn) {
|
|||
if err != nil {
|
||||
c.Close()
|
||||
} else {
|
||||
go relay(proxyConn, NewConn(c), DataEncode)
|
||||
go relay(NewConn(c), proxyConn, DataDecode)
|
||||
go relay(proxyConn, NewConn(c), s.enCompress)
|
||||
go relay(NewConn(c), proxyConn, s.deCompress)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -258,27 +263,32 @@ func (s *Sock5ModeServer) Auth(c net.Conn) error {
|
|||
return errors.New("未知错误")
|
||||
}
|
||||
|
||||
func (s *Sock5ModeServer) Start() {
|
||||
l, err := net.Listen("tcp", ":"+strconv.Itoa(s.httpPort))
|
||||
func (s *Sock5ModeServer) Start() error {
|
||||
s.listener, err = net.Listen("tcp", ":"+strconv.Itoa(s.httpPort))
|
||||
if err != nil {
|
||||
log.Fatal("listen error: ", err)
|
||||
return err
|
||||
}
|
||||
s.StartTunnel()
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
conn, err := s.listener.Accept()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "use of closed network connection") {
|
||||
break
|
||||
}
|
||||
log.Fatal("accept error: ", err)
|
||||
}
|
||||
go s.handleNewConn(conn)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewSock5ModeServer(tcpPort, httpPort int, u, p string) *Sock5ModeServer {
|
||||
func (s *Sock5ModeServer) Close() error {
|
||||
return s.listener.Close()
|
||||
}
|
||||
|
||||
func NewSock5ModeServer(httpPort int, u, p string, brige *Tunnel, enCompress int, deCompress int, vKey string) *Sock5ModeServer {
|
||||
s := new(Sock5ModeServer)
|
||||
s.tunnelPort = tcpPort
|
||||
s.httpPort = httpPort
|
||||
s.tunnelList = make(chan *Conn, 1000)
|
||||
s.signalList = make(chan *Conn, 10)
|
||||
s.bridge = brige
|
||||
if u != "" && p != "" {
|
||||
s.isVerify = true
|
||||
s.u = u
|
||||
|
|
@ -286,5 +296,8 @@ func NewSock5ModeServer(tcpPort, httpPort int, u, p string) *Sock5ModeServer {
|
|||
} else {
|
||||
s.isVerify = false
|
||||
}
|
||||
s.enCompress = enCompress
|
||||
s.deCompress = deCompress
|
||||
s.vKey = vKey
|
||||
return s
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
package lib
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type list struct {
|
||||
connList chan *Conn
|
||||
}
|
||||
|
||||
func (l *list) Add(c *Conn) {
|
||||
l.connList <- c
|
||||
}
|
||||
|
||||
func (l *list) Pop() *Conn {
|
||||
return <-l.connList
|
||||
}
|
||||
func (l *list) Len() int {
|
||||
return len(l.connList)
|
||||
}
|
||||
|
||||
func newList() *list {
|
||||
l := new(list)
|
||||
l.connList = make(chan *Conn, 100)
|
||||
return l
|
||||
}
|
||||
|
||||
type Tunnel struct {
|
||||
tunnelPort int //通信隧道端口
|
||||
listener *net.TCPListener //server端监听
|
||||
signalList map[string]*list //通信
|
||||
tunnelList map[string]*list //隧道
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func newTunnel(tunnelPort int) *Tunnel {
|
||||
t := new(Tunnel)
|
||||
t.tunnelPort = tunnelPort
|
||||
t.signalList = make(map[string]*list)
|
||||
t.tunnelList = make(map[string]*list)
|
||||
return t
|
||||
}
|
||||
|
||||
func (s *Tunnel) StartTunnel() error {
|
||||
var err error
|
||||
s.listener, err = net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), s.tunnelPort, ""})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go s.tunnelProcess()
|
||||
return nil
|
||||
}
|
||||
|
||||
//tcp server
|
||||
func (s *Tunnel) tunnelProcess() error {
|
||||
var err error
|
||||
for {
|
||||
conn, err := s.listener.Accept()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
go s.cliProcess(NewConn(conn))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
//验证失败,返回错误验证flag,并且关闭连接
|
||||
func (s *Tunnel) verifyError(c *Conn) {
|
||||
c.conn.Write([]byte(VERIFY_EER))
|
||||
c.conn.Close()
|
||||
}
|
||||
|
||||
func (s *Tunnel) cliProcess(c *Conn) error {
|
||||
c.conn.(*net.TCPConn).SetReadDeadline(time.Now().Add(time.Duration(5) * time.Second))
|
||||
vval := make([]byte, 32)
|
||||
if _, err := c.conn.Read(vval); err != nil {
|
||||
log.Println("客户端读超时。客户端地址为::", c.conn.RemoteAddr())
|
||||
c.conn.Close()
|
||||
return err
|
||||
}
|
||||
//TODO:暂时取消
|
||||
if !verify(string(vval)) {
|
||||
log.Println("当前客户端连接校验错误,关闭此客户端:", c.conn.RemoteAddr())
|
||||
s.verifyError(c)
|
||||
return err
|
||||
}
|
||||
c.conn.(*net.TCPConn).SetReadDeadline(time.Time{})
|
||||
//做一个判断 添加到对应的channel里面以供使用
|
||||
if flag, err := c.ReadFlag(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return s.typeDeal(flag, c, string(vval))
|
||||
}
|
||||
}
|
||||
|
||||
//tcp连接类型区分
|
||||
func (s *Tunnel) typeDeal(typeVal string, c *Conn, cFlag string) error {
|
||||
switch typeVal {
|
||||
case WORK_MAIN:
|
||||
s.addList(s.signalList, c, cFlag)
|
||||
case WORK_CHAN:
|
||||
s.addList(s.tunnelList, c, cFlag)
|
||||
default:
|
||||
return errors.New("无法识别")
|
||||
}
|
||||
c.SetAlive()
|
||||
return nil
|
||||
}
|
||||
|
||||
//加到对应的list中
|
||||
func (s *Tunnel) addList(m map[string]*list, c *Conn, cFlag string) {
|
||||
s.Lock()
|
||||
if v, ok := m[cFlag]; ok {
|
||||
v.Add(c)
|
||||
} else {
|
||||
l := newList()
|
||||
l.Add(c)
|
||||
m[cFlag] = l
|
||||
}
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
//新建隧道
|
||||
func (s *Tunnel) newChan(cFlag string) {
|
||||
s.wait(s.signalList, cFlag)
|
||||
retry:
|
||||
connPass := s.signalList[cFlag].Pop()
|
||||
_, err := connPass.conn.Write([]byte("chan"))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
goto retry
|
||||
}
|
||||
s.signalList[cFlag].Add(connPass)
|
||||
}
|
||||
|
||||
//得到一个tcp隧道
|
||||
func (s *Tunnel) GetTunnel(cFlag string, en, de int) *Conn {
|
||||
if v, ok := s.tunnelList[cFlag]; !ok || v.Len() < 10 { //新建通道
|
||||
go s.newChan(cFlag)
|
||||
}
|
||||
retry:
|
||||
s.wait(s.tunnelList, cFlag)
|
||||
c := s.tunnelList[cFlag].Pop()
|
||||
if _, err := c.wTest(); err != nil {
|
||||
c.Close()
|
||||
goto retry
|
||||
}
|
||||
c.WriteCompressType(en, de)
|
||||
return c
|
||||
}
|
||||
|
||||
//得到一个通信通道
|
||||
func (s *Tunnel) GetSignal(cFlag string) (err error, conn *Conn) {
|
||||
if v, ok := s.signalList[cFlag]; !ok || v.Len() == 0 {
|
||||
err = errors.New("客户端未连接")
|
||||
return
|
||||
}
|
||||
conn = s.signalList[cFlag].Pop()
|
||||
return
|
||||
}
|
||||
|
||||
//重回slice 复用
|
||||
func (s *Tunnel) ReturnSignal(conn *Conn, cFlag string) {
|
||||
if v, ok := s.signalList[cFlag]; ok {
|
||||
v.Add(conn)
|
||||
}
|
||||
}
|
||||
|
||||
//等待
|
||||
func (s *Tunnel) wait(m map[string]*list, cFlag string) {
|
||||
ticker := time.NewTicker(time.Millisecond * 100)
|
||||
for {
|
||||
<-ticker.C
|
||||
if _, ok := m[cFlag]; ok {
|
||||
ticker.Stop()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UdpModeServer struct {
|
||||
bridge *Tunnel
|
||||
udpPort int //监听的udp端口
|
||||
tunnelTarget string //udp目标地址
|
||||
listener *net.UDPConn
|
||||
udpMap map[string]*Conn
|
||||
enCompress int
|
||||
deCompress int
|
||||
vKey string
|
||||
}
|
||||
|
||||
func NewUdpModeServer(udpPort int, tunnelTarget string, bridge *Tunnel, enCompress int, deCompress int, vKey string) *UdpModeServer {
|
||||
s := new(UdpModeServer)
|
||||
s.udpPort = udpPort
|
||||
s.tunnelTarget = tunnelTarget
|
||||
s.bridge = bridge
|
||||
s.udpMap = make(map[string]*Conn)
|
||||
s.enCompress = enCompress
|
||||
s.deCompress = deCompress
|
||||
s.vKey = vKey
|
||||
return s
|
||||
}
|
||||
|
||||
//开始
|
||||
func (s *UdpModeServer) Start() error {
|
||||
s.listener, err = net.ListenUDP("udp", &net.UDPAddr{net.ParseIP("0.0.0.0"), s.udpPort, ""})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data := make([]byte, 1472) //udp数据包大小
|
||||
for {
|
||||
n, addr, err := s.listener.ReadFromUDP(data)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "use of closed network connection") {
|
||||
break
|
||||
}
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
go s.process(addr, data[:n])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//TODO:效率问题有待解决
|
||||
func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) {
|
||||
fmt.Println(addr.String())
|
||||
fmt.Println(string(data))
|
||||
conn := s.bridge.GetTunnel(getverifyval(s.vKey),s.enCompress,s.deCompress)
|
||||
if _, err := conn.WriteHost(CONN_UDP, s.tunnelTarget); err != nil {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
conn.WriteCompress(data, s.enCompress)
|
||||
go func(addr *net.UDPAddr, conn *Conn) {
|
||||
buf := make([]byte, 1024)
|
||||
conn.conn.SetReadDeadline(time.Now().Add(time.Duration(time.Second * 3)))
|
||||
n, err := conn.ReadFromCompress(buf, s.deCompress)
|
||||
if err != nil || err == io.EOF {
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
s.listener.WriteToUDP(buf[:n], addr)
|
||||
conn.Close()
|
||||
}(addr, conn)
|
||||
}
|
||||
|
||||
func (s *UdpModeServer) Close() error {
|
||||
return s.listener.Close()
|
||||
}
|
||||
|
|
@ -1,20 +1,27 @@
|
|||
package main
|
||||
package lib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/golang/snappy"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -130,6 +137,7 @@ func relay(in, out *Conn, compressType int) {
|
|||
buf := make([]byte, 32*1024)
|
||||
switch compressType {
|
||||
case COMPRESS_GZIP_ENCODE:
|
||||
//TODO:GZIP压缩存在问题有待解决
|
||||
w := gzip.NewWriter(in)
|
||||
for {
|
||||
n, err := out.Read(buf)
|
||||
|
|
@ -146,32 +154,140 @@ func relay(in, out *Conn, compressType int) {
|
|||
}
|
||||
w.Close()
|
||||
case COMPRESS_SNAPY_ENCODE:
|
||||
w := snappy.NewBufferedWriter(in)
|
||||
for {
|
||||
n, err := out.Read(buf)
|
||||
if err != nil || err == io.EOF {
|
||||
break
|
||||
}
|
||||
if _, err = w.Write(buf[:n]); err != nil {
|
||||
break
|
||||
}
|
||||
if err = w.Flush(); err != nil {
|
||||
log.Println(err)
|
||||
break
|
||||
}
|
||||
}
|
||||
w.Close()
|
||||
io.Copy(NewSnappyConn(in.conn), out)
|
||||
case COMPRESS_GZIP_DECODE:
|
||||
r, err := gzip.NewReader(out)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
io.Copy(in, r)
|
||||
io.Copy(in, NewGzipConn(out.conn))
|
||||
case COMPRESS_SNAPY_DECODE:
|
||||
r := snappy.NewReader(out)
|
||||
io.Copy(in, r)
|
||||
io.Copy(in, NewSnappyConn(out.conn))
|
||||
default:
|
||||
io.Copy(in, out)
|
||||
}
|
||||
out.Close()
|
||||
in.Close()
|
||||
}
|
||||
|
||||
type Site struct {
|
||||
Host string
|
||||
Url string
|
||||
Port int
|
||||
}
|
||||
type Config struct {
|
||||
SiteList []Site
|
||||
Replace int
|
||||
}
|
||||
type JsonStruct struct {
|
||||
}
|
||||
|
||||
func NewJsonStruct() *JsonStruct {
|
||||
return &JsonStruct{}
|
||||
}
|
||||
func (jst *JsonStruct) Load(filename string) (Config, error) {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
config := Config{}
|
||||
if err != nil {
|
||||
return config, errors.New("配置文件打开错误")
|
||||
}
|
||||
err = json.Unmarshal(data, &config)
|
||||
if err != nil {
|
||||
return config, errors.New("配置文件解析错误")
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
//判断压缩方式
|
||||
func getCompressType(compress string) (int, int) {
|
||||
switch compress {
|
||||
case "":
|
||||
return COMPRESS_NONE, COMPRESS_NONE
|
||||
case "gzip":
|
||||
return COMPRESS_GZIP_DECODE, COMPRESS_GZIP_ENCODE
|
||||
case "snappy":
|
||||
return COMPRESS_SNAPY_DECODE, COMPRESS_SNAPY_ENCODE
|
||||
default:
|
||||
log.Fatalln("数据压缩格式错误")
|
||||
}
|
||||
return COMPRESS_NONE, COMPRESS_NONE
|
||||
}
|
||||
|
||||
// 简单的一个校验值
|
||||
func getverifyval(vkey string) string {
|
||||
//单客户端模式
|
||||
if *verifyKey != "" {
|
||||
return Md5(*verifyKey)
|
||||
}
|
||||
return Md5(vkey)
|
||||
}
|
||||
|
||||
func verify(verifyKeyMd5 string) bool {
|
||||
if getverifyval(*verifyKey) == verifyKeyMd5 {
|
||||
return true
|
||||
}
|
||||
if *verifyKey == "" {
|
||||
for _, v := range CsvDb.Tasks {
|
||||
if _, ok := RunList[v.VerifyKey]; getverifyval(v.VerifyKey) == verifyKeyMd5 && ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getKeyByHost(host string) (h *HostList, t *TaskList, err error) {
|
||||
for _, v := range CsvDb.Hosts {
|
||||
if strings.Contains(host, v.Host) {
|
||||
h = v
|
||||
t, err = CsvDb.GetTask(v.Vkey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
err = errors.New("未找到host对应的内网目标")
|
||||
return
|
||||
}
|
||||
|
||||
//生成32位md5字串
|
||||
func Md5(s string) string {
|
||||
h := md5.New()
|
||||
h.Write([]byte(s))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
//生成随机验证密钥
|
||||
func GetRandomString(l int) string {
|
||||
str := "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||
bytes := []byte(str)
|
||||
result := []byte{}
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
for i := 0; i < l; i++ {
|
||||
result = append(result, bytes[r.Intn(len(bytes))])
|
||||
}
|
||||
return string(result)
|
||||
}
|
||||
|
||||
func Gethostbyname(hostname string) string {
|
||||
if !DomainCheck(hostname) {
|
||||
return hostname
|
||||
}
|
||||
ips, _ := net.LookupIP(hostname)
|
||||
if ips != nil {
|
||||
for _, v := range ips {
|
||||
if v.To4() != nil {
|
||||
return v.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func DomainCheck(domain string) bool {
|
||||
var match bool
|
||||
IsLine := "^((http://)|(https://))?([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}(/)"
|
||||
NotLine := "^((http://)|(https://))?([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}"
|
||||
match, _ = regexp.MatchString(IsLine, domain)
|
||||
if !match {
|
||||
match, _ = regexp.MatchString(NotLine, domain)
|
||||
}
|
||||
return match
|
||||
}
|
||||
77
main.go
77
main.go
|
|
@ -1,81 +1,10 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
var (
|
||||
configPath = flag.String("config", "config.json", "配置文件路径")
|
||||
tcpPort = flag.Int("tcpport", 8284, "Socket连接或者监听的端口")
|
||||
httpPort = flag.Int("httpport", 8024, "当mode为server时为服务端监听端口,当为mode为client时为转发至本地客户端的端口")
|
||||
rpMode = flag.String("mode", "client", "启动模式,可选为client|server")
|
||||
tunnelTarget = flag.String("target", "10.1.50.203:80", "远程目标")
|
||||
verifyKey = flag.String("vkey", "", "验证密钥")
|
||||
u = flag.String("u", "", "socks5验证用户名")
|
||||
p = flag.String("p", "", "socks5验证密码")
|
||||
compress = flag.String("compress", "", "数据压缩(gzip|snappy)")
|
||||
config Config
|
||||
err error
|
||||
DataEncode int
|
||||
DataDecode int
|
||||
"github.com/cnlh/easyProxy/lib"
|
||||
_ "github.com/cnlh/easyProxy/routers"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||
switch *compress {
|
||||
case "":
|
||||
DataDecode = COMPRESS_NONE
|
||||
DataEncode = COMPRESS_NONE
|
||||
case "gzip":
|
||||
DataDecode = COMPRESS_GZIP_DECODE
|
||||
DataEncode = COMPRESS_GZIP_ENCODE
|
||||
case "snnapy":
|
||||
DataDecode = COMPRESS_SNAPY_DECODE
|
||||
DataEncode = COMPRESS_SNAPY_ENCODE
|
||||
default:
|
||||
log.Fatalln("数据压缩格式错误")
|
||||
}
|
||||
if *rpMode == "client" {
|
||||
JsonParse := NewJsonStruct()
|
||||
config, err = JsonParse.Load(*configPath)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
*verifyKey = config.Server.Vkey
|
||||
log.Println("客户端启动,连接:", config.Server.Ip, ", 端口:", config.Server.Tcp)
|
||||
cli := NewRPClient(fmt.Sprintf("%s:%d", config.Server.Ip, config.Server.Tcp), config.Server.Num)
|
||||
cli.Start()
|
||||
} else {
|
||||
if *verifyKey == "" {
|
||||
log.Fatalln("必须输入一个验证的key")
|
||||
}
|
||||
if *tcpPort <= 0 || *tcpPort >= 65536 {
|
||||
log.Fatalln("请输入正确的tcp端口。")
|
||||
}
|
||||
if *httpPort <= 0 || *httpPort >= 65536 {
|
||||
log.Fatalln("请输入正确的http端口。")
|
||||
}
|
||||
log.Println("服务端启动,监听tcp服务端端口:", *tcpPort, ", 外部服务端端口:", *httpPort)
|
||||
if *rpMode == "httpServer" {
|
||||
svr := NewHttpModeServer(*tcpPort, *httpPort)
|
||||
if err := svr.Start(); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
} else if *rpMode == "tunnelServer" {
|
||||
svr := NewTunnelModeServer(*tcpPort, *httpPort, *tunnelTarget, ProcessTunnel)
|
||||
svr.Start()
|
||||
} else if *rpMode == "sock5Server" {
|
||||
svr := NewSock5ModeServer(*tcpPort, *httpPort, *u, *p)
|
||||
svr.Start()
|
||||
} else if *rpMode == "httpProxyServer" {
|
||||
svr := NewTunnelModeServer(*tcpPort, *httpPort, *tunnelTarget, ProcessHttp)
|
||||
svr.Start()
|
||||
} else if *rpMode == "udpServer" {
|
||||
svr := NewUdpModeServer(*tcpPort, *httpPort, *tunnelTarget)
|
||||
svr.Start()
|
||||
}
|
||||
}
|
||||
lib.InitMode()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package routers
|
||||
|
||||
import (
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/cnlh/easyProxy/controllers"
|
||||
)
|
||||
|
||||
func init() {
|
||||
beego.Router("/", &controllers.IndexController{}, "*:Index")
|
||||
beego.AutoRouter(&controllers.IndexController{})
|
||||
beego.AutoRouter(&controllers.LoginController{})
|
||||
}
|
||||
212
server.go
212
server.go
|
|
@ -1,212 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
VERIFY_EER = "vkey"
|
||||
WORK_MAIN = "main"
|
||||
WORK_CHAN = "chan"
|
||||
RES_SIGN = "sign"
|
||||
RES_MSG = "msg0"
|
||||
TEST_FLAG = "tst"
|
||||
CONN_TCP = "tcp"
|
||||
CONN_UDP = "udp"
|
||||
)
|
||||
|
||||
type HttpModeServer struct {
|
||||
Tunnel
|
||||
httpPort int
|
||||
}
|
||||
|
||||
func NewHttpModeServer(tcpPort, httpPort int) *HttpModeServer {
|
||||
s := new(HttpModeServer)
|
||||
s.tunnelPort = tcpPort
|
||||
s.httpPort = httpPort
|
||||
s.signalList = make(chan *Conn, 1000)
|
||||
return s
|
||||
}
|
||||
|
||||
//开始
|
||||
func (s *HttpModeServer) Start() (error) {
|
||||
err := s.StartTunnel()
|
||||
if err != nil {
|
||||
log.Fatalln("开启客户端失败!", err)
|
||||
return err
|
||||
}
|
||||
s.startHttpServer()
|
||||
return nil
|
||||
}
|
||||
|
||||
//开启http端口监听
|
||||
func (s *HttpModeServer) startHttpServer() {
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
retry:
|
||||
if len(s.signalList) == 0 {
|
||||
BadRequest(w)
|
||||
return
|
||||
}
|
||||
conn := <-s.signalList
|
||||
if err := s.writeRequest(r, conn); err != nil {
|
||||
log.Println(err)
|
||||
conn.Close()
|
||||
goto retry
|
||||
return
|
||||
}
|
||||
err = s.writeResponse(w, conn)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
conn.Close()
|
||||
goto retry
|
||||
return
|
||||
}
|
||||
s.signalList <- conn
|
||||
})
|
||||
log.Fatalln(http.ListenAndServe(fmt.Sprintf(":%d", s.httpPort), nil))
|
||||
}
|
||||
|
||||
//req转为bytes发送给client端
|
||||
func (s *HttpModeServer) writeRequest(r *http.Request, conn *Conn) error {
|
||||
raw, err := EncodeRequest(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
conn.wSign()
|
||||
c, err := conn.WriteCompress(raw, DataEncode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c != len(raw) {
|
||||
return errors.New("写出长度与字节长度不一致。")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//从client读取出Response
|
||||
func (s *HttpModeServer) writeResponse(w http.ResponseWriter, c *Conn) error {
|
||||
flags, err := c.ReadFlag()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch flags {
|
||||
case RES_SIGN:
|
||||
buf := make([]byte, 1024*32)
|
||||
n, err := c.ReadFromCompress(buf, DataDecode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp, err := DecodeResponse(buf[:n])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range resp.Header {
|
||||
for _, v2 := range v {
|
||||
w.Header().Set(k, v2)
|
||||
}
|
||||
}
|
||||
w.WriteHeader(resp.StatusCode)
|
||||
w.Write(bodyBytes)
|
||||
case RES_MSG:
|
||||
BadRequest(w)
|
||||
return errors.New("客户端请求出错")
|
||||
default:
|
||||
BadRequest(w)
|
||||
return errors.New("无法解析此错误")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type process func(c *Conn, s *TunnelModeServer) error
|
||||
type TunnelModeServer struct {
|
||||
Tunnel
|
||||
httpPort int
|
||||
tunnelTarget string
|
||||
process process
|
||||
}
|
||||
|
||||
func NewTunnelModeServer(tcpPort, httpPort int, tunnelTarget string, process process) *TunnelModeServer {
|
||||
s := new(TunnelModeServer)
|
||||
s.tunnelPort = tcpPort
|
||||
s.httpPort = httpPort
|
||||
s.tunnelTarget = tunnelTarget
|
||||
s.tunnelList = make(chan *Conn, 1000)
|
||||
s.signalList = make(chan *Conn, 10)
|
||||
s.process = process
|
||||
return s
|
||||
}
|
||||
|
||||
//开始
|
||||
func (s *TunnelModeServer) Start() (error) {
|
||||
err := s.StartTunnel()
|
||||
if err != nil {
|
||||
log.Fatalln("开启客户端失败!", err)
|
||||
return err
|
||||
}
|
||||
s.startTunnelServer()
|
||||
return nil
|
||||
}
|
||||
|
||||
//隧道模式server
|
||||
func (s *TunnelModeServer) startTunnelServer() {
|
||||
listener, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), s.httpPort, ""})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
for {
|
||||
conn, err := listener.AcceptTCP()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
go s.process(NewConn(conn), s)
|
||||
}
|
||||
}
|
||||
|
||||
//TODO:这种实现方式……
|
||||
//tcp隧道模式
|
||||
func ProcessTunnel(c *Conn, s *TunnelModeServer) error {
|
||||
link := s.GetTunnel()
|
||||
if _, err := link.WriteHost(CONN_TCP, s.tunnelTarget); err != nil {
|
||||
link.Close()
|
||||
c.Close()
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
go relay(link, c, DataEncode)
|
||||
relay(c, link, DataDecode)
|
||||
return nil
|
||||
}
|
||||
|
||||
//http代理模式
|
||||
func ProcessHttp(c *Conn, s *TunnelModeServer) error {
|
||||
method, addr, rb, err := c.GetHost()
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return err
|
||||
}
|
||||
link := s.GetTunnel()
|
||||
if _, err := link.WriteHost("tcp", addr); err != nil {
|
||||
c.Close()
|
||||
link.Close()
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
if method == "CONNECT" {
|
||||
fmt.Fprint(c, "HTTP/1.1 200 Connection established\r\n")
|
||||
} else {
|
||||
link.WriteCompress(rb, DataEncode)
|
||||
}
|
||||
go relay(link, c, DataEncode)
|
||||
relay(c, link, DataDecode)
|
||||
return nil
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 434 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,27 @@
|
|||
(function () {
|
||||
"use strict";
|
||||
|
||||
var treeviewMenu = $('.app-menu');
|
||||
|
||||
// Toggle Sidebar
|
||||
$('[data-toggle="sidebar"]').click(function(event) {
|
||||
event.preventDefault();
|
||||
$('.app').toggleClass('sidenav-toggled');
|
||||
});
|
||||
|
||||
// Activate sidebar treeview toggle
|
||||
$("[data-toggle='treeview']").click(function(event) {
|
||||
event.preventDefault();
|
||||
if(!$(this).parent().hasClass('is-expanded')) {
|
||||
treeviewMenu.find("[data-toggle='treeview']").parent().removeClass('is-expanded');
|
||||
}
|
||||
$(this).parent().toggleClass('is-expanded');
|
||||
});
|
||||
|
||||
// Set initial active toggle
|
||||
$("[data-toggle='treeview.'].is-expanded").parent().toggleClass('is-expanded');
|
||||
|
||||
//Activate bootstrip tooltips
|
||||
// $("[data-toggle='tooltip']").tooltip();
|
||||
|
||||
})();
|
||||
111
tunnel.go
111
tunnel.go
|
|
@ -1,111 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Tunnel struct {
|
||||
tunnelPort int //通信隧道端口
|
||||
listener *net.TCPListener //server端监听
|
||||
signalList chan *Conn //通信
|
||||
tunnelList chan *Conn //隧道
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *Tunnel) StartTunnel() error {
|
||||
var err error
|
||||
s.listener, err = net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), s.tunnelPort, ""})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
go s.tunnelProcess()
|
||||
return nil
|
||||
}
|
||||
|
||||
//tcp server
|
||||
func (s *Tunnel) tunnelProcess() error {
|
||||
var err error
|
||||
for {
|
||||
conn, err := s.listener.Accept()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
go s.cliProcess(NewConn(conn))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
//验证失败,返回错误验证flag,并且关闭连接
|
||||
func (s *Tunnel) verifyError(c *Conn) {
|
||||
c.conn.Write([]byte(VERIFY_EER))
|
||||
c.conn.Close()
|
||||
}
|
||||
|
||||
func (s *Tunnel) cliProcess(c *Conn) error {
|
||||
c.conn.(*net.TCPConn).SetReadDeadline(time.Now().Add(time.Duration(5) * time.Second))
|
||||
vval := make([]byte, 20)
|
||||
_, err := c.conn.Read(vval)
|
||||
if err != nil {
|
||||
log.Println("客户端读超时。客户端地址为::", c.conn.RemoteAddr())
|
||||
c.conn.Close()
|
||||
return err
|
||||
}
|
||||
if bytes.Compare(vval, getverifyval()[:]) != 0 {
|
||||
log.Println("当前客户端连接校验错误,关闭此客户端:", c.conn.RemoteAddr())
|
||||
s.verifyError(c)
|
||||
return err
|
||||
}
|
||||
c.conn.(*net.TCPConn).SetReadDeadline(time.Time{})
|
||||
//做一个判断 添加到对应的channel里面以供使用
|
||||
flag, err := c.ReadFlag()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.typeDeal(flag, c)
|
||||
}
|
||||
|
||||
//tcp连接类型区分
|
||||
func (s *Tunnel) typeDeal(typeVal string, c *Conn) error {
|
||||
switch typeVal {
|
||||
case WORK_MAIN:
|
||||
s.signalList <- c
|
||||
case WORK_CHAN:
|
||||
s.tunnelList <- c
|
||||
default:
|
||||
return errors.New("无法识别")
|
||||
}
|
||||
c.SetAlive()
|
||||
return nil
|
||||
}
|
||||
|
||||
//新建隧道
|
||||
func (s *Tunnel) newChan() {
|
||||
retry:
|
||||
connPass := <-s.signalList
|
||||
_, err := connPass.conn.Write([]byte("chan"))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
goto retry
|
||||
}
|
||||
s.signalList <- connPass
|
||||
}
|
||||
|
||||
func (s *Tunnel) GetTunnel() *Conn {
|
||||
if len(s.tunnelList) < 10 { //新建通道
|
||||
go s.newChan()
|
||||
}
|
||||
retry:
|
||||
c := <-s.tunnelList
|
||||
_, err := c.wTest()
|
||||
if err != nil {
|
||||
c.Close()
|
||||
goto retry
|
||||
}
|
||||
return c
|
||||
}
|
||||
83
udp.go
83
udp.go
|
|
@ -1,83 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
type UdpModeServer struct {
|
||||
Tunnel
|
||||
udpPort int //监听的udp端口
|
||||
tunnelTarget string //udp目标地址
|
||||
listener *net.UDPConn
|
||||
udpMap map[string]*Conn
|
||||
}
|
||||
|
||||
func NewUdpModeServer(tcpPort, udpPort int, tunnelTarget string) *UdpModeServer {
|
||||
s := new(UdpModeServer)
|
||||
s.tunnelPort = tcpPort
|
||||
s.udpPort = udpPort
|
||||
s.tunnelTarget = tunnelTarget
|
||||
s.tunnelList = make(chan *Conn, 1000)
|
||||
s.signalList = make(chan *Conn, 10)
|
||||
s.udpMap = make(map[string]*Conn)
|
||||
return s
|
||||
}
|
||||
|
||||
//开始
|
||||
func (s *UdpModeServer) Start() (error) {
|
||||
err := s.StartTunnel()
|
||||
if err != nil {
|
||||
log.Fatalln("启动失败!", err)
|
||||
return err
|
||||
}
|
||||
s.startTunnelServer()
|
||||
return nil
|
||||
}
|
||||
|
||||
//udp监听
|
||||
func (s *UdpModeServer) startTunnelServer() {
|
||||
s.listener, err = net.ListenUDP("udp", &net.UDPAddr{net.ParseIP("0.0.0.0"), s.udpPort, ""})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
data := make([]byte, 1472) //udp数据包大小
|
||||
for {
|
||||
n, addr, err := s.listener.ReadFromUDP(data)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
go s.process(addr, data[:n])
|
||||
}
|
||||
}
|
||||
|
||||
func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) {
|
||||
conn := s.GetTunnel()
|
||||
if _, err := conn.WriteHost(CONN_UDP, s.tunnelTarget);err!=nil{
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
buf := make([]byte, 1024)
|
||||
conn.conn.SetReadDeadline(time.Now().Add(time.Duration(time.Second * 3)))
|
||||
n, err := conn.ReadFromCompress(buf, DataDecode)
|
||||
if err != nil || err == io.EOF {
|
||||
conn.Close()
|
||||
break
|
||||
}
|
||||
_, err = s.listener.WriteToUDP(buf[:n], addr)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}()
|
||||
if _, err = conn.WriteCompress(data, DataEncode); err != nil {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
12
verify.go
12
verify.go
|
|
@ -1,12 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 简单的一个校验值
|
||||
func getverifyval() []byte {
|
||||
b := sha1.Sum([]byte(time.Now().Format("2006-01-02 15") + *verifyKey))
|
||||
return b[:]
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<div class="row tile">
|
||||
<div class="col-lg-12">
|
||||
<div class="bs-component">
|
||||
<div class="alert alert-dismissible alert-success">
|
||||
<button class="close" type="button" data-dismiss="alert">×</button>
|
||||
<span id="info"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-md-auto">
|
||||
<div>
|
||||
<h3 class="tile-title">添加</h3>
|
||||
<div class="tile-body">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label class="control-label">模式</label>
|
||||
<select class="form-control" name="type" id="type">
|
||||
<option {{if eq "tunnelServer" .type}}selected{{end}} value="tunnelServer">tcp隧道</option>
|
||||
<option {{if eq "udpServer" .type}}selected{{end}} value="udpServer">udp隧道</option>
|
||||
<option {{if eq "sock5Server" .type}}selected{{end}} value="sock5Server">socks5代理</option>
|
||||
<option {{if eq "httpProxyServer" .type}}selected{{end}} value="httpProxyServer">http代理
|
||||
<option {{if eq "hostServer" .type}}selected{{end}} value="hostServer">host客户端</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="port">
|
||||
<label class="control-label">监听的端口</label>
|
||||
<input class="form-control" type="text" name="port" placeholder="公网服务器对外访问端口,例如8024">
|
||||
</div>
|
||||
<div class="form-group" id="target">
|
||||
<label class="control-label">内网目标(ip:端口)</label>
|
||||
<input class="form-control" type="text" name="target" placeholder="内网代理地址,例如10.1.50.203:22">
|
||||
</div>
|
||||
<div class="form-group" id="compress">
|
||||
<label class="control-label">数据压缩方式</label>
|
||||
<select class="form-control" name="compress">
|
||||
<option value="">不压缩</option>
|
||||
<option value="gzip">gzip压缩</option>
|
||||
<option value="snappy">snappy</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="u">
|
||||
<label class="control-label">socks5代理模式验证用户名</label>
|
||||
<input class="form-control" type="text" name="u" placeholder="不填则无需验证">
|
||||
</div>
|
||||
<div class="form-group" id="p">
|
||||
<label class="control-label">socks5代理模式验证密码</label>
|
||||
<input class="form-control" type="text" name="p" placeholder="不填则无需验证">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tile-footer">
|
||||
<button class="btn btn-success" href="#" id="add"><i
|
||||
class="fa fa-fw fa-lg fa-eye"></i>添加
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<script>
|
||||
var arr = []
|
||||
arr["all"] = ["type", "port", "compress", "u", "p", "target"]
|
||||
arr["tunnelServer"] = ["type", "port", "target", "compress", "tcp隧道模式,提供一条tcp隧道,适用于ssh、远程桌面等,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,访问公网服务器的设定端口,则相当于访问内网目标地址的目标端口"]
|
||||
arr["udpServer"] = ["type", "port", "target", "compress", "udp隧道模式,提供一条udp隧道,适用于dns、内网dns访问等,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,访问公网服务器的设定端口,则相当于访问内网目标地址的udp目标端口"]
|
||||
arr["sock5Server"] = ["type", "port", "compress", "u", "p", "socks5代理模式,内网socks5代理,配合proxifer,可如同使用vpn一样访问内网设备或资源,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,在外网环境下本机配置socks5代理,即访问内网设备或者资源 "]
|
||||
arr["httpProxyServer"] = ["type", "port", "compress", " http代理模式,内网http代理,可访问内网网站,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,在外网环境下本机配置http代理,即访问内网站点"]
|
||||
arr["hostServer"] = ["type", "compress", "域名分发模式,使用域名代理内网服务,适用于小程序开发、公众号开发、站点演示等,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,使用nginx将请求反向代理到本程序,再进行域名配置,即可解析"]
|
||||
|
||||
function resetForm() {
|
||||
for (var i = 0; i < arr["all"].length; i++) {
|
||||
$("#" + arr["all"][i]).css("display", "none")
|
||||
}
|
||||
o = $("#type option:selected").val()
|
||||
for (var i = 0; i < arr[o].length - 1; i++) {
|
||||
$("#" + arr[o][i]).css("display", "block")
|
||||
}
|
||||
$("#info").html(arr[o][arr[o].length - 1])
|
||||
}
|
||||
|
||||
$(function () {
|
||||
resetForm()
|
||||
$("#type").on("change", function () {
|
||||
resetForm()
|
||||
})
|
||||
$("#add").on("click", function () {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/index/add",
|
||||
data: $("form").serializeArray(),
|
||||
success: function (res) {
|
||||
alert(res.msg)
|
||||
if (res.status) {
|
||||
history.back(-1)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
<div class="row tile">
|
||||
<div class="col-md-6 col-md-auto">
|
||||
<div>
|
||||
<h3 class="tile-title">添加</h3>
|
||||
<div class="tile-body">
|
||||
<form>
|
||||
<input type="hidden" name="vKey" value="{{.t.VerifyKey}}">
|
||||
<div class="form-group">
|
||||
<label class="control-label">模式</label>
|
||||
<select class="form-control" name="type" id="type">
|
||||
<option {{if eq "tunnelServer" .t.Mode}}selected{{end}} value="tunnelServer">tcp隧道</option>
|
||||
<option {{if eq "udpServer" .t.Mode}}selected{{end}} value="udpServer">udp隧道</option>
|
||||
<option {{if eq "sock5Server" .t.Mode}}selected{{end}} value="sock5Server">socks5代理</option>
|
||||
<option {{if eq "httpProxyServer" .t.Mode}}selected{{end}} value="httpProxyServer">http代理
|
||||
<option {{if eq "hostServer" .t.Mode}}selected{{end}} value="hostServer">host客户端</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="port">
|
||||
<label class="control-label">监听的端口</label>
|
||||
<input class="form-control" value="{{.t.TcpPort}}" type="text" name="port"
|
||||
placeholder="公网服务器对外访问端口,例如8024">
|
||||
</div>
|
||||
<div class="form-group" id="target">
|
||||
<label class="control-label">内网目标(仅tcp、udp隧道模式需填写)</label>
|
||||
<input class="form-control" value="{{.t.Target}}" type="text" name="target"
|
||||
placeholder="内网隧道目标,例如10.1.50.203:22">
|
||||
</div>
|
||||
<div class="form-group" id="compress">
|
||||
<label class="control-label">数据压缩方式(所有模式均支持)</label>
|
||||
<select class="form-control" name="compress">
|
||||
<option {{if eq "" .t.Compress}}selected{{end}} value="">不压缩</option>
|
||||
<option {{if eq "gzip" .t.Compress}}selected{{end}} value="gzip">gzip压缩</option>
|
||||
<option {{if eq "snappy" .t.Compress}}selected{{end}} value="snappy">snappy压缩</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group" id="u">
|
||||
<label class="control-label">socks5代理模式验证用户名</label>
|
||||
<input class="form-control" value="{{.t.U}}" type="text" name="u"
|
||||
placeholder="不填则无需验证,非socks5模式不填">
|
||||
</div>
|
||||
<div class="form-group" id="p">
|
||||
<label class="control-label">socks5代理模式验证密码</label>
|
||||
<input class="form-control" value="{{.t.P}}" type="text" name="p"
|
||||
placeholder="不填则无需验证,非socks5模式不填">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tile-footer">
|
||||
<button class="btn btn-success" href="#" id="add"><i
|
||||
class="fa fa-fw fa-lg fa-eye"></i>保存
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<script>
|
||||
var arr = []
|
||||
arr["all"] = ["type", "port", "compress", "u", "p","target"]
|
||||
arr["tunnelServer"] = ["type", "port", "target", "compress"]
|
||||
arr["udpServer"] = ["type", "port", "target", "compress"]
|
||||
arr["sock5Server"] = ["type", "port", "compress", "u", "p"]
|
||||
arr["httpProxyServer"] = ["type", "port", "compress"]
|
||||
arr["hostServer"] = ["type", "compress"]
|
||||
|
||||
function resetForm() {
|
||||
for (var i = 0; i < arr["all"].length; i++) {
|
||||
$("#" + arr["all"][i]).css("display", "none")
|
||||
}
|
||||
o = $("#type option:selected").val()
|
||||
for (var i = 0; i < arr[o].length; i++) {
|
||||
$("#" + arr[o][i]).css("display", "block")
|
||||
}
|
||||
}
|
||||
$(function () {
|
||||
resetForm()
|
||||
$("#type").on("change", function () {
|
||||
resetForm()
|
||||
})
|
||||
$("#add").on("click", function () {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/index/edit",
|
||||
data: $("form").serializeArray(),
|
||||
success: function (res) {
|
||||
alert(res.msg)
|
||||
if (res.status) {
|
||||
history.back(-1)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<div class="row tile">
|
||||
<div class="col-md-6 col-md-auto">
|
||||
<div>
|
||||
<h3 class="tile-title">添加</h3>
|
||||
<div class="tile-body">
|
||||
<form>
|
||||
<input type="hidden" name="vkey" value="{{.vkey}}">
|
||||
<div class="form-group">
|
||||
<label class="control-label">域名</label>
|
||||
<input class="form-control" type="text" name="host" placeholder="域名">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label">内网目标</label>
|
||||
<input class="form-control" type="text" name="target" placeholder="内网隧道目标,例如10.1.50.203:22">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="tile-footer">
|
||||
<button class="btn btn-success" href="#" id="add"><i
|
||||
class="fa fa-fw fa-lg fa-eye"></i>添加
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<script>
|
||||
$(function () {
|
||||
$("#add").on("click", function () {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/index/addhost",
|
||||
data: $("form").serializeArray(),
|
||||
success: function (res) {
|
||||
alert(res.msg)
|
||||
if (res.status) {
|
||||
history.back(-1)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="tile">
|
||||
<div class="tile-body">
|
||||
<table class="table table-hover table-bordered" id="sampleTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>客户端key</th>
|
||||
<th>host</th>
|
||||
<th>内网目标</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script type="text/javascript">
|
||||
function del(host) {
|
||||
if (confirm("确定要删除数据吗?")) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/index/delhost",
|
||||
data: {"host": host},
|
||||
success: function (res) {
|
||||
alert(res.msg)
|
||||
if (res.status) {
|
||||
document.location.reload();
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function add() {
|
||||
window.location.href = "/index/addhost?vkey={{.vkey}}"
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
var table = $('#sampleTable').DataTable({
|
||||
dom: 'Bfrtip',
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
autoWidth: false,
|
||||
ordering: false,
|
||||
ajax: {
|
||||
url: window.location,
|
||||
type: 'POST'
|
||||
},
|
||||
dom: '<"top"fl><"toolbar">rt<"bottom"ip><"clear">',
|
||||
columns: [ //这个是显示到界面上的个数据 格式为 {data:'显示的字段名'}
|
||||
{data: 'Vkey'},
|
||||
{data: 'Host'},
|
||||
{data: 'Target'},
|
||||
{data: 'Target'},
|
||||
],
|
||||
bFilter: false,
|
||||
columnDefs: [{
|
||||
targets: -1,
|
||||
render: function (data, type, row, meta) {
|
||||
|
||||
return '<div class="btn-group" role="group" aria-label="..."> ' +
|
||||
'<button onclick="del(\'' + row.Host + '\')" type="button" class="btn btn-danger btn-sm">删除</button>' +
|
||||
' </div>'
|
||||
}
|
||||
}
|
||||
],
|
||||
buttons: []
|
||||
});
|
||||
$("#sampleTable_length").html('<button class="btn btn-primary" onclick="add()" type="button">新增</button>')
|
||||
})
|
||||
;
|
||||
|
||||
|
||||
</script>
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="tile">
|
||||
<iframe src="https://ghbtns.com/github-btn.html?user=cnlh&repo=easyProxy&type=star&count=true&size=large"
|
||||
frameborder="0" scrolling="0" width="160px" height="30px"></iframe>
|
||||
<iframe src="https://ghbtns.com/github-btn.html?user=cnlh&repo=easyProxy&type=watch&count=true&size=large&v=2"
|
||||
frameborder="0" scrolling="0" width="160px" height="30px"></iframe>
|
||||
<iframe src="https://ghbtns.com/github-btn.html?user=cnlh&repo=easyProxy&type=fork&count=true&size=large"
|
||||
frameborder="0" scrolling="0" width="158px" height="30px"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="tile">
|
||||
<h3 class="tile-title">域名代理模式</h3>
|
||||
<p>
|
||||
<b>适用范围:</b> 小程序开发、微信公众号开发、产品演示
|
||||
</p>
|
||||
<p>
|
||||
<b>假设场景:</b>
|
||||
<li>有一个域名proxy.com,有一台公网机器ip为{{.ip}}</li>
|
||||
<li>两个内网开发站点127.0.0.1:81,127.0.0.1:82</li>
|
||||
<li>想通过a.proxy.com访问127.0.0.1:81,通过b.proxy.com访问127.0.0.1:82</li>
|
||||
</p>
|
||||
<p><b>使用步骤:</b></p>
|
||||
<ul>
|
||||
<li>将a.proxy.com,b.proxy.com解析到公网服务器{{.ip}}</li>
|
||||
<li>使用nginx监听这两个个域名,并配置ssl等……</li>
|
||||
<li>在nginx配置中添加反向代理<br>
|
||||
<pre><code>
|
||||
server {
|
||||
listen 80;
|
||||
server_name a.proxy.com b.proxy.com;#也可以是泛解析*.proxy.com
|
||||
#ssl等配置
|
||||
<b>location / {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Nginx-Proxy true;
|
||||
proxy_set_header Connection "";
|
||||
proxy_pass http://127.0.0.1:{{.proxyPort}};
|
||||
}</b>
|
||||
}
|
||||
</code></pre>
|
||||
</li>
|
||||
<li>在域名代理管理中添加一个客户端,选择压缩方式,保存。 <a href="/index/add?type=hostServer">立即添加</a></li>
|
||||
{{/*<li>在域名代理管理中找到新加的客户端(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
|
||||
<li>点击该客户端的域名管理,添加两条规则规则:1、域名:a.proxy.com,内网目标:127.0.0.1:81,2、域名:b.proxy.com,内网目标:127.0.0.1:82</li>
|
||||
<li>现在访问a.proxy.com,b.proxy.com即可成功</li>
|
||||
</ul>
|
||||
<p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认启动方式为单客户端模式,默认内网客户端已经启动</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="tile">
|
||||
<h3 class="tile-title">tcp隧道模式</h3>
|
||||
<p>
|
||||
<b>适用范围:</b> ssh、远程桌面等tcp连接场景
|
||||
</p>
|
||||
<p>
|
||||
<b>假设场景:</b> 想通过访问公网服务器{{.ip}}的8001端口,连接内网机器10.1.50.101的22端口,实现ssh连接
|
||||
</p>
|
||||
<p><b>使用步骤:</b></p>
|
||||
<ul>
|
||||
<li>在tcp隧道管理中添加一条隧道,填写监听的端口(8001)、内网目标ip和目标端口(10.1.50.101:22),选择压缩方式,保存。 <a
|
||||
href="/index/add?type=tunnelServer">立即添加</a></li>
|
||||
{{/*<li>在tcp管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
|
||||
<li>访问公网服务器ip({{.ip}}):填写的监听端口(8001),相当于访问内网ip(10.1.50.101):目标端口(22),例如:ssh -p 8001 root@{{.ip}}</li>
|
||||
</ul>
|
||||
<p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认启动方式为单客户端模式,默认内网客户端已经启动</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="tile">
|
||||
<h3 class="tile-title">udp隧道模式</h3>
|
||||
<p>
|
||||
<b>适用范围:</b> 内网dns解析等udp连接场景
|
||||
</p>
|
||||
<p>
|
||||
<b>假设场景:</b> 内网有一台dns(10.1.50.102:53),在非内网环境下想使用该dns,公网服务器为{{.ip}}
|
||||
</p>
|
||||
<p><b>使用步骤:</b></p>
|
||||
<ul>
|
||||
<li>在udp隧道管理中添加一条隧道,填写监听的端口(8002)、内网目标ip和目标端口(10.1.50.102:53),选择压缩方式,保存。 <a
|
||||
href="/index/add?type=udpServer">立即添加</a></li>
|
||||
{{/*<li>在udp管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
|
||||
<li>修改本机dns为{{.ip}},则相当于使用10.1.50.202作为dns服务器</li>
|
||||
</ul>
|
||||
<p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认启动方式为单客户端模式,默认内网客户端已经启动</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="tile">
|
||||
<h3 class="tile-title">socks5代理模式</h3>
|
||||
<p>
|
||||
<b>适用范围:</b> 在外网环境下如同使用vpn一样访问内网设备或者资源
|
||||
</p>
|
||||
<p>
|
||||
<b>假设场景:</b> 想将公网服务器{{.ip}}的8003端口作为socks5代理,达到访问内网任意设备或者资源的效果
|
||||
</p>
|
||||
<p><b>使用步骤:</b></p>
|
||||
<ul>
|
||||
<li>在socks5隧道管理中添加一条隧道,填写监听的端口(8003),验证用户名和密码自行选择(建议先不填,部分客户端不支持,proxifer支持),选择压缩方式,保存。 <a
|
||||
href="/index/add?type=sock5Server">立即添加</a></li>
|
||||
{{/*<li>在socks5代理管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>*/}}
|
||||
<li>在外网环境的本机配置socks5代理,ip为公网服务器ip({{.ip}}),端口为填写的监听端口(8003),即可畅享内网了</li>
|
||||
</ul>
|
||||
<p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认启动方式为单客户端模式,默认内网客户端已经启动</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="tile">
|
||||
<h3 class="tile-title">http代理模式</h3>
|
||||
<p>
|
||||
<b>适用范围:</b> 在外网环境下访问内网站点
|
||||
</p>
|
||||
<p>
|
||||
<b>假设场景:</b> 想将公网服务器{{.ip}}的8004端口作为http代理,访问内网网站
|
||||
</p>
|
||||
<p><b>使用步骤:</b></p>
|
||||
<ul>
|
||||
<li>在http隧道管理中添加一条隧道,填写监听的端口(8004),选择压缩方式,保存。 <a
|
||||
href="/index/add?type=httpProxyServer">立即添加</a></li>
|
||||
<li>在http代理管理列表中找到新加的隧道(查看其vkey或者客户端命令),任意内网机器执行客户端命令</li>
|
||||
<li>在外网环境的本机配置http代理,ip为公网服务器ip({{.ip}}),端口为填写的监听端口(8004),即可访问了</li>
|
||||
</ul>
|
||||
<p>注:上文中提到公网ip({{.ip}})为系统自动识别,如果是在测试环境中请自行对应,默认启动方式为单客户端模式,默认内网客户端已经启动</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="tile">
|
||||
<p>
|
||||
<b>多客户端模式:</b>
|
||||
<li>服务端启动:./easyProxy -mode=webServer -tcpport=8284</li>
|
||||
<li>客户端启动:./easyProxy -server={{.ip}}:{{.p}} -vkey=xxx(见管理列表的客户端启动模式)</li>
|
||||
</p>
|
||||
<p><b>支持客户端同时建立多条隧道,例如单个通道时命令为./easyProxy -server={{.ip}}:{{.p}} -vkey=ccc,如果要支持另外一个隧道,则对应的执行命令为./easyProxy
|
||||
-server={{.ip}}:{{.p}} -vkey=ccc,ddd,即用逗号分隔开多个vkey,适用于所有模式!</b></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="tile">
|
||||
<div class="tile-body">
|
||||
<table class="table table-hover table-bordered" id="sampleTable">
|
||||
<thead>
|
||||
<tr>
|
||||
{{/*<th>模式</th>*/}}
|
||||
<th>监听端口</th>
|
||||
<th>内网目标</th>
|
||||
<th>多客户端模式客户端执行命令</th>
|
||||
<th>压缩方式</th>
|
||||
<th>用户名</th>
|
||||
<th>密码</th>
|
||||
<th>客户端状态</th>
|
||||
<th>状态</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script type="text/javascript">
|
||||
function del(vKey) {
|
||||
if (confirm("确定要删除数据吗?")) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/index/del",
|
||||
data: {"vKey": vKey},
|
||||
success: function (res) {
|
||||
alert(res.msg)
|
||||
if (res.status) {
|
||||
document.location.reload();
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function start(vKey) {
|
||||
if (confirm("确定要开始任务吗?")) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/index/start",
|
||||
data: {"vKey": vKey},
|
||||
success: function (res) {
|
||||
alert(res.msg)
|
||||
if (res.status) {
|
||||
document.location.reload();
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function stop(vKey) {
|
||||
if (confirm("确定要暂停吗?")) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/index/stop",
|
||||
data: {"vKey": vKey},
|
||||
success: function (res) {
|
||||
alert(res.msg)
|
||||
if (res.status) {
|
||||
document.location.reload();
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function edit(vKey) {
|
||||
window.location.href = "/index/edit?vKey=" + vKey
|
||||
}
|
||||
|
||||
function add() {
|
||||
window.location.href = "/index/add?type=" +{{.type}}
|
||||
}
|
||||
|
||||
function hostList(vkey) {
|
||||
window.location.href = "/index/hostlist?vkey=" + vkey
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
var table = $('#sampleTable').DataTable({
|
||||
dom: 'Bfrtip',
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
autoWidth: false,
|
||||
ordering: false,
|
||||
ajax: {
|
||||
url: '/index/gettasklist?type={{.type}}',
|
||||
type: 'POST'
|
||||
},
|
||||
dom: '<"top"fl><"toolbar">rt<"bottom"ip><"clear">',
|
||||
columns: [ //这个是显示到界面上的个数据 格式为 {data:'显示的字段名'}
|
||||
// {data: 'Mode'},
|
||||
{data: 'TcpPort'},
|
||||
{data: 'Target'},
|
||||
{data: 'VerifyKey'},
|
||||
{data: 'Compress'},
|
||||
{data: 'U'},
|
||||
{data: 'P'},
|
||||
{data: 'ClientStatus'},
|
||||
{data: 'IsRun'},
|
||||
{data: "Id"}
|
||||
],
|
||||
bFilter: false,
|
||||
columnDefs: [{
|
||||
targets: -1,
|
||||
render: function (data, type, row, meta) {
|
||||
if (row.IsRun == 1) {
|
||||
btn = "<button onclick=\"stop('" + row.VerifyKey + "')\" class=\"btn btn-secondary btn-sm\" type=\"button\">关闭</button>"
|
||||
} else {
|
||||
btn = "<button onclick=\"start('" + row.VerifyKey + "')\" class=\"btn btn-success btn-sm\" type=\"button\">打开</button>"
|
||||
}
|
||||
btn_edit = '<button onclick="edit(\'' + row.VerifyKey + '\')" type="button" class="btn btn-primary btn-sm">查看编辑</button> '
|
||||
if ({{.type}} == "hostServer"
|
||||
)
|
||||
{
|
||||
btn_host = '<button onclick="hostList(\'' + row.VerifyKey + '\')" type="button" class="btn btn-info btn-sm">域名管理</button> '
|
||||
}
|
||||
else
|
||||
{
|
||||
btn_host = ""
|
||||
}
|
||||
return '<div class="btn-group" role="group" aria-label="..."> ' +
|
||||
'<button onclick="del(\'' + row.VerifyKey + '\')" type="button" class="btn btn-danger btn-sm">删除</button>' +
|
||||
btn_edit + btn + btn_host + ' </div>'
|
||||
}
|
||||
},
|
||||
{
|
||||
targets: -2,
|
||||
render: function (data, type, row, meta) {
|
||||
if (data == 0) {
|
||||
return "<span class=\"badge badge-pill badge-secondary\">暂停</span>"
|
||||
} else {
|
||||
return "<span class=\"badge badge-pill badge-success\">正常</span>"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
targets: 2,
|
||||
render: function (data, type, row, meta) {
|
||||
return "./easyProxy -server={{.ip}}:{{.p}} -vkey=" + data
|
||||
// return data
|
||||
}
|
||||
},
|
||||
{
|
||||
targets: -3,
|
||||
render: function (data, type, row, meta) {
|
||||
if (data == 0) {
|
||||
return "<span class=\"badge badge-pill badge-secondary\">离线</span>"
|
||||
} else {
|
||||
return "<span class=\"badge badge-pill badge-success\">在线</span>"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
buttons: []
|
||||
});
|
||||
$("#sampleTable_length").html('<button class="btn btn-primary" onclick="add()" type="button">新增</button>')
|
||||
})
|
||||
;
|
||||
|
||||
|
||||
</script>
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="/static/img/favicon.ico">
|
||||
<!-- Main CSS-->
|
||||
<link rel="stylesheet" type="text/css" href="/static/css/main.css">
|
||||
<!-- Font-icon css-->
|
||||
<link rel="stylesheet" type="text/css" href="/static/css/font-awesome.min.css">
|
||||
<title>easyProxy内网穿透</title>
|
||||
</head>
|
||||
<body>
|
||||
<section class="material-half-bg">
|
||||
<div class="cover"></div>
|
||||
</section>
|
||||
<section class="login-content">
|
||||
<div class="logo">
|
||||
<h1></h1>
|
||||
</div>
|
||||
<div class="login-box">
|
||||
<form class="login-form" onsubmit="return false">
|
||||
<h3 class="login-head"><i class="fa fa-lg fa-fw fa-user"></i>内网穿透管理登陆</h3>
|
||||
<div class="form-group">
|
||||
<label class="control-label">密码</label>
|
||||
<input class="form-control" name="psd" type="password" placeholder="" autofocus>
|
||||
</div>
|
||||
<div class="form-group btn-container">
|
||||
<button onclick="login()" class="btn btn-primary btn-block"><i class="fa fa-sign-in fa-lg fa-fw"></i>登陆
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script type="text/javascript" src="/static/js/pdfmake.min.js"></script>
|
||||
<script type="text/javascript" src="/static/js/vfs_fonts.js"></script>
|
||||
<script type="text/javascript" src="/static/js/datatables.min.js"></script>
|
||||
<script src="/static/js/main.js"></script>
|
||||
<script type="text/javascript">
|
||||
// Login Page Flipbox control
|
||||
function login() {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/login/verify",
|
||||
data: $("form").serializeArray(),
|
||||
success: function (res) {
|
||||
if (res.status) {
|
||||
window.location.href = "/index/index"
|
||||
} else {
|
||||
alert(res.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
return false
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<div class="page-error tile">
|
||||
<h1><i class="fa fa-exclamation-circle"></i> Error 404: Page not found</h1>
|
||||
<p>The page you have requested is not found.</p>
|
||||
<p><a class="btn btn-primary" href="javascript:window.history.back();">Go Back</a></p>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="description"
|
||||
content="Vali is a responsive and free admin theme built with Bootstrap 4, SASS and PUG.js. It's fully customizable and modular.">
|
||||
<link rel="shortcut icon" href="/static/img/favicon.ico">
|
||||
<title>easyProxy内网穿透</title>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- Font-icon css-->
|
||||
<link rel="stylesheet" type="text/css" href="/static/css/font-awesome.min.css">
|
||||
<!-- Main CSS-->
|
||||
<link rel="stylesheet" type="text/css" href="/static/css/main.css">
|
||||
</head>
|
||||
<body class="app sidebar-mini rtl">
|
||||
<!-- Navbar-->
|
||||
<header class="app-header">
|
||||
<a class="app-header__logo" href="/"></a>
|
||||
<!-- Sidebar toggle button--><a class="app-sidebar__toggle" href="#" data-toggle="sidebar"
|
||||
aria-label="Hide Sidebar"></a>
|
||||
<ul class="app-nav">
|
||||
<!--Notification Menu-->
|
||||
<!-- User Menu-->
|
||||
<li class="dropdown"><a class="app-nav__item" href="/login/out" data-toggle="dropdown"
|
||||
aria-label="Open Profile Menu"><i class="fa fa-sign-out fa-lg"></i>退出登陆</a>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</header>
|
||||
<!-- Sidebar menu-->
|
||||
<div class="app-sidebar__overlay" data-toggle="sidebar"></div>
|
||||
<aside class="app-sidebar">
|
||||
<div class="app-sidebar__user"><img class="app-sidebar__user-avatar"
|
||||
src="/static/img/48.jpg"
|
||||
alt="User Image">
|
||||
<div>
|
||||
<p class="app-sidebar__user-name">System</p>
|
||||
<p class="app-sidebar__user-designation">欢迎使用</p>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="app-menu">
|
||||
<li><a class="app-menu__item {{if eq "index" .menu}}active{{end}}" href="/"><i
|
||||
class="app-menu__icon fa fa-dashboard"></i><span class="app-menu__label">使用说明</span></a></li>
|
||||
<li><a class="app-menu__item {{if eq "host" .menu}}active{{end}}" href="/index/host"><i
|
||||
class="app-menu__icon fa fa-lemon-o"></i><span class="app-menu__label">域名代理</span></a></li>
|
||||
<li><a class="app-menu__item {{if eq "tcp" .menu}}active{{end}}" href="/index/tcp"><i
|
||||
class="app-menu__icon fa fa-life-buoy"></i><span class="app-menu__label">tcp隧道管理</span></a></li>
|
||||
<li><a class="app-menu__item {{if eq "udp" .menu}}active{{end}}" href="/index/udp"><i
|
||||
class="app-menu__icon fa fa-laptop"></i><span class="app-menu__label">udp隧道管理</span></a></li>
|
||||
<li><a class="app-menu__item {{if eq "socks5" .menu}}active{{end}}" href="/index/socks5"><i
|
||||
class="app-menu__icon fa fa-lightbulb-o"></i><span class="app-menu__label">socks5代理</span></a></li>
|
||||
<li><a class="app-menu__item {{if eq "http" .menu}}active{{end}}" href="/index/http"><i
|
||||
class="app-menu__icon fa fa-magic"></i><span class="app-menu__label">http代理</span></a></li>
|
||||
|
||||
</ul>
|
||||
</aside>
|
||||
|
||||
<script type="text/javascript" src="/static/js/datatables.min.js"></script>
|
||||
<script src="/static/js/main.js"></script>
|
||||
<main class="app-content">
|
||||
<div class="app-title">
|
||||
<div>
|
||||
<h1><i class="fa fa-th-list"></i> {{.name}}</h1>
|
||||
<a href="javascript:history.back(-1);"><i class="fa fa-mail-reply"></i>返回</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{{.LayoutContent}}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue