From 3c79cf44b7b1984326a806c755ee65fd66cceba3 Mon Sep 17 00:00:00 2001 From: "rui.zheng" Date: Tue, 30 Aug 2016 18:05:25 +0800 Subject: [PATCH] add udp port forwarding --- conn.go | 31 +++++++++++++++--------- forward.go | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++---- http.go | 21 +++++++++-------- socks.go | 51 +++++++++++++++++++++------------------- 4 files changed, 123 insertions(+), 49 deletions(-) diff --git a/conn.go b/conn.go index f3edf3a..c3c7d92 100644 --- a/conn.go +++ b/conn.go @@ -55,8 +55,7 @@ func listenAndServe(arg Args) error { case "tcp": // TCP port forwarding return listenAndServeTcpForward(arg) case "udp": // UDP port forwarding - //return listenAndServeUdpForward(arg) - return nil + return listenAndServeUdpForward(arg) default: ln, err = net.Listen("tcp", arg.Addr) } @@ -82,6 +81,8 @@ func listenAndServeTcpForward(arg Args) error { if err != nil { return err } + defer ln.Close() + for { conn, err := ln.Accept() if err != nil { @@ -93,29 +94,32 @@ func listenAndServeTcpForward(arg Args) error { return nil } -/* func listenAndServeUdpForward(arg Args) error { - addr, err := net.ResolveUDPAddr("udp", arg.Addr) + laddr, err := net.ResolveUDPAddr("udp", arg.Addr) if err != nil { return err } - ln, err := net.ListenUDP("udp", addr) + ln, err := net.ListenUDP("udp", laddr) if err != nil { return err } + defer ln.Close() + for { b := udpPool.Get().([]byte) - defer udpPool.Put(b) - _, c, err := ln.ReadFromUDP(b) + n, raddr, err := ln.ReadFromUDP(b) if err != nil { glog.V(LWARNING).Infoln(err) continue } - handleUdpForward(c, arg) + go func(data []byte, length int) { + handleUdpForward(ln, raddr, data[:length], arg) + udpPool.Put(data) + }(b, n) } } -*/ + func handleConn(conn net.Conn, arg Args) { atomic.AddInt32(&connCounter, 1) glog.V(LINFO).Infof("%s connected, connections: %d", @@ -261,6 +265,7 @@ func Connect(addr string) (conn net.Conn, err error) { return conn, nil } +// establish connection throughout the forward chain func forwardChain(chain ...Args) (conn net.Conn, end Args, err error) { end = chain[0] if conn, err = net.DialTimeout("tcp", end.Addr, time.Second*90); err != nil { @@ -294,10 +299,14 @@ func forward(conn net.Conn, arg Args) (net.Conn, error) { var err error if glog.V(LINFO) { proto := arg.Protocol - if proto == "default" { + trans := arg.Transport + if proto == "" { proto = "http" // default is http } - glog.Infof("forward: %s/%s %s", proto, arg.Transport, arg.Addr) + if trans == "" { // default is tcp + trans = "tcp" + } + glog.Infof("forward: %s/%s %s", proto, trans, arg.Addr) } var tlsUsed bool diff --git a/forward.go b/forward.go index 590eeb3..1979fa7 100644 --- a/forward.go +++ b/forward.go @@ -1,23 +1,84 @@ package main import ( + "github.com/ginuerzh/gosocks5" "github.com/golang/glog" "net" + "strings" ) func handleTcpForward(conn net.Conn, arg Args) { - glog.V(LINFO).Infoln("[tcp-forward] CONNECT", arg.Forward) + defer conn.Close() + glog.V(LINFO).Infof("[tcp-forward] %s -> %s", conn.RemoteAddr(), arg.Forward) c, err := Connect(arg.Forward) if err != nil { - glog.V(LWARNING).Infoln("[tcp-forward] CONNECT", arg.Forward, err) + glog.V(LWARNING).Infof("[tcp-forward] %s -> %s : %s", conn.RemoteAddr(), arg.Forward, err) return } defer c.Close() - glog.V(LINFO).Infoln("[tcp-forward] CONNECT", arg.Forward, "OK") + glog.V(LINFO).Infof("[tcp-forward] %s <-> %s OK", conn.RemoteAddr(), arg.Forward) Transport(conn, c) + glog.V(LINFO).Infof("[tcp-forward] %s >-< %s DISCONNECTED", conn.RemoteAddr(), arg.Forward) } -func handleUdpForward(conn *net.UDPConn, arg Args) { +func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Args) { + if !strings.Contains(arg.Forward, ":") { + arg.Forward += ":53" + } + glog.V(LINFO).Infof("[udp-forward] %s -> %s", raddr, arg.Forward) + fconn, _, err := forwardChain(forwardArgs...) + if err != nil { + glog.V(LWARNING).Infof("[udp-forward] %s -> %s : %s", raddr, arg.Forward, err) + if fconn != nil { + fconn.Close() + } + return + } + defer fconn.Close() + glog.V(LINFO).Infof("[udp-forward] %s -> %s ASSOCIATE", raddr, arg.Forward) + + req := gosocks5.NewRequest(CmdUdpTun, nil) + if err = req.Write(fconn); err != nil { + glog.V(LWARNING).Infof("[udp-forward] %s -> %s ASSOCIATE : %s", raddr, arg.Forward, err) + return + } + glog.V(LDEBUG).Infof("[udp-forward] %s -> %s\n%s", raddr, arg.Forward, req) + + rep, err := gosocks5.ReadReply(fconn) + if err != nil { + glog.V(LWARNING).Infof("[udp-forward] %s <- %s ASSOCIATE : %s", raddr, arg.Forward, err) + return + } + glog.V(LDEBUG).Infof("[udp-forward] %s <- %s\n%s", raddr, arg.Forward, rep) + if rep.Rep != gosocks5.Succeeded { + glog.V(LWARNING).Infof("[udp-forward] %s <- %s ASSOCIATE failured", raddr, arg.Forward) + return + } + glog.V(LINFO).Infof("[udp-forward] %s <-> %s ASSOCIATE ON %s OK", raddr, arg.Forward, rep.Addr) + + addr, err := net.ResolveUDPAddr("udp", arg.Forward) + if err != nil { + glog.V(LWARNING).Infof("[udp-forward] %s -> %s : %s", raddr, arg.Forward, err) + return + } + dgram := gosocks5.NewUDPDatagram( + gosocks5.NewUDPHeader(uint16(len(data)), 0, ToSocksAddr(addr)), data) + if err = dgram.Write(fconn); err != nil { + glog.V(LWARNING).Infof("[udp-forward] %s -> %s : %s", raddr, arg.Forward, err) + return + } + glog.V(LINFO).Infof("[udp-forward] %s >>> %s length %d", raddr, arg.Forward, len(data)) + + dgram, err = gosocks5.ReadUDPDatagram(fconn) + if err != nil { + glog.V(LWARNING).Infof("[udp-forward] %s <- %s : %s", raddr, arg.Forward, err) + return + } + glog.V(LINFO).Infof("[udp-forward] %s <<< %s length %d", raddr, arg.Forward, len(dgram.Data)) + + if _, err = conn.WriteToUDP(dgram.Data, raddr); err != nil { + glog.V(LWARNING).Infof("[udp-forward] %s <- %s : %s", raddr, arg.Forward, err) + } } diff --git a/http.go b/http.go index 8e7c55a..8feb5d1 100644 --- a/http.go +++ b/http.go @@ -18,7 +18,7 @@ func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) { glog.Infoln(string(dump)) } } - glog.V(LINFO).Infoln("[http] CONNECT", req.Host) + glog.V(LINFO).Infof("[http] %s -> %s", conn.RemoteAddr(), req.Host) var username, password string if arg.User != nil { @@ -34,21 +34,21 @@ func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) { "Proxy-Agent: gost/" + Version + "\r\n\r\n" if _, err := conn.Write([]byte(resp)); err != nil { - glog.V(LWARNING).Infoln(err) + glog.V(LWARNING).Infof("[http] %s <- %s : %s", conn.RemoteAddr(), req.Host, err) } - glog.V(LDEBUG).Infoln(resp) + glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, resp) - glog.V(LWARNING).Infoln("http: proxy authentication required") + glog.V(LWARNING).Infof("[http] %s <- %s : proxy authentication required", conn.RemoteAddr(), req.Host) return } c, err := Connect(req.Host) if err != nil { - glog.V(LWARNING).Infoln("[http] CONNECT", req.Host, err) + glog.V(LWARNING).Infof("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err) b := []byte("HTTP/1.1 503 Service unavailable\r\n" + "Proxy-Agent: gost/" + Version + "\r\n\r\n") - glog.V(LDEBUG).Infoln(string(b)) + glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b)) conn.Write(b) return } @@ -57,10 +57,10 @@ func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) { if req.Method == "CONNECT" { b := []byte("HTTP/1.1 200 Connection established\r\n" + "Proxy-Agent: gost/" + Version + "\r\n\r\n") - glog.V(LDEBUG).Infoln(string(b)) + glog.V(LDEBUG).Infof("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b)) if _, err := conn.Write(b); err != nil { - glog.V(LWARNING).Infoln(err) + glog.V(LWARNING).Infof("[http] %s <- %s : %s", conn.RemoteAddr(), req.Host, err) return } } else { @@ -68,13 +68,14 @@ func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) { req.Header.Set("Connection", "Keep-Alive") if err = req.Write(c); err != nil { - glog.V(LWARNING).Infoln(err) + glog.V(LWARNING).Infof("[http] %s -> %s : %s", conn.RemoteAddr(), req.Host, err) return } } - glog.V(LINFO).Infoln("[http] CONNECT", req.Host, "OK") + glog.V(LINFO).Infof("[http] %s <-> %s OK", conn.RemoteAddr(), req.Host) Transport(conn, c) + glog.V(LINFO).Infof("[http] %s >-< %s DISCONNECTED", conn.RemoteAddr(), req.Host) } func basicAuth(authInfo string) (username, password string, ok bool) { diff --git a/socks.go b/socks.go index ad04e54..8b7cc11 100644 --- a/socks.go +++ b/socks.go @@ -162,20 +162,20 @@ func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Con } func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { - glog.V(LDEBUG).Infoln(req) + glog.V(LDEBUG).Infof("[socks5-connect] %s -> %s\n%s", conn.RemoteAddr(), req.Addr, req) switch req.Cmd { case gosocks5.CmdConnect: - glog.V(LINFO).Infoln("[socks5] CONNECT", req.Addr) + glog.V(LINFO).Infof("[socks5-connect] %s -> %s", conn.RemoteAddr(), req.Addr) tconn, err := Connect(req.Addr.String()) if err != nil { - glog.V(LWARNING).Infoln("[socks5] CONNECT", req.Addr, err) + glog.V(LWARNING).Infof("[socks5-connect] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil) if err := rep.Write(conn); err != nil { - glog.V(LWARNING).Infoln("socks5 connect:", err) + glog.V(LWARNING).Infof("[socks5-connect] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) } else { - glog.V(LDEBUG).Infoln(rep) + glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) } return } @@ -183,47 +183,50 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { rep := gosocks5.NewReply(gosocks5.Succeeded, nil) if err := rep.Write(conn); err != nil { - glog.V(LWARNING).Infoln("socks5 connect:", err) + glog.V(LWARNING).Infof("[socks5-connect] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) return } - glog.V(LDEBUG).Infoln(rep) + glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) - glog.V(LINFO).Infoln("[socks5] CONNECT", req.Addr, "OK") + glog.V(LINFO).Infof("[socks5-connect] %s <-> %s OK", conn.RemoteAddr(), req.Addr) Transport(conn, tconn) + glog.V(LINFO).Infof("[socks5-connect] %s >-< %s DISCONNECTED", conn.RemoteAddr(), req.Addr) case gosocks5.CmdBind: - glog.V(LINFO).Infoln("[socks5] BIND", req.Addr) + glog.V(LINFO).Infof("[socks5-bind] %s -> %s", conn.RemoteAddr(), req.Addr) if len(forwardArgs) > 0 { forwardBind(req, conn) } else { serveBind(conn) } + glog.V(LINFO).Infof("[socks5-bind] %s >-< %s DISCONNECTED", conn.RemoteAddr(), req.Addr) case gosocks5.CmdUdp, CmdUdpTun: - glog.V(LINFO).Infoln("[socks5] UDP ASSOCIATE", req.Addr) + // TODO: udp tunnel <-> forward chain + glog.V(LINFO).Infof("[socks5-udp] %s -> %s ASSOCIATE", conn.RemoteAddr(), req.Addr) uconn, err := net.ListenUDP("udp", nil) if err != nil { - glog.V(LWARNING).Infoln("socks5 udp listen:", err) + glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) rep := gosocks5.NewReply(gosocks5.Failure, nil) if err := rep.Write(conn); err != nil { - glog.V(LWARNING).Infoln("socks5 udp listen:", err) + glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) } else { - glog.V(LDEBUG).Infoln(rep) + glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) } return } defer uconn.Close() addr := ToSocksAddr(uconn.LocalAddr()) - addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) + addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) // BUG: when server has multi-interfaces, this may cause a mistake rep := gosocks5.NewReply(gosocks5.Succeeded, addr) if err := rep.Write(conn); err != nil { - glog.V(LWARNING).Infoln("socks5 udp:", err) + glog.V(LWARNING).Infof("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) return } else { - glog.V(LDEBUG).Infoln(rep) - glog.V(LINFO).Infoln("[socks5] UDP listen on", addr) + glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) + glog.V(LINFO).Infof("[socks5-udp] %s -> %s LISTEN ON %s", conn.RemoteAddr(), req.Addr, addr) } var cc *UDPConn @@ -231,32 +234,32 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { if req.Cmd == CmdUdpTun { dgram, err = gosocks5.ReadUDPDatagram(conn) if err != nil { - glog.V(LWARNING).Infoln("socks5 udp:", err) + glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) return } + glog.V(LINFO).Infof("[socks5-udp] %s >>> %s, length %d", conn.RemoteAddr(), dgram.Header.Addr, len(dgram.Data)) cc = Client(conn, nil) - glog.V(LINFO).Infof("[udp] -> %s, length %d", dgram.Header.Addr, len(dgram.Data)) } else { b := udpPool.Get().([]byte) defer udpPool.Put(b) n, raddr, err := uconn.ReadFromUDP(b) if err != nil { - glog.V(LWARNING).Infoln("socks5 udp:", err) + glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) return } dgram, err = gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n])) if err != nil { - glog.V(LWARNING).Infoln("socks5 udp:", err) + glog.V(LWARNING).Infof("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) return } + glog.V(LINFO).Infof("[socks5-udp] %s >>> %s, length %d", raddr, dgram.Header.Addr, len(dgram.Data)) cc = Client(uconn, raddr) - glog.V(LINFO).Infof("[udp] %s -> %s, length %d", raddr, dgram.Header.Addr, len(dgram.Data)) } sc, err := createServerConn(uconn) if err != nil { - glog.V(LWARNING).Infoln("socks5 udp:", err) + glog.V(LWARNING).Infof("[socks5-udp] %s", err) return } defer sc.Close() @@ -270,7 +273,7 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { glog.V(LWARNING).Infoln("socks5 udp:", err) return } - glog.V(LINFO).Infof("[udp] <- %s, length %d", dgram.Header.Addr, len(dgram.Data)) + glog.V(LINFO).Infof("[socks5-udp] <<< %s, length %d", dgram.Header.Addr, len(dgram.Data)) if err = cc.WriteUDPTimeout(dgram, time.Second*90); err != nil { glog.V(LWARNING).Infoln("socks5 udp:", err)