Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
Bluek404 committed Aug 1, 2015
1 parent 0d63de5 commit 08aac45
Show file tree
Hide file tree
Showing 3 changed files with 356 additions and 1 deletion.
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,24 @@
# tcprp
TCP Reverse Proxy,可反向代理任何TCP链接
TCP Reverse Proxy,可反向代理任何内网TCP链接

## 编译

```
go get github.com/Bluek404/tcprp/...
```

## 使用

客户端放在需要代理的内网服务器上

`./client 目标服务器 代理服务器 密钥`

例如: `./client 127.0.0.1:8080 example.com:8091 KEYKEY` (此处example.com代指服务器地址)

服务端放在有公网IP的服务器上

`./server 服务端口 代理通信端口 密钥`

例如: `./server :80 :8091 KEYKEY`

然后用户访问*example.com:80*就可以了
138 changes: 138 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*/ //
* The MIT License (MIT)
*
* Copyright (c) 2015 Bluek404
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
/*/ //

package main

import (
"fmt"
"io"
"log"
"net"
"os"
"time"
)

const (
CMD byte = iota
PROXY
)

var proxyServer, targetServer, key string

func init() {
if len(os.Args) != 4 {
fmt.Printf("用法:\n"+
"%v 目标服务器 代理服务器 密钥\n", os.Args[0])
os.Exit(2)
}
targetServer, proxyServer, key = os.Args[1], os.Args[2], os.Args[3]
}

func c2Server(sleep time.Duration) net.Conn {
time.Sleep(time.Second * sleep)
conn, err := net.Dial("tcp", proxyServer)
if err != nil {
log.Println("连接服务端失败:", err, "将于", int(sleep+1), "秒后重试")
return c2Server(sleep + 1)
}
_, err = conn.Write(append([]byte{CMD}, []byte(key)...))
if err != nil {
log.Println(err)
os.Exit(1)
}
return conn
}

func copy(dst, src net.Conn) {
io.Copy(dst, src)
dst.Close()
src.Close()
}

func proxy() {
c, err := net.Dial("tcp", proxyServer)
if err != nil {
log.Println(err)
return
}
_, err = c.Write(append([]byte{PROXY}, []byte(key)...))
if err != nil {
log.Println(err)
os.Exit(1)
}
// 读取用户IP长度
buf := make([]byte, 1)
n, err := c.Read(buf)
if err != nil {
log.Println(err)
c.Close()
return
}
if n != 1 {
log.Println("读取用户IP长度失败")
c.Close()
return
}
ipLen := buf[0]
buf = make([]byte, ipLen)
// 读取用户真实IP
n, err = c.Read(buf)
if err != nil {
log.Println(err)
c.Close()
return
}
if n != int(ipLen) {
log.Println("用户IP长度错误")
c.Close()
return
}
clientIP := string(buf)
// 与需代理的程序连接
pc, err := net.Dial("tcp", targetServer)
if err != nil {
log.Println(err)
c.Close()
return
}
log.Printf("[+]: %v(%v)\n", clientIP, pc.LocalAddr())
defer log.Printf("[-]: %v(%v)\n", clientIP, pc.LocalAddr())
go copy(pc, c)
copy(c, pc)
}

func main() {
conn := c2Server(0)
defer conn.Close()

buf := make([]byte, 1)
for {
_, err := conn.Read(buf)
if err != nil {
log.Println(err)
conn = c2Server(0)
}
go proxy()
}
}
195 changes: 195 additions & 0 deletions server/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/*/ //
* The MIT License (MIT)
*
* Copyright (c) 2015 Bluek404
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
/*/ //

package main

import (
"fmt"
"io"
"log"
"net"
"os"
)

const proxyBuffSize = 128

const (
CMD byte = iota
PROXY
)

var (
serverPort, proxyPort, key string
exit = make(chan int)
proxy = &Proxy{
request: make(chan bool, proxyBuffSize),
response: make(chan net.Conn, proxyBuffSize),
destroy: make(chan bool),
}
)

func init() {
if len(os.Args) != 4 {
fmt.Printf("用法:\n"+
"%v 服务端口 代理通信端口 密钥\n", os.Args[0])
os.Exit(2)
}
serverPort, proxyPort, key = os.Args[1], os.Args[2], os.Args[3]
}

func copy(dst, src net.Conn) {
io.Copy(dst, src)
dst.Close()
src.Close()
}

// 与用户通信
func serve() {
ln, err := net.Listen("tcp", serverPort)
if err != nil {
log.Println(err)
exit <- 1
}
for {
conn, err := ln.Accept()
if err != nil {
log.Println(err)
continue
}
go func() {
log.Println("[+]:", conn.RemoteAddr())
defer log.Println("[-]:", conn.RemoteAddr())
pconn := proxy.getConn()
if pconn == nil {
// 代理客户端不在线
conn.Close()
return
}
go copy(conn, pconn)
copy(pconn, conn)
}()
}
}

type Proxy struct {
request chan bool
response chan net.Conn
// 因为没用心跳包,
// 所以只能靠用户请求来检测在线,
// 但是如果还没有用户请求代理客户端就下线并重新上线,
// 需要手动销毁上一个已关闭链接
destroy chan bool
online bool
}

// 与代理客户端通信
func (p *Proxy) serve() {
go func() { <-p.destroy }() // 接收首次销毁请求,看下面就明白了
ln, err := net.Listen("tcp", proxyPort)
if err != nil {
log.Println(err)
exit <- 1
}
for {
conn, err := ln.Accept()
if err != nil {
log.Println(err)
continue
}
go func() {
clientIP := conn.RemoteAddr().String()
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
log.Println(err)
conn.Close()
return
}
k := string(buf[1:n])
// TODO: 支持超长key和登录后使用session代替key
if k != key {
// 认证失败
log.Println(conn.RemoteAddr(), "错误key:", k)
conn.Close()
return
}
// 首位字节为请求类型
switch buf[0] {
case CMD:
log.Println("[+]代理客户端:", clientIP)
defer log.Println("[-]代理客户端:", clientIP)
p.destroy <- true // 请求销毁上一个链接
defer conn.Close()
p.online = true
for {
select {
case <-p.destroy:
// 新的代理客户端连线,销毁这个
return
case <-p.request:
_, err = conn.Write([]byte{0})
if err != nil {
log.Println(err)
p.response <- nil
p.online = false
// 因为现在已经销毁,所以需要开一个线程
// 用于接收代理客户端上线时的销毁请求
go func() { <-p.destroy }()
return
}
}
}
case PROXY:
// 发送用户真实IP给代理客户端
// 首位字节为IP的字节长度
ipByte := []byte(clientIP)
l := byte(len(ipByte))
_, err = conn.Write(append([]byte{l}, ipByte...))
if err != nil {
log.Println(err)
return
}
p.response <- conn
}
}()
}
}

func (p *Proxy) getConn() net.Conn {
if !p.online {
return nil
}
p.request <- true
c := <-p.response
if c == nil {
return c
}
return c
}

func main() {
go serve()
go proxy.serve()
os.Exit(<-exit)
}

0 comments on commit 08aac45

Please sign in to comment.