zoukankan      html  css  js  c++  java
  • [帆软][内网穿透][LanProxy]蛋疼的网络架构探讨

    源码和excel资料都在github:https://github.com/landv/golang-test/tree/master/lanproxy-go-client-regisServices

    原本架构

    能够访问互联网的机器,去访问: 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
    ", *flagServiceName, *flagServiceDesc, err)
            }
            fmt.Printf("Done
    ")
            return
        }
        // remove service
        if *flagServiceUninstall {
            if err := winsvc.RemoveService(*flagServiceName); err != nil {
                log.Fatalln("removeService:", err)
            }
            fmt.Printf("Done
    ")
            return
        }
        // start service
        if *flagServiceStart {
            if err := winsvc.StartService(*flagServiceName); err != nil {
                log.Fatalln("startService:", err)
            }
            fmt.Printf("Done
    ")
            return
        }
        // stop service
        if *flagServiceStop {
            if err := winsvc.StopService(*flagServiceName); err != nil {
                log.Fatalln("stopService:", err)
            }
            fmt.Printf("Done
    ")
            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
    ", 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
  • 相关阅读:
    嵌入式C程序基础与编程结构
    英特尔图形处理器第8代架构
    用servlet打内容到网页上
    servlet获取多个同名参数
    JAVA之复制数组
    用JAVA写一个简单的英文加密器
    Tomcat使用
    JAVA,字符串替换
    使用自己的域名解析博客园地址(CSDN也可以)
    错误笔记20181124
  • 原文地址:https://www.cnblogs.com/landv/p/12970399.html
Copyright © 2011-2022 走看看