[帆軟][內網穿透][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\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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM