源码和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