├── .gitignore ├── README.md ├── bin └── golocproxy.zip ├── build.bat ├── client └── client.go ├── server └── server.go └── util └── util.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | golocproxy 2 | ========== 3 | 轻量级超强反向代理软件,用go语言开发,通过公共可知的服务器端口进行代理,把局域网内任何主机的本地服务发布给局域网外的用户,可用来跨越各种防火墙。 4 | 5 | Usage 6 | ----- 7 | 8 | 例如如下场景: 9 | 10 | 1. 局域网内的主机A(192.168.1.2)上开启http服务 11 | 2. 外部网络的主机B希望访问A的服务。由于A被防火墙保护,局域网外的主机完全无法访问A 12 | 13 | 使用golocproxy可实现这一要求 14 | 15 | 1. 找一台A和B都能访问的内网或公网服务器P(61.1.1.1),在其上启动golocproxy服务程序 `./server -p 8009 -up 8010 -pwd mypassword` 16 | 2. 在A上启动golocproxy客户程序 `./client -l 127.0.0.1:80 -r 61.1.1.1:8009 -pwd mypassword` 17 | 3. 外部的任何主机直接通过`http://61.1.1.1:8010`即可访问A的http服务 18 | 19 | Download 20 | -------- 21 | 22 | -------------------------------------------------------------------------------- /bin/golocproxy.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jijinggang/golocproxy/36c9a5cd403b30eb06b274540ec77596238c0d34/bin/golocproxy.zip -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | cd server 2 | goxc -os=linux -arch="amd64" -d="../bin" -z="false" 3 | goxc -os=windows -arch="386" -d="../bin" -z="false" 4 | cd ../client 5 | goxc -os=linux -arch="amd64" -d="../bin" -z="false" 6 | goxc -os=windows -arch="386" -d="../bin" -z="false" 7 | cd ../bin 8 | rd windows_386 /S /Q 9 | mv "./unknown/windows_386" "." 10 | rd linux_amd64 /S /Q 11 | mv "./unknown/linux_amd64" "." 12 | rd "./unknown" /S /Q 13 | cd .. -------------------------------------------------------------------------------- /client/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "../util" 5 | "flag" 6 | // "io" 7 | "log" 8 | "net" 9 | "time" 10 | ) 11 | 12 | var ( 13 | local = flag.String("l", "127.0.0.1:80", "Address of the local app service") 14 | remote = flag.String("r", "127.0.0.1:8010", "Address of the golocproxy server") 15 | pwd = flag.String("pwd", "jjg", "password to access server") 16 | ) 17 | 18 | func main() { 19 | flag.Usage = util.Usage 20 | flag.Parse() 21 | 22 | log.Println("golocproxy client starting: ", *local, "->", *remote) 23 | for { 24 | connectServer() 25 | time.Sleep(10 * time.Second) //retry after 10s 26 | } 27 | } 28 | 29 | func connectServer() { 30 | proxy, err := net.DialTimeout("tcp", *remote, 5*time.Second) 31 | if err != nil { 32 | log.Println("CAN'T CONNECT:", *remote, " err:", err) 33 | return 34 | } 35 | defer proxy.Close() 36 | util.WriteString(proxy, *pwd+"\n"+util.C2P_CONNECT) 37 | 38 | for { 39 | proxy.SetReadDeadline(time.Now().Add(2 * time.Second)) 40 | msg, err := util.ReadString(proxy) 41 | // proxy.SetReadDeadline(time.Time{}) 42 | if err == nil { 43 | if msg == util.P2C_NEW_SESSION { 44 | go session() 45 | } else { 46 | log.Println(msg) 47 | } 48 | } else { 49 | if nerr, ok := err.(net.Error); ok && nerr.Timeout() { 50 | //log.Println("Timeout") 51 | proxy.SetWriteDeadline(time.Now().Add(2 * time.Second)) 52 | _, werr := util.WriteString(proxy, util.C2P_KEEP_ALIVE) //send KeepAlive msg 53 | if werr != nil { 54 | log.Println("CAN'T WRITE, err:", werr) 55 | return 56 | } 57 | 58 | continue 59 | } else { 60 | log.Println("SERVER CLOSE, err:", err) 61 | return 62 | } 63 | } 64 | //time.Sleep(2*time.Second) 65 | } 66 | 67 | } 68 | 69 | //客户端单次连接处理 70 | func session() { 71 | log.Println("Create Session") 72 | rp, err := net.Dial("tcp", *remote) 73 | if err != nil { 74 | log.Println("Can't' connect:", *remote, " err:", err) 75 | return 76 | } 77 | //defer util.CloseConn(rp) 78 | util.WriteString(rp, *pwd+"\n"+util.C2P_SESSION) 79 | lp, err := net.Dial("tcp", *local) 80 | if err != nil { 81 | log.Println("Can't' connect:", *local, " err:", err) 82 | rp.Close() 83 | return 84 | } 85 | go util.CopyFromTo(rp, lp, nil) 86 | go util.CopyFromTo(lp, rp, nil) 87 | } 88 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "../util" 5 | "flag" 6 | // "io" 7 | "log" 8 | "net" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | var ( 14 | _port = flag.String("p", "8010", "The Listen port of golocproxy, golocproxy client will access the port.") 15 | _userport = flag.String("up", "8020", "The Listen port of user connect.") 16 | _pwd = flag.String("pwd", "jjg", "Password to valid Client Proxy") 17 | ) 18 | 19 | type OnConnectFunc func(net.Conn, chan net.Conn) 20 | 21 | func main() { 22 | flag.Usage = util.Usage 23 | flag.Parse() 24 | //flag.Usage() 25 | 26 | chSession := make(chan net.Conn, 100) 27 | if nil != listen(*_port, chSession, onClientConnect) { 28 | return 29 | } 30 | if nil != listen(*_userport, chSession, onUserConnect) { 31 | return 32 | } 33 | time.Sleep(999999 * time.Hour) 34 | } 35 | 36 | func listen(port string, chSession chan net.Conn, onConnect OnConnectFunc) error { 37 | server, err := net.Listen("tcp", net.JoinHostPort("0.0.0.0", port)) 38 | if err != nil { 39 | log.Fatal("CAN'T LISTEN: ", err) 40 | return err 41 | } 42 | log.Println("listen port:", port) 43 | go func() { 44 | defer server.Close() 45 | for { 46 | conn, err := server.Accept() 47 | if err != nil { 48 | log.Println("Can't Accept: ", err) 49 | continue 50 | } 51 | go onConnect(conn, chSession) 52 | } 53 | }() 54 | return nil 55 | } 56 | 57 | func onClientConnect(conn net.Conn, chSession chan net.Conn) { 58 | strConn := util.Conn2Str(conn) 59 | log.Println("Proxy Client Connect:", strConn) 60 | 61 | conn.SetReadDeadline(time.Now().Add(5 * time.Second)) 62 | msg, err := util.ReadString(conn) 63 | conn.SetReadDeadline(time.Time{}) 64 | //println("Read:", string(buf[0:n])) 65 | if err != nil { 66 | log.Println("Can't Read: ", err) 67 | conn.Close() 68 | return 69 | } 70 | msgs := strings.Split(msg, "\n") 71 | pwd := msgs[0] 72 | if *_pwd != pwd { 73 | util.CloseConn(conn) 74 | return 75 | } 76 | token := msgs[1] 77 | //log.Println("token=", token) 78 | if token == util.C2P_CONNECT { 79 | //内网服务器启动时连接代理,建立长连接 80 | clientConnect(conn) 81 | return 82 | } else if token == util.C2P_SESSION { 83 | //为客户端的单次连接请求建立一个临时的"内网服务器<->代理"的连接 84 | initUserSession(conn, chSession) 85 | return 86 | } 87 | 88 | } 89 | 90 | //代理客户端连接 91 | var _clientProxy net.Conn = nil 92 | 93 | //处理golocproxy client的连接 94 | func clientConnect(conn net.Conn) { 95 | defer util.CloseConn(conn) // conn.Close() 96 | if _clientProxy != nil { 97 | util.WriteString(conn, "SERVICE EXIST") 98 | util.CloseConn(conn) 99 | return 100 | } 101 | println("REG SERVICE") 102 | _clientProxy = conn 103 | defer func() { 104 | _clientProxy = nil 105 | }() 106 | for { 107 | _, err := util.ReadString(_clientProxy) 108 | if err != nil { 109 | log.Println("UNREG SERVICE") 110 | break 111 | } 112 | } 113 | } 114 | 115 | func initUserSession(conn net.Conn, chSession chan net.Conn) { 116 | chSession <- conn 117 | } 118 | 119 | //处理最终用户的连接 120 | func onUserConnect(conn net.Conn, chSession chan net.Conn) { 121 | if _clientProxy == nil { 122 | conn.Write([]byte("NO SERVICE")) 123 | util.CloseConn(conn) 124 | return 125 | } 126 | _, err := util.WriteString(_clientProxy, util.P2C_NEW_SESSION) 127 | if err != nil { 128 | conn.Write([]byte("SERVICE FAIL")) 129 | util.CloseConn(conn) 130 | return 131 | } 132 | connSession := recvSession(chSession) // := <-chSession 133 | if connSession == nil { 134 | util.CloseConn(conn) 135 | return 136 | } 137 | log.Println("Transfer...") 138 | go util.CopyFromTo(conn, connSession, nil) 139 | go util.CopyFromTo(connSession, conn, nil) 140 | } 141 | 142 | //加入超时 143 | func recvSession(ch chan net.Conn) net.Conn { 144 | var conn net.Conn = nil 145 | select { 146 | case conn = <-ch: 147 | case <-time.After(time.Second * 5): 148 | conn = nil 149 | } 150 | return conn 151 | } 152 | -------------------------------------------------------------------------------- /util/util.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "flag" 7 | "fmt" 8 | "io" 9 | "net" 10 | "os" 11 | ) 12 | 13 | //TOKEN 14 | const ( 15 | TOKEN_LEN = 4 16 | C2P_CONNECT = "C2P0" 17 | C2P_SESSION = "C2P1" 18 | C2P_KEEP_ALIVE = "C2P2" 19 | P2C_NEW_SESSION = "P2C1" 20 | SEPS = "\n" 21 | ) 22 | 23 | func Usage() { 24 | fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS]\n", os.Args[0]) 25 | fmt.Fprintf(os.Stderr, "Options:\n") 26 | flag.PrintDefaults() 27 | } 28 | 29 | func Conn2Str(conn net.Conn) string { 30 | return conn.LocalAddr().String() + " <-> " + conn.RemoteAddr().String() 31 | } 32 | 33 | func CopyFromTo(r, w io.ReadWriteCloser, buf []byte) { 34 | defer CloseConn(r) 35 | if buf != nil && len(buf) > 0 { 36 | _, err := w.Write(buf) 37 | if err != nil { 38 | return 39 | } 40 | } 41 | io.Copy(r, w) 42 | } 43 | 44 | func CloseConn(a io.ReadWriteCloser) { 45 | fmt.Println("CLOSE") 46 | a.Close() 47 | } 48 | 49 | func WriteString(w io.Writer, str string) (int, error) { 50 | binary.Write(w, binary.LittleEndian, int32(len(str))) 51 | return w.Write([]byte(str)) 52 | } 53 | 54 | const MAX_STRING = 10240 55 | 56 | func ReadString(r io.Reader) (string, error) { 57 | var size int32 58 | err := binary.Read(r, binary.LittleEndian, &size) 59 | if err != nil { 60 | return "", err 61 | } 62 | if size > MAX_STRING { 63 | return "", errors.New("too long string") 64 | } 65 | 66 | buff := make([]byte, size) 67 | n, err := r.Read(buff) 68 | if err != nil { 69 | return "", err 70 | } 71 | if int32(n) != size { 72 | return "", errors.New("invalid string size") 73 | } 74 | return string(buff), nil 75 | } 76 | --------------------------------------------------------------------------------