From 3a1ea87c31a802368f25d2dbe4991f54f242bc75 Mon Sep 17 00:00:00 2001 From: "rui.zheng" Date: Wed, 14 Sep 2016 12:51:47 +0800 Subject: [PATCH] add udp connect for udp port forwarding --- README.md | 6 +-- conn.go | 112 ++++++++++++++++++++++++++++++++++++++++------------- forward.go | 47 +++++++++++++++++----- socks.go | 90 +++++++++++++++++++++++++++++++++++++++++- udp.go | 8 ++-- 5 files changed, 218 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index a675031..cf31a10 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ gost -L=rtcp://:2222/192.168.1.1:22 -F=... -F=socks://172.24.10.1:1080 ``` 将172.24.10.1:2222上的数据(通过转发链)转发到192.168.1.1:22上。 -#### 远程端口转发(UDP +#### 远程端口转发(UDP) ```bash gost -L=rudp://:5353/192.168.1.1:53 -F=... -F=socks://172.24.10.1:1080 @@ -182,7 +182,7 @@ gost -L=:8080 -F=ss://aes-128-cfb:123456@server_ip:8338 ``` #### TLS -gost内置了tls证书,如果需要使用其他tls证书,在gost目录放置cert.pem(公钥)和key.pem(私钥)两个文件即可。 +gost内置了tls证书,如果需要使用其他tls证书,在gost运行目录放置cert.pem(公钥)和key.pem(私钥)两个文件即可。 SOCKS5 UDP数据处理 @@ -201,7 +201,7 @@ gost作为标准socks5代理处理UDP数据 -当设置转发代理时,gost会使用UDP-Over-TCP方式转发UDP数据。proxy1 - proxyN可以为任意http(s)/socks5/shadowsocks类型代理。 +当设置转发代理时,gost会使用UDP-over-TCP方式转发UDP数据。proxy1 - proxyN可以为任意http(s)/socks5/shadowsocks类型代理。 限制条件 ------ diff --git a/conn.go b/conn.go index d73a988..4d077ad 100644 --- a/conn.go +++ b/conn.go @@ -76,10 +76,12 @@ func listenAndServe(arg Args) error { glog.V(LWARNING).Infoln(err) continue } + if tc, ok := conn.(*net.TCPConn); ok { tc.SetKeepAlive(true) tc.SetKeepAlivePeriod(time.Second * 180) } + go handleConn(conn, arg) } } @@ -91,15 +93,48 @@ func listenAndServeTcpForward(arg Args) error { } defer ln.Close() + raddr, err := net.ResolveTCPAddr("tcp", arg.Remote) + if err != nil { + return err + } + for { conn, err := ln.Accept() if err != nil { glog.V(LWARNING).Infoln(err) continue } - go handleTcpForward(conn, arg) + go handleTcpForward(conn, raddr) } - return nil +} + +func prepareUdpConnectTunnel(addr net.Addr) (net.Conn, error) { + conn, _, err := forwardChain(forwardArgs...) + if err != nil { + return nil, err + } + + conn.SetWriteDeadline(time.Now().Add(time.Second * 90)) + if err = gosocks5.NewRequest(CmdUdpConnect, ToSocksAddr(addr)).Write(conn); err != nil { + conn.Close() + return nil, err + } + conn.SetWriteDeadline(time.Time{}) + + conn.SetReadDeadline(time.Now().Add(90 * time.Second)) + reply, err := gosocks5.ReadReply(conn) + if err != nil { + conn.Close() + return nil, err + } + conn.SetReadDeadline(time.Time{}) + + if reply.Rep != gosocks5.Succeeded { + conn.Close() + return nil, errors.New("udp connect failure") + } + + return conn, nil } func listenAndServeUdpForward(arg Args) error { @@ -107,24 +142,61 @@ func listenAndServeUdpForward(arg Args) error { if err != nil { return err } - ln, err := net.ListenUDP("udp", laddr) + + raddr, err := net.ResolveUDPAddr("udp", arg.Remote) if err != nil { return err } - defer ln.Close() for { - b := udpPool.Get().([]byte) + var conn *net.UDPConn - n, raddr, err := ln.ReadFromUDP(b) - if err != nil { - glog.V(LWARNING).Infoln(err) - continue + for { + conn, err = net.ListenUDP("udp", laddr) + if err != nil { + glog.V(LWARNING).Infof("[udp-connect] %s -> %s : %s", laddr, raddr, err) + time.Sleep((1) * time.Second) + continue + } + break } - go func(data []byte, length int) { - handleUdpForward(ln, raddr, data[:length], arg) - udpPool.Put(data) - }(b, n) + + if len(forwardArgs) == 0 { + defer conn.Close() + + for { + b := udpPool.Get().([]byte) + + n, addr, err := conn.ReadFromUDP(b) + if err != nil { + glog.V(LWARNING).Infoln(err) + continue + } + go func() { + handleUdpForwardLocal(conn, addr, raddr, b[:n]) + udpPool.Put(b) + }() + } + } + + var tun net.Conn + retry := 0 + for { + tun, err = prepareUdpConnectTunnel(raddr) + if err != nil { + glog.V(LWARNING).Infof("[udp-connect] %s -> %s : %s", laddr, raddr, err) + time.Sleep((1 << uint(retry)) * time.Second) + if retry < 5 { + retry++ + } + continue + } + break + } + glog.V(LWARNING).Infof("[udp-connect] %s <-> %s : %s", laddr, raddr) + tunnelUDP(conn, tun, false) + glog.V(LWARNING).Infof("[udp-connect] %s >-< %s : %s", laddr, raddr) + conn.Close() } } @@ -179,19 +251,6 @@ func serveRUdpForward(arg Args) error { } func handleConn(conn net.Conn, arg Args) { - /* - atomic.AddInt32(&connCounter, 1) - glog.V(LDEBUG).Infof("%s connected, connections: %d", - conn.RemoteAddr(), atomic.LoadInt32(&connCounter)) - - if glog.V(LDEBUG) { - defer func() { - glog.Infof("%s disconnected, connections: %d", - conn.RemoteAddr(), atomic.LoadInt32(&connCounter)) - }() - } - defer atomic.AddInt32(&connCounter, -1) - */ defer conn.Close() // socks5 server supported methods @@ -335,6 +394,7 @@ func forwardChain(chain ...Args) (conn net.Conn, end Args, err error) { if conn, err = net.DialTimeout("tcp", end.Addr, time.Second*90); err != nil { return } + tc := conn.(*net.TCPConn) tc.SetKeepAlive(true) tc.SetKeepAlivePeriod(time.Second * 180) // 3min diff --git a/forward.go b/forward.go index 3901ed1..e50738a 100644 --- a/forward.go +++ b/forward.go @@ -10,23 +10,50 @@ import ( "time" ) -func handleTcpForward(conn net.Conn, arg Args) { +func handleTcpForward(conn net.Conn, raddr net.Addr) { defer conn.Close() - if !strings.Contains(arg.Remote, ":") { - arg.Remote += ":22" // default is ssh service - } - glog.V(LINFO).Infof("[tcp-forward] %s - %s", conn.RemoteAddr(), arg.Remote) - c, err := Connect(arg.Remote) + glog.V(LINFO).Infof("[tcp-forward] %s - %s", conn.RemoteAddr(), raddr) + c, err := Connect(raddr.String()) if err != nil { - glog.V(LWARNING).Infof("[tcp-forward] %s -> %s : %s", conn.RemoteAddr(), arg.Remote, err) + glog.V(LWARNING).Infof("[tcp-forward] %s -> %s : %s", conn.RemoteAddr(), raddr, err) return } defer c.Close() - glog.V(LINFO).Infof("[tcp-forward] %s <-> %s", conn.RemoteAddr(), arg.Remote) + glog.V(LINFO).Infof("[tcp-forward] %s <-> %s", conn.RemoteAddr(), raddr) Transport(conn, c) - glog.V(LINFO).Infof("[tcp-forward] %s >-< %s", conn.RemoteAddr(), arg.Remote) + glog.V(LINFO).Infof("[tcp-forward] %s >-< %s", conn.RemoteAddr(), raddr) +} + +func handleUdpForwardLocal(conn *net.UDPConn, laddr, raddr *net.UDPAddr, data []byte) { + lconn, err := net.ListenUDP("udp", nil) + if err != nil { + glog.V(LWARNING).Infof("[udp-forward] %s -> %s : %s", laddr, raddr, err) + return + } + defer lconn.Close() + + if _, err := lconn.WriteToUDP(data, raddr); err != nil { + glog.V(LWARNING).Infof("[udp-forward] %s -> %s : %s", laddr, raddr, err) + return + } + glog.V(LDEBUG).Infof("[udp-forward] %s >>> %s length %d", laddr, raddr, len(data)) + + b := udpPool.Get().([]byte) + defer udpPool.Put(b) + lconn.SetReadDeadline(time.Now().Add(time.Second * 60)) + n, addr, err := lconn.ReadFromUDP(b) + if err != nil { + glog.V(LWARNING).Infof("[udp-forward] %s <- %s : %s", laddr, raddr, err) + return + } + glog.V(LDEBUG).Infof("[udp-forward] %s <<< %s length %d", laddr, addr, n) + + if _, err := conn.WriteToUDP(b[:n], laddr); err != nil { + glog.V(LWARNING).Infof("[udp-forward] %s <- %s : %s", laddr, raddr, err) + } + return } func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Args) { @@ -141,7 +168,7 @@ func connectRTcpForward(conn net.Conn, arg Args) error { req := gosocks5.NewRequest(gosocks5.CmdBind, ToSocksAddr(addr)) bindAddr := req.Addr if err := req.Write(conn); err != nil { - glog.V(LWARNING).Infof("[rtcp] %s <- %s : %s", bindAddr, arg.Remote, err) + glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", bindAddr, arg.Remote, err) return err } diff --git a/socks.go b/socks.go index f84958d..9165d50 100644 --- a/socks.go +++ b/socks.go @@ -21,7 +21,8 @@ const ( ) const ( - CmdUdpTun uint8 = 0xf3 // extended method for udp over tcp + CmdUdpConnect uint8 = 0xF1 // extended method for udp local port forwarding + CmdUdpTun uint8 = 0xF3 // extended method for udp over tcp ) type clientSelector struct { @@ -218,6 +219,10 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { Transport(conn, fconn) glog.V(LINFO).Infof("[socks5-bind] %s >-< %s", conn.RemoteAddr(), fconn.RemoteAddr()) + case CmdUdpConnect: + glog.V(LINFO).Infof("[udp-connect] %s - %s", conn.RemoteAddr(), req.Addr) + udpConnect(req, conn) + case gosocks5.CmdUdp: glog.V(LINFO).Infof("[socks5-udp] %s - %s", conn.RemoteAddr(), req.Addr) socks5UDP(req, conn) @@ -240,6 +245,87 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { } } +func udpConnect(req *gosocks5.Request, conn net.Conn) error { + if len(forwardArgs) > 0 { // direct forwarding + fconn, _, err := forwardChain(forwardArgs...) + if err != nil { + glog.V(LINFO).Infof("[udp-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) + gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) + return err + } + defer fconn.Close() + + if err := req.Write(fconn); err != nil { + glog.V(LINFO).Infof("[udp-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) + gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) + return err + } + + glog.V(LINFO).Infof("[udp-connect] %s <-> %s", conn.RemoteAddr(), req.Addr) + err = Transport(conn, fconn) + glog.V(LINFO).Infof("[udp-connect] %s >-< %s", conn.RemoteAddr(), req.Addr) + return err + } + + raddr, err := net.ResolveUDPAddr("udp", req.Addr.String()) + if err != nil { + glog.V(LINFO).Infof("[udp-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) + gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) + return err + } + + if err := gosocks5.NewReply(gosocks5.Succeeded, nil).Write(conn); err != nil { + glog.V(LINFO).Infof("[udp-connect] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) + return err + } + + glog.V(LINFO).Infof("[udp-connect] %s <-> %s", conn.RemoteAddr(), raddr) + defer glog.V(LINFO).Infof("[udp-connect] %s >-< %s", conn.RemoteAddr(), raddr) + + for { + dgram, err := gosocks5.ReadUDPDatagram(conn) + if err != nil { + glog.V(LWARNING).Infof("[udp-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) + return err + } + + go func() { + b := udpPool.Get().([]byte) + defer udpPool.Put(b) + + relay, err := net.DialUDP("udp", nil, raddr) + if err != nil { + glog.V(LWARNING).Infof("[udp-connect] %s -> %s : %s", conn.RemoteAddr(), raddr, err) + return + } + defer relay.Close() + + if _, err := relay.Write(dgram.Data); err != nil { + glog.V(LWARNING).Infof("[udp-connect] %s -> %s : %s", conn.RemoteAddr(), raddr, err) + return + } + glog.V(LDEBUG).Infof("[udp-connect] %s >>> %s length: %d", conn.RemoteAddr(), raddr, len(dgram.Data)) + + relay.SetReadDeadline(time.Now().Add(time.Second * 60)) + n, err := relay.Read(b) + if err != nil { + glog.V(LWARNING).Infof("[udp-connect] %s <- %s : %s", conn.RemoteAddr(), raddr, err) + return + } + relay.SetReadDeadline(time.Time{}) + + glog.V(LDEBUG).Infof("[udp-connect] %s <<< %s length: %d", conn.RemoteAddr(), raddr, n) + + conn.SetWriteDeadline(time.Now().Add(time.Second * 90)) + if err := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(uint16(n), 0, dgram.Header.Addr), b[:n]).Write(conn); err != nil { + glog.V(LWARNING).Infof("[udp-connect] %s <- %s : %s", conn.RemoteAddr(), raddr, err) + return + } + conn.SetWriteDeadline(time.Time{}) + }() + } +} + func socks5UDP(req *gosocks5.Request, conn net.Conn) error { bindAddr, _ := net.ResolveUDPAddr("udp", req.Addr.String()) relay, err := net.ListenUDP("udp", bindAddr) // udp associate, strict mode: if the port already in use, it will return error @@ -319,7 +405,7 @@ func socks5UDP(req *gosocks5.Request, conn net.Conn) error { } func socks5TunnelUDP(req *gosocks5.Request, conn net.Conn) error { - if len(forwardArgs) > 0 { // tunnel -> tunnel, direct forward + if len(forwardArgs) > 0 { // tunnel -> tunnel, direct forwarding tun, _, err := forwardChain(forwardArgs...) if err != nil { return err diff --git a/udp.go b/udp.go index b21d37c..f751211 100644 --- a/udp.go +++ b/udp.go @@ -111,7 +111,7 @@ func tunnelUDP(conn *net.UDPConn, tun net.Conn, client bool) (err error) { rChan <- err return } - glog.V(LDEBUG).Infof("[socks5-udp] %s >>> %s length: %d", conn.LocalAddr(), dgram.Header.Addr, len(dgram.Data)) + glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", conn.LocalAddr(), dgram.Header.Addr, len(dgram.Data)) } else { // pipe from peer to tunnel dgram = gosocks5.NewUDPDatagram( gosocks5.NewUDPHeader(uint16(n), 0, ToSocksAddr(addr)), b[:n]) @@ -119,7 +119,7 @@ func tunnelUDP(conn *net.UDPConn, tun net.Conn, client bool) (err error) { rChan <- err return } - glog.V(LDEBUG).Infof("[socks5-udp] %s <<< %s length: %d", tun.RemoteAddr(), dgram.Header.Addr, len(dgram.Data)) + glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", tun.RemoteAddr(), dgram.Header.Addr, len(dgram.Data)) } } }() @@ -144,7 +144,7 @@ func tunnelUDP(conn *net.UDPConn, tun net.Conn, client bool) (err error) { wChan <- err return } - glog.V(LDEBUG).Infof("[socks5-udp] %s <<< %s length: %d", conn.LocalAddr(), dgram.Header.Addr, len(dgram.Data)) + glog.V(LDEBUG).Infof("[udp-tun] %s <<< %s length: %d", conn.LocalAddr(), dgram.Header.Addr, len(dgram.Data)) } else { // pipe from tunnel to peer addr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String()) if err != nil { @@ -154,7 +154,7 @@ func tunnelUDP(conn *net.UDPConn, tun net.Conn, client bool) (err error) { wChan <- err return } - glog.V(LDEBUG).Infof("[socks5-udp] %s >>> %s length: %d", tun.RemoteAddr(), addr, len(dgram.Data)) + glog.V(LDEBUG).Infof("[udp-tun] %s >>> %s length: %d", tun.RemoteAddr(), addr, len(dgram.Data)) } } }()