专栏首页landv[内网穿透][LanProxy]蛋疼的网络架构探讨

[内网穿透][LanProxy]蛋疼的网络架构探讨

原本架构

能够访问互联网的机器,去访问: 2.2.2.2:8080,就是访问的mac电脑的80端口 2.2.2.2:8081,就是访问的Windows电脑的1433端口。

这是基于公网暴露端口的方式,可能造成端口访问不安全问题。

客户端》服务器中转》访问端。三方彼此独立

蛋疼架构

能够访问这台安装lanproxy的机器,去访问: 192.168.1.2:8080,就是访问的mac电脑的80端口

192.168.1.2:8081,就是访问的Windows电脑的1433端口

酱紫,公网就不暴露端口了,有点灰鸽子反弹上线的样子

这样就有一个蛋疼的问题,你本地必须拥有公网IP。

客户端》服务端(lanProxy和访问端在一起)

因为本地公网防火墙和本机防火墙均只暴露了4900服务。其他端口出不去,酱紫就保护了端口的安全。

终极改进架构

虽然叫它终极改进架构,但也不是最好的,反正又不是去黑人,用不了那么多层跳板,跳来跳去的。

能够访问这台安装lanproxy的机器, 去链接socks5代理 IP不能与本地IP冲突

192.168.1.2:8080,就是访问的mac电脑的80端口

192.168.1.2:8081,就是访问的Windows电脑的1433端口

酱紫,公网就不暴露端口了,加了一层socks5代理 客户端》服务端》socks5代理》访问端

因我本地拥有公网IP,带宽足够大,没有使用这种方式。

毕竟我阿里云服务器1M小水管,这么走一圈有点慢。

客户端改写,Windows注册为服务启动

由于自己用懒得去优化了,直接将配置都固化到代码里面了,这个改写主要目的是注册成服务。

package main

import (
    "crypto/tls"
    "encoding/binary"
    "flag"
    "fmt"
    "github.com/chai2010/winsvc"
    "log"
    "net"
    "net/http"
    "os"
    "path/filepath"
    "runtime/debug"
    "strconv"
    "time"
)

/**
先把lanproxy-go-client的东东加进来
*/
const (
    /* 心跳消息 */
    TYPE_HEARTBEAT = 0x07

    /* 认证消息,检测clientKey是否正确 */
    C_TYPE_AUTH = 0x01

    /* 代理后端服务器建立连接消息 */
    TYPE_CONNECT = 0x03

    /* 代理后端服务器断开连接消息 */
    TYPE_DISCONNECT = 0x04

    /* 代理数据传输 */
    P_TYPE_TRANSFER = 0x05

    /* 用户与代理服务器以及代理客户端与真实服务器连接是否可写状态同步 */
    C_TYPE_WRITE_CONTROL = 0x06

    //协议各字段长度
    LEN_SIZE = 4

    TYPE_SIZE = 1

    SERIAL_NUMBER_SIZE = 8

    URI_LENGTH_SIZE = 1

    //心跳周期,服务器端空闲连接如果60秒没有数据上报就会关闭连接
    HEARTBEAT_INTERVAL = 30
)

type LPMessageHandler struct {
    connPool    *ConnHandlerPool
    connHandler *ConnHandler
    clientKey   string
    die         chan struct{}
}

type Message struct {
    Type         byte
    SerialNumber uint64
    Uri          string
    Data         []byte
}

type ProxyConnPooler struct {
    addr string
    conf *tls.Config
}

func start(key string, ip string, port int, conf *tls.Config) {
    connPool := &ConnHandlerPool{Size: 100, Pooler: &ProxyConnPooler{addr: ip + ":" + strconv.Itoa(port), conf: conf}}
    connPool.Init()
    connHandler := &ConnHandler{}
    for {
        //cmd connection
        conn := connect(key, ip, port, conf)
        connHandler.conn = conn
        messageHandler := LPMessageHandler{connPool: connPool}
        messageHandler.connHandler = connHandler
        messageHandler.clientKey = key
        messageHandler.startHeartbeat()
        log.Println("start listen cmd message:", messageHandler)
        connHandler.Listen(conn, &messageHandler)
    }
}

func connect(key string, ip string, port int, conf *tls.Config) net.Conn {
    for {
        var conn net.Conn
        var err error
        p := strconv.Itoa(port)
        if conf != nil {
            conn, err = tls.Dial("tcp", ip+":"+p, conf)
        } else {
            conn, err = net.Dial("tcp", ip+":"+p)
        }
        if err != nil {
            log.Println("Error dialing", err.Error())
            time.Sleep(time.Second * 3)
            continue
        }

        return conn
    }
}

func (messageHandler *LPMessageHandler) Encode(msg interface{}) []byte {
    if msg == nil {
        return []byte{}
    }

    message := msg.(Message)
    uriBytes := []byte(message.Uri)
    bodyLen := TYPE_SIZE + SERIAL_NUMBER_SIZE + URI_LENGTH_SIZE + len(uriBytes) + len(message.Data)
    data := make([]byte, LEN_SIZE, bodyLen+LEN_SIZE)
    binary.BigEndian.PutUint32(data, uint32(bodyLen))
    data = append(data, message.Type)
    snBytes := make([]byte, 8)
    binary.BigEndian.PutUint64(snBytes, message.SerialNumber)
    data = append(data, snBytes...)
    data = append(data, byte(len(uriBytes)))
    data = append(data, uriBytes...)
    data = append(data, message.Data...)
    return data
}

func (messageHandler *LPMessageHandler) Decode(buf []byte) (interface{}, int) {
    lenBytes := buf[0:LEN_SIZE]
    bodyLen := binary.BigEndian.Uint32(lenBytes)
    if uint32(len(buf)) < bodyLen+LEN_SIZE {
        return nil, 0
    }
    n := int(bodyLen + LEN_SIZE)
    body := buf[LEN_SIZE:n]
    msg := Message{}
    msg.Type = body[0]
    msg.SerialNumber = binary.BigEndian.Uint64(body[TYPE_SIZE : SERIAL_NUMBER_SIZE+TYPE_SIZE])
    uriLen := uint8(body[SERIAL_NUMBER_SIZE+TYPE_SIZE])
    msg.Uri = string(body[SERIAL_NUMBER_SIZE+TYPE_SIZE+URI_LENGTH_SIZE : SERIAL_NUMBER_SIZE+TYPE_SIZE+URI_LENGTH_SIZE+uriLen])
    msg.Data = body[SERIAL_NUMBER_SIZE+TYPE_SIZE+URI_LENGTH_SIZE+uriLen:]
    return msg, n
}

func (messageHandler *LPMessageHandler) MessageReceived(connHandler *ConnHandler, msg interface{}) {
    message := msg.(Message)
    switch message.Type {
    case TYPE_CONNECT:
        go func() {
            log.Println("received connect message:", message.Uri, "=>", string(message.Data))
            addr := string(message.Data)
            realServerMessageHandler := &RealServerMessageHandler{LpConnHandler: connHandler, ConnPool: messageHandler.connPool, UserId: message.Uri, ClientKey: messageHandler.clientKey}
            conn, err := net.Dial("tcp", addr)
            if err != nil {
                log.Println("connect realserver failed", err)
                realServerMessageHandler.ConnFailed()
            } else {
                connHandler := &ConnHandler{}
                connHandler.conn = conn
                connHandler.Listen(conn, realServerMessageHandler)
            }
        }()
    case P_TYPE_TRANSFER:
        if connHandler.NextConn != nil {
            connHandler.NextConn.Write(message.Data)
        }
    case TYPE_DISCONNECT:
        if connHandler.NextConn != nil {
            connHandler.NextConn.NextConn = nil
            connHandler.NextConn.conn.Close()
            connHandler.NextConn = nil
        }
        if messageHandler.clientKey == "" {
            messageHandler.connPool.Return(connHandler)
        }
    }
}

func (messageHandler *LPMessageHandler) ConnSuccess(connHandler *ConnHandler) {
    log.Println("connSuccess, clientkey:", messageHandler.clientKey)
    if messageHandler.clientKey != "" {
        msg := Message{Type: C_TYPE_AUTH}
        msg.Uri = messageHandler.clientKey
        connHandler.Write(msg)
    }
}

func (messageHandler *LPMessageHandler) ConnError(connHandler *ConnHandler) {
    log.Println("connError:", connHandler)
    if messageHandler.die != nil {
        close(messageHandler.die)
    }

    if connHandler.NextConn != nil {
        connHandler.NextConn.NextConn = nil
        connHandler.NextConn.conn.Close()
        connHandler.NextConn = nil
    }

    connHandler.messageHandler = nil
    messageHandler.connHandler = nil
    time.Sleep(time.Second * 3)
}

func (messageHandler *LPMessageHandler) startHeartbeat() {
    log.Println("start heartbeat:", messageHandler.connHandler)
    messageHandler.die = make(chan struct{})
    go func() {
        defer func() {
            if err := recover(); err != nil {
                log.Printf("run time panic: %v", err)
                debug.PrintStack()
            }
        }()
        for {
            select {
            case <-time.After(time.Second * HEARTBEAT_INTERVAL):
                if time.Now().Unix()-messageHandler.connHandler.ReadTime >= 2*HEARTBEAT_INTERVAL {
                    log.Println("proxy connection timeout:", messageHandler.connHandler, time.Now().Unix()-messageHandler.connHandler.ReadTime)
                    messageHandler.connHandler.conn.Close()
                    return
                }
                msg := Message{Type: TYPE_HEARTBEAT}
                messageHandler.connHandler.Write(msg)
            case <-messageHandler.die:
                return
            }
        }
    }()
}

func (pooler *ProxyConnPooler) Create(pool *ConnHandlerPool) (*ConnHandler, error) {
    var conn net.Conn
    var err error
    if pooler.conf != nil {
        conn, err = tls.Dial("tcp", pooler.addr, pooler.conf)
    } else {
        conn, err = net.Dial("tcp", pooler.addr)
    }

    if err != nil {
        log.Println("Error dialing", err.Error())
        return nil, err
    } else {
        messageHandler := LPMessageHandler{connPool: pool}
        connHandler := &ConnHandler{}
        connHandler.Active = true
        connHandler.conn = conn
        connHandler.messageHandler = interface{}(&messageHandler).(MessageHandler)
        messageHandler.connHandler = connHandler
        messageHandler.startHeartbeat()
        go func() {
            connHandler.Listen(conn, &messageHandler)
        }()
        return connHandler, nil
    }
}

func (pooler *ProxyConnPooler) Remove(conn *ConnHandler) {
    conn.conn.Close()
}

func (pooler *ProxyConnPooler) IsActive(conn *ConnHandler) bool {
    return conn.Active
}

/**
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build fuckRegisServices.go

*/
var (
    server *http.Server
)
var (
    appPath              string
    flagServiceName      = flag.String("service-name", "myserver", "Set service name")
    flagServiceDesc      = flag.String("service-desc", "myserver service", "Set service description")
    flagServiceInstall   = flag.Bool("service-install", false, "Install service")
    flagServiceUninstall = flag.Bool("service-remove", false, "Remove service")
    flagServiceStart     = flag.Bool("service-start", false, "Start service")
    flagServiceStop      = flag.Bool("service-stop", false, "Stop service")
)

func init() {
    // change to current dir
    var err error
    if appPath, err = winsvc.GetAppPath(); err != nil {
        log.Fatal(err)
    }
    if err := os.Chdir(filepath.Dir(appPath)); err != nil {
        log.Fatal(err)
    }
}
func main() {
    flag.Parse()
    // install service
    if *flagServiceInstall {
        if err := winsvc.InstallService(appPath, *flagServiceName, *flagServiceDesc); err != nil {
            log.Fatalf("installService(%s, %s): %v\n", *flagServiceName, *flagServiceDesc, err)
        }
        fmt.Printf("Done\n")
        return
    }
    // remove service
    if *flagServiceUninstall {
        if err := winsvc.RemoveService(*flagServiceName); err != nil {
            log.Fatalln("removeService:", err)
        }
        fmt.Printf("Done\n")
        return
    }
    // start service
    if *flagServiceStart {
        if err := winsvc.StartService(*flagServiceName); err != nil {
            log.Fatalln("startService:", err)
        }
        fmt.Printf("Done\n")
        return
    }
    // stop service
    if *flagServiceStop {
        if err := winsvc.StopService(*flagServiceName); err != nil {
            log.Fatalln("stopService:", err)
        }
        fmt.Printf("Done\n")
        return
    }
    // run as service
    if !winsvc.InServiceMode() {
        log.Println("main:", "runService")
        if err := winsvc.RunAsService(*flagServiceName, StartServer, StopServer, false); err != nil {
            log.Fatalf("svc.Run: %v\n", err)
        }
        return
    }
    // run as normal
    StartServer()
}
func StartServer() {
    //start(c.String("k"), c.String("s"), c.Int("p"), conf)
    var conf *tls.Config
    start("key", "IP", 4900, conf) // TODO 修改这里固化配置信息
    //log.Println("StartServer, port = 8080")
    //http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    //    fmt.Fprintln(w, "winsrv server", time.Now())
    //})
    //server = &http.Server{Addr: ":8080"}
    //server.ListenAndServe()

    //log.Println("lanproxy - help you expose a local server behind a NAT or firewall to the internet")
    //app := cli.NewApp()
    //app.Name = "lanproxy"
    //app.Flags = []cli.Flag{
    //    cli.StringFlag{
    //        Name:  "k",
    //        Value: "",
    //        Usage: "client key",
    //    },
    //    cli.StringFlag{
    //        Name:  "s",
    //        Value: "",
    //        Usage: "proxy server host",
    //    },
    //    cli.IntFlag{
    //        Name:  "p",
    //        Value: 4900,
    //        Usage: "proxy server port",
    //    }, cli.StringFlag{
    //        Name:  "ssl",
    //        Value: "false",
    //        Usage: "enable ssl",
    //    }, cli.StringFlag{
    //        Name:  "cer",
    //        Value: "",
    //        Usage: "ssl cert path, default skip verify certificate",
    //    }}
    //app.Usage = "help you expose a local server behind a NAT or firewall to the internet"
    //app.Action = func(c *cli.Context) error {
    //    if c.String("s") == "" {
    //        log.Println("server ip addr is required, use -s")
    //        log.Println("exit")
    //        return nil
    //    }
    //    if c.String("k") == "" {
    //        log.Println("clientkey is required, use -k")
    //        log.Println("exit")
    //        return nil
    //    }
    //    log.Println("client key:", c.String("k"))
    //    log.Println("server addr:", c.String("s"))
    //    log.Println("server port:", c.Int("p"))
    //    log.Println("enable ssl:", c.String("ssl"))
    //    cerPath := c.String("cer")
    //    if c.String("cer") == "" {
    //        cerPath = "certificate path is null, skip verify certificate"
    //    }
    //    log.Println("ssl cer path:", cerPath)
    //    var conf *tls.Config
    //    if c.String("ssl") == "true" {
    //        skipVerify := false
    //        if c.String("cer") == "" {
    //            skipVerify = true
    //        }
    //        conf = &tls.Config{
    //            InsecureSkipVerify: skipVerify,
    //        }
    //
    //        if c.String("cer") != "" {
    //            cert, err := ioutil.ReadFile(c.String("cer"))
    //            if err != nil {
    //                log.Fatalf("Couldn't load file", err)
    //                return nil
    //            }
    //            certPool := x509.NewCertPool()
    //            certPool.AppendCertsFromPEM(cert)
    //            conf.ClientCAs = certPool
    //        }
    //    }
    //    start(c.String("k"), c.String("s"), c.Int("p"), conf)
    //    return nil
    //}
    //
    //app.Run(os.Args)
}
func StopServer() {
    //if server != nil {
    //    server.Shutdown(context.Background()) // Go 1.8+
    //}
    //log.Println("StopServer")

}
#这是一个改写版
这是原版server
github.com/ffay/lanproxy
这是原版客户端
lanproxy-go-client https://github.com/ffay/lanproxy-go-client

# TODO
在Windows上面把客户端注册为服务,emmm有源码直接改写就好了,不使用其他外挂式方式。

# 交叉编译
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
# 注册为Windows服务
$ lanproxy-go-client-regisServices.exe -service-install
# 启动和停止Windows服务
$ lanproxy-go-client-regisServices.exe -service-start  
$ lanproxy-go-client-regisServices.exe -service-stop
# 删除服务
# 删除之前需要先停止服务
$ lanproxy-go-client-regisServices.exe -service-remove

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • [CodeIgniter4]-处理多环境

    开发者常常希望根据是生产环境还是开发环境能够区分不同的定制行为,例如,如果在开发环境的程序当中输出详细的错误信息这样做对开发者来说是非常有帮助的,但是这样做的话...

    landv
  • golang-os文件操作

    landv
  • [OHIF-Viewers]医疗数字阅片-医学影像-中间插播一下-es6-使用const加箭头函数声明函数相对于function声明函数有什么好处?

    这个好多人都已经写过了,这里插播一下,在OHIF-Viewers里面有很多这样的写法,当然OHIF-Viewers维护者众多,有人用这种新的写法也有原始的写法,...

    landv
  • Github 「stars」 平均 3558,最棒的 30 个机器学习实例

    在过去的一年里,我们比较了近 8800 个机器学习开源项目选出了其中最棒的 30 个 (几率只有 0.3%)。 这是一个非常具有竞争力的名单,名单是从 2017...

    AI研习社
  • 使命必达: 深入剖析WCF的可靠会话[编程篇](下)

    整个可靠会话的机制是完全在信道层实现的,而整个信道层的最终缔造者就是绑定,所以可靠会话编程是围绕着绑定进行的。《上篇》对实现可靠会话的绑定元素已经如何使用系统绑...

    蒋金楠
  • 二条题目:Reading Club | 算法和人生选择:如何最高效地找到合适的那件衣服?

    大数据文摘
  • 可变形卷积的理解极其源码分析

    简单地说就是对特征图上的一块小区域进行加权平均,再输出相应值,其形状是规则的方形。作者觉得这个感受野太规则,不能很好地捕捉特征,所以在此基础上对卷积又加了偏置:

    于小勇
  • 在python中使用SageMaker Debugger进行机器学习模型的开发调试

    如果你从事软件开发,你就会知道 Bug 是生活的一部分。当你开始你的项目时,Bug 就可能存在,当你把你的产品交付给客户时,Bug 也可能存在。在过去的几十年中...

    deephub
  • win7远程桌面工具比自带好用推荐

      一、windows自带的远程桌面,比较便利的一点是不需要额外安装,在局域网内很方便,如果不在局域网里边,就需要给远程的主机分配一个公网ip,或者将地址的33...

    it妹
  • 【腾讯云CKV缓存】cloud key value·红黑树排名实现过程解析

    红黑树是一种自平衡的二叉查找树,它可以在O(logn)时间内执行查找、插入和删除。在c++ STL,linux内核中都有使用。

    腾讯云数据库 TencentDB

扫码关注云+社区

领取腾讯云代金券