This commit is contained in:
esrrhs 2018-12-18 15:36:59 +08:00
parent 429083487b
commit 99b477a7dd
4 changed files with 202 additions and 72 deletions

View File

@ -7,39 +7,53 @@ import (
) )
var usage = ` var usage = `
通过伪造ping把udp流量通过远程服务器转发到目的服务器上用于突破某些运营商封锁UDP流量
By forging ping, the udp traffic is forwarded to the destination server through the remote server. Used to break certain operators to block UDP traffic.
Usage: Usage:
pingtunnel -type server pingtunnel -type server
pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -t 4455 pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -t SERVER_IP:4455
-type 服务器或者客户端
client or server
-l 本地的地址发到这个端口的流量将转发到服务器
Local address, traffic sent to this port will be forwarded to the server
-s 服务器的地址流量将通过隧道转发到这个服务器
The address of the server, the traffic will be forwarded to this server through the tunnel
-t 远端服务器转发的目的地址流量将转发到这个地址
Destination address forwarded by the remote server, traffic will be forwarded to this address
-timeout 本地记录连接超时的时间单位是秒
The time when the local record connection timed out, in seconds
` `
func main() { func main() {
fmt.Println("start...")
t := flag.String("type", "client", "client or server") t := flag.String("type", "", "client or server")
listen := flag.String("l", ":4455", "listen addr") listen := flag.String("l", "", "listen addr")
target := flag.Int("t", 4455, "target port") target := flag.String("t", "", "target addr")
server := flag.String("s", "127.0.0.1", "server addr") server := flag.String("s", "", "server addr")
timeout := flag.Int("timeout", 60, "conn timeout")
flag.Usage = func() { flag.Usage = func() {
fmt.Printf(usage) fmt.Printf(usage)
} }
flag.Parse() flag.Parse()
if flag.NArg() != 0 { if (*t != "client" && *t != "server") || (*t == "client" && (len(*listen) == 0 || len(*target) == 0 || len(*server) == 0)) {
flag.Usage() flag.Usage()
return return
} }
fmt.Printf("type %s\n", *t) fmt.Println("start...")
fmt.Printf("listen %s\n", *listen)
fmt.Printf("server %s\n", *server)
fmt.Printf("target port %d\n", *target)
if *t == "server" { if *t == "server" {
s, err := pingtunnel.NewServer() s, err := pingtunnel.NewServer(*timeout)
if err != nil { if err != nil {
fmt.Printf("ERROR: %s\n", err.Error()) fmt.Printf("ERROR: %s\n", err.Error())
return return
@ -48,13 +62,19 @@ func main() {
s.Run() s.Run()
} }
if *t == "client" { if *t == "client" {
c, err := pingtunnel.NewClient(*listen, *server, *target)
fmt.Printf("type %s\n", *t)
fmt.Printf("listen %s\n", *listen)
fmt.Printf("server %s\n", *server)
fmt.Printf("target %s\n", *target)
c, err := pingtunnel.NewClient(*listen, *server, *target, *timeout)
if err != nil { if err != nil {
fmt.Printf("ERROR: %s\n", err.Error()) fmt.Printf("ERROR: %s\n", err.Error())
return return
} }
fmt.Printf("Client Listen %s (%s) Server %s (%s) TargetPort %d:\n", c.Addr(), c.IPAddr(), fmt.Printf("Client Listen %s (%s) Server %s (%s) TargetPort %s:\n", c.Addr(), c.IPAddr(),
c.ServerAddr(), c.ServerIPAddr(), c.TargetPort()) c.ServerAddr(), c.ServerIPAddr(), c.TargetAddr())
c.Run() c.Run()
} }
} }

View File

@ -7,7 +7,7 @@ import (
"time" "time"
) )
func NewClient(addr string, server string, target int) (*Client, error) { func NewClient(addr string, server string, target string, timeout int) (*Client, error) {
ipaddr, err := net.ResolveUDPAddr("udp", addr) ipaddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil { if err != nil {
@ -24,24 +24,33 @@ func NewClient(addr string, server string, target int) (*Client, error) {
addr: addr, addr: addr,
ipaddrServer: ipaddrServer, ipaddrServer: ipaddrServer,
addrServer: server, addrServer: server,
targetPort: (uint16)(target), targetAddr: target,
timeout: timeout,
}, nil }, nil
} }
type Client struct { type Client struct {
timeout int
ipaddr *net.UDPAddr ipaddr *net.UDPAddr
addr string addr string
ipaddrServer *net.IPAddr ipaddrServer *net.IPAddr
addrServer string addrServer string
targetPort uint16 targetAddr string
conn *icmp.PacketConn conn *icmp.PacketConn
listenConn *net.UDPConn listenConn *net.UDPConn
localConnToIdMap map[string]uint32 localAddrToConnMap map[string]*ClientConn
localIdToConnMap map[uint32]*net.UDPAddr localIdToConnMap map[string]*ClientConn
}
type ClientConn struct {
ipaddr *net.UDPAddr
id string
activeTime time.Time
} }
func (p *Client) Addr() string { func (p *Client) Addr() string {
@ -52,8 +61,8 @@ func (p *Client) IPAddr() *net.UDPAddr {
return p.ipaddr return p.ipaddr
} }
func (p *Client) TargetPort() uint16 { func (p *Client) TargetAddr() string {
return p.targetPort return p.targetAddr
} }
func (p *Client) ServerIPAddr() *net.IPAddr { func (p *Client) ServerIPAddr() *net.IPAddr {
@ -82,16 +91,21 @@ func (p *Client) Run() {
defer listener.Close() defer listener.Close()
p.listenConn = listener p.listenConn = listener
p.localConnToIdMap = make(map[string]uint32) p.localAddrToConnMap = make(map[string]*ClientConn)
p.localIdToConnMap = make(map[uint32]*net.UDPAddr) p.localIdToConnMap = make(map[string]*ClientConn)
go p.Accept() go p.Accept()
recv := make(chan *Packet, 1000) recv := make(chan *Packet, 1000)
go recvICMP(*p.conn, recv) go recvICMP(*p.conn, recv)
interval := time.NewTicker(time.Second)
defer interval.Stop()
for { for {
select { select {
case <-interval.C:
p.checkTimeoutConn()
case r := <-recv: case r := <-recv:
p.processPacket(r) p.processPacket(r)
} }
@ -119,33 +133,58 @@ func (p *Client) Accept() error {
} }
} }
uuid := p.localConnToIdMap[srcaddr.String()] now := time.Now()
if uuid == 0 { clientConn := p.localAddrToConnMap[srcaddr.String()]
uuid = UniqueId() if clientConn == nil {
p.localConnToIdMap[srcaddr.String()] = uuid uuid := UniqueId()
p.localIdToConnMap[uuid] = srcaddr clientConn = &ClientConn{ipaddr: srcaddr, id: uuid, activeTime: now}
fmt.Printf("client accept new local %d %s\n", uuid, srcaddr.String()) p.localAddrToConnMap[srcaddr.String()] = clientConn
p.localIdToConnMap[uuid] = clientConn
fmt.Printf("client accept new local %s %s\n", uuid, srcaddr.String())
} }
sendICMP(*p.conn, p.ipaddrServer, p.targetPort, uuid, (uint32)(DATA), bytes[:n]) clientConn.activeTime = now
sendICMP(*p.conn, p.ipaddrServer, p.targetAddr, clientConn.id, (uint32)(DATA), bytes[:n])
} }
} }
func (p *Client) processPacket(packet *Packet) { func (p *Client) processPacket(packet *Packet) {
fmt.Printf("processPacket %d %s %d\n", packet.id, packet.src.String(), len(packet.data)) fmt.Printf("processPacket %s %s %d\n", packet.id, packet.src.String(), len(packet.data))
addr := p.localIdToConnMap[packet.id] clientConn := p.localIdToConnMap[packet.id]
if addr == nil { if clientConn == nil {
fmt.Printf("processPacket no conn %d \n", packet.id) fmt.Printf("processPacket no conn %s \n", packet.id)
return return
} }
addr := clientConn.ipaddr
now := time.Now()
clientConn.activeTime = now
_, err := p.listenConn.WriteToUDP(packet.data, addr) _, err := p.listenConn.WriteToUDP(packet.data, addr)
if err != nil { if err != nil {
fmt.Printf("WriteToUDP Error read udp %s\n", err) fmt.Printf("WriteToUDP Error read udp %s\n", err)
p.localConnToIdMap[addr.String()] = 0 p.Close(clientConn)
p.localIdToConnMap[packet.id] = nil
return return
} }
} }
func (p *Client) Close(clientConn *ClientConn) {
if p.localIdToConnMap[clientConn.id] != nil {
delete(p.localIdToConnMap, clientConn.id)
delete(p.localAddrToConnMap, clientConn.ipaddr.String())
}
}
func (p *Client) checkTimeoutConn() {
now := time.Now()
for id, conn := range p.localIdToConnMap {
diff := now.Sub(conn.activeTime)
if diff > time.Second*(time.Duration(p.timeout)) {
fmt.Printf("close inactive conn %s %s\n", id, conn.ipaddr.String())
p.Close(conn)
}
}
}

View File

@ -1,12 +1,16 @@
package pingtunnel package pingtunnel
import ( import (
"crypto/md5"
"crypto/rand"
"encoding/base64"
"encoding/binary" "encoding/binary"
"encoding/hex"
"fmt" "fmt"
"golang.org/x/net/icmp" "golang.org/x/net/icmp"
"golang.org/x/net/ipv4" "golang.org/x/net/ipv4"
"io"
"net" "net"
"sync/atomic"
"syscall" "syscall"
"time" "time"
) )
@ -19,9 +23,9 @@ const (
// An Echo represents an ICMP echo request or reply message body. // An Echo represents an ICMP echo request or reply message body.
type MyMsg struct { type MyMsg struct {
ID uint32
TYPE uint32 TYPE uint32
TARGET uint16 ID string
TARGET string
Data []byte Data []byte
} }
@ -30,37 +34,61 @@ func (p *MyMsg) Len(proto int) int {
if p == nil { if p == nil {
return 0 return 0
} }
return 10 + len(p.Data) return 4 + p.LenString(p.ID) + p.LenString(p.TARGET) + len(p.Data)
}
func (p *MyMsg) LenString(s string) int {
return 2 + len(s)
} }
// Marshal implements the Marshal method of MessageBody interface. // Marshal implements the Marshal method of MessageBody interface.
func (p *MyMsg) Marshal(proto int) ([]byte, error) { func (p *MyMsg) Marshal(proto int) ([]byte, error) {
b := make([]byte, p.Len(proto)) b := make([]byte, p.Len(proto))
binary.BigEndian.PutUint32(b[:4], uint32(p.ID))
binary.BigEndian.PutUint32(b[4:8], uint32(p.TYPE)) binary.BigEndian.PutUint32(b[:4], uint32(p.TYPE))
binary.BigEndian.PutUint16(b[8:10], uint16(p.TARGET))
copy(b[10:], p.Data) id := p.MarshalString(p.ID)
copy(b[4:], id)
target := p.MarshalString(p.TARGET)
copy(b[4+p.LenString(p.ID):], target)
copy(b[4+p.LenString(p.ID)+p.LenString(p.TARGET):], p.Data)
return b, nil return b, nil
} }
func (p *MyMsg) MarshalString(s string) []byte {
b := make([]byte, p.LenString(s))
binary.BigEndian.PutUint16(b[:2], uint16(len(s)))
copy(b[2:], []byte(s))
return b
}
// Marshal implements the Marshal method of MessageBody interface. // Marshal implements the Marshal method of MessageBody interface.
func (p *MyMsg) Unmarshal(b []byte) error { func (p *MyMsg) Unmarshal(b []byte) error {
p.ID = binary.BigEndian.Uint32(b[:4])
p.TYPE = binary.BigEndian.Uint32(b[4:8]) p.TYPE = binary.BigEndian.Uint32(b[:4])
p.TARGET = binary.BigEndian.Uint16(b[8:10])
p.Data = make([]byte, len(b[10:])) p.ID = p.UnmarshalString(b[4:])
copy(p.Data, b[10:])
p.TARGET = p.UnmarshalString(b[4+p.LenString(p.ID):])
p.Data = make([]byte, len(b[4+p.LenString(p.ID)+p.LenString(p.TARGET):]))
copy(p.Data, b[4+p.LenString(p.ID)+p.LenString(p.TARGET):])
return nil return nil
} }
var uuid uint32 func (p *MyMsg) UnmarshalString(b []byte) string {
len := binary.BigEndian.Uint16(b[:2])
func UniqueId() uint32 { data := make([]byte, len)
newValue := atomic.AddUint32(&uuid, 1) copy(data, b[2:])
return (uint32)(newValue) return string(data)
} }
func sendICMP(conn icmp.PacketConn, server *net.IPAddr, target uint16, connId uint32, msgType uint32, data []byte) { func sendICMP(conn icmp.PacketConn, server *net.IPAddr, target string, connId string, msgType uint32, data []byte) {
m := &MyMsg{ m := &MyMsg{
ID: connId, ID: connId,
@ -130,7 +158,22 @@ func recvICMP(conn icmp.PacketConn, recv chan<- *Packet) {
type Packet struct { type Packet struct {
data []byte data []byte
id uint32 id string
target uint16 target string
src *net.IPAddr src *net.IPAddr
} }
func UniqueId() string {
b := make([]byte, 48)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
return ""
}
return GetMd5String(base64.URLEncoding.EncodeToString(b))
}
func GetMd5String(s string) string {
h := md5.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}

View File

@ -4,25 +4,28 @@ import (
"fmt" "fmt"
"golang.org/x/net/icmp" "golang.org/x/net/icmp"
"net" "net"
"strconv"
"time" "time"
) )
func NewServer() (*Server, error) { func NewServer(timeout int) (*Server, error) {
return &Server{ return &Server{
timeout: timeout,
}, nil }, nil
} }
type Server struct { type Server struct {
timeout int
conn *icmp.PacketConn conn *icmp.PacketConn
localConnMap map[uint32]*Conn localConnMap map[string]*ServerConn
} }
type Conn struct { type ServerConn struct {
ipaddrTarget *net.UDPAddr ipaddrTarget *net.UDPAddr
conn *net.UDPConn conn *net.UDPConn
id uint32 id string
activeTime time.Time
} }
func (p *Server) Run() { func (p *Server) Run() {
@ -34,13 +37,18 @@ func (p *Server) Run() {
} }
p.conn = conn p.conn = conn
p.localConnMap = make(map[uint32]*Conn) p.localConnMap = make(map[string]*ServerConn)
recv := make(chan *Packet, 1000) recv := make(chan *Packet, 1000)
go recvICMP(*p.conn, recv) go recvICMP(*p.conn, recv)
interval := time.NewTicker(time.Second)
defer interval.Stop()
for { for {
select { select {
case <-interval.C:
p.checkTimeoutConn()
case r := <-recv: case r := <-recv:
p.processPacket(r) p.processPacket(r)
} }
@ -49,13 +57,15 @@ func (p *Server) Run() {
func (p *Server) processPacket(packet *Packet) { func (p *Server) processPacket(packet *Packet) {
fmt.Printf("processPacket %d %s %d\n", packet.id, packet.src.String(), len(packet.data)) fmt.Printf("processPacket %s %s %d\n", packet.id, packet.src.String(), len(packet.data))
now := time.Now()
id := packet.id id := packet.id
udpConn := p.localConnMap[id] udpConn := p.localConnMap[id]
if udpConn == nil { if udpConn == nil {
addr := ":" + strconv.Itoa((int)(packet.target)) addr := packet.target
ipaddrTarget, err := net.ResolveUDPAddr("udp", addr) ipaddrTarget, err := net.ResolveUDPAddr("udp", addr)
if err != nil { if err != nil {
fmt.Printf("Error ResolveUDPAddr for udp addr: %s %s\n", addr, err.Error()) fmt.Printf("Error ResolveUDPAddr for udp addr: %s %s\n", addr, err.Error())
@ -67,11 +77,13 @@ func (p *Server) processPacket(packet *Packet) {
fmt.Printf("Error listening for udp packets: %s\n", err.Error()) fmt.Printf("Error listening for udp packets: %s\n", err.Error())
return return
} }
udpConn = &Conn{conn: targetConn, ipaddrTarget: ipaddrTarget, id: id} udpConn = &ServerConn{conn: targetConn, ipaddrTarget: ipaddrTarget, id: id, activeTime: now}
p.localConnMap[id] = udpConn p.localConnMap[id] = udpConn
go p.Recv(udpConn, id, packet.src) go p.Recv(udpConn, id, packet.src)
} }
udpConn.activeTime = now
_, err := udpConn.conn.Write(packet.data) _, err := udpConn.conn.Write(packet.data)
if err != nil { if err != nil {
fmt.Printf("WriteToUDP Error %s\n", err) fmt.Printf("WriteToUDP Error %s\n", err)
@ -80,9 +92,9 @@ func (p *Server) processPacket(packet *Packet) {
} }
} }
func (p *Server) Recv(conn *Conn, id uint32, src *net.IPAddr) { func (p *Server) Recv(conn *ServerConn, id string, src *net.IPAddr) {
fmt.Printf("server waiting target response %s\n", conn.ipaddrTarget.String()) fmt.Printf("server waiting target response %s -> %s %s\n", conn.ipaddrTarget.String(), conn.id, conn.conn.LocalAddr().String())
bytes := make([]byte, 10240) bytes := make([]byte, 10240)
@ -102,13 +114,29 @@ func (p *Server) Recv(conn *Conn, id uint32, src *net.IPAddr) {
} }
} }
sendICMP(*p.conn, src, 0, id, (uint32)(DATA), bytes[:n]) now := time.Now()
conn.activeTime = now
sendICMP(*p.conn, src, "", id, (uint32)(DATA), bytes[:n])
} }
} }
func (p *Server) Close(conn *Conn) { func (p *Server) Close(conn *ServerConn) {
if p.localConnMap[conn.id] != nil { if p.localConnMap[conn.id] != nil {
conn.conn.Close() conn.conn.Close()
p.localConnMap[conn.id] = nil delete(p.localConnMap, conn.id)
} }
} }
func (p *Server) checkTimeoutConn() {
now := time.Now()
for id, conn := range p.localConnMap {
diff := now.Sub(conn.activeTime)
if diff > time.Second*(time.Duration(p.timeout)) {
fmt.Printf("close inactive conn %s %s\n", id, conn.ipaddrTarget.String())
p.Close(conn)
}
}
}