add udp port forwarding

This commit is contained in:
rui.zheng 2016-08-30 18:05:25 +08:00
parent 1814806ecf
commit 3c79cf44b7
4 changed files with 123 additions and 49 deletions

31
conn.go
View File

@ -55,8 +55,7 @@ func listenAndServe(arg Args) error {
case "tcp": // TCP port forwarding case "tcp": // TCP port forwarding
return listenAndServeTcpForward(arg) return listenAndServeTcpForward(arg)
case "udp": // UDP port forwarding case "udp": // UDP port forwarding
//return listenAndServeUdpForward(arg) return listenAndServeUdpForward(arg)
return nil
default: default:
ln, err = net.Listen("tcp", arg.Addr) ln, err = net.Listen("tcp", arg.Addr)
} }
@ -82,6 +81,8 @@ func listenAndServeTcpForward(arg Args) error {
if err != nil { if err != nil {
return err return err
} }
defer ln.Close()
for { for {
conn, err := ln.Accept() conn, err := ln.Accept()
if err != nil { if err != nil {
@ -93,29 +94,32 @@ func listenAndServeTcpForward(arg Args) error {
return nil return nil
} }
/*
func listenAndServeUdpForward(arg Args) error { func listenAndServeUdpForward(arg Args) error {
addr, err := net.ResolveUDPAddr("udp", arg.Addr) laddr, err := net.ResolveUDPAddr("udp", arg.Addr)
if err != nil { if err != nil {
return err return err
} }
ln, err := net.ListenUDP("udp", addr) ln, err := net.ListenUDP("udp", laddr)
if err != nil { if err != nil {
return err return err
} }
defer ln.Close()
for { for {
b := udpPool.Get().([]byte) b := udpPool.Get().([]byte)
defer udpPool.Put(b)
_, c, err := ln.ReadFromUDP(b) n, raddr, err := ln.ReadFromUDP(b)
if err != nil { if err != nil {
glog.V(LWARNING).Infoln(err) glog.V(LWARNING).Infoln(err)
continue 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) { func handleConn(conn net.Conn, arg Args) {
atomic.AddInt32(&connCounter, 1) atomic.AddInt32(&connCounter, 1)
glog.V(LINFO).Infof("%s connected, connections: %d", glog.V(LINFO).Infof("%s connected, connections: %d",
@ -261,6 +265,7 @@ func Connect(addr string) (conn net.Conn, err error) {
return conn, nil return conn, nil
} }
// establish connection throughout the forward chain
func forwardChain(chain ...Args) (conn net.Conn, end Args, err error) { func forwardChain(chain ...Args) (conn net.Conn, end Args, err error) {
end = chain[0] end = chain[0]
if conn, err = net.DialTimeout("tcp", end.Addr, time.Second*90); err != nil { 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 var err error
if glog.V(LINFO) { if glog.V(LINFO) {
proto := arg.Protocol proto := arg.Protocol
if proto == "default" { trans := arg.Transport
if proto == "" {
proto = "http" // default is http 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 var tlsUsed bool

View File

@ -1,23 +1,84 @@
package main package main
import ( import (
"github.com/ginuerzh/gosocks5"
"github.com/golang/glog" "github.com/golang/glog"
"net" "net"
"strings"
) )
func handleTcpForward(conn net.Conn, arg Args) { 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) c, err := Connect(arg.Forward)
if err != nil { 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 return
} }
defer c.Close() 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) 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)
}
} }

21
http.go
View File

@ -18,7 +18,7 @@ func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) {
glog.Infoln(string(dump)) 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 var username, password string
if arg.User != nil { 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" "Proxy-Agent: gost/" + Version + "\r\n\r\n"
if _, err := conn.Write([]byte(resp)); err != nil { 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 return
} }
c, err := Connect(req.Host) c, err := Connect(req.Host)
if err != nil { 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" + b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
"Proxy-Agent: gost/" + Version + "\r\n\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) conn.Write(b)
return return
} }
@ -57,10 +57,10 @@ func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) {
if req.Method == "CONNECT" { if req.Method == "CONNECT" {
b := []byte("HTTP/1.1 200 Connection established\r\n" + b := []byte("HTTP/1.1 200 Connection established\r\n" +
"Proxy-Agent: gost/" + Version + "\r\n\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 { 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 return
} }
} else { } else {
@ -68,13 +68,14 @@ func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) {
req.Header.Set("Connection", "Keep-Alive") req.Header.Set("Connection", "Keep-Alive")
if err = req.Write(c); err != nil { 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 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) Transport(conn, c)
glog.V(LINFO).Infof("[http] %s >-< %s DISCONNECTED", conn.RemoteAddr(), req.Host)
} }
func basicAuth(authInfo string) (username, password string, ok bool) { func basicAuth(authInfo string) (username, password string, ok bool) {

View File

@ -162,20 +162,20 @@ func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Con
} }
func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { 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 { switch req.Cmd {
case gosocks5.CmdConnect: 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()) tconn, err := Connect(req.Addr.String())
if err != nil { 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) rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil)
if err := rep.Write(conn); err != 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 { } else {
glog.V(LDEBUG).Infoln(rep) glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
} }
return return
} }
@ -183,47 +183,50 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) {
rep := gosocks5.NewReply(gosocks5.Succeeded, nil) rep := gosocks5.NewReply(gosocks5.Succeeded, nil)
if err := rep.Write(conn); err != 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 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) Transport(conn, tconn)
glog.V(LINFO).Infof("[socks5-connect] %s >-< %s DISCONNECTED", conn.RemoteAddr(), req.Addr)
case gosocks5.CmdBind: 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 { if len(forwardArgs) > 0 {
forwardBind(req, conn) forwardBind(req, conn)
} else { } else {
serveBind(conn) serveBind(conn)
} }
glog.V(LINFO).Infof("[socks5-bind] %s >-< %s DISCONNECTED", conn.RemoteAddr(), req.Addr)
case gosocks5.CmdUdp, CmdUdpTun: 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) uconn, err := net.ListenUDP("udp", nil)
if err != 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) rep := gosocks5.NewReply(gosocks5.Failure, nil)
if err := rep.Write(conn); err != 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 { } else {
glog.V(LDEBUG).Infoln(rep) glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
} }
return return
} }
defer uconn.Close() defer uconn.Close()
addr := ToSocksAddr(uconn.LocalAddr()) 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) rep := gosocks5.NewReply(gosocks5.Succeeded, addr)
if err := rep.Write(conn); err != nil { 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 return
} else { } else {
glog.V(LDEBUG).Infoln(rep) glog.V(LDEBUG).Infof("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep)
glog.V(LINFO).Infoln("[socks5] UDP listen on", addr) glog.V(LINFO).Infof("[socks5-udp] %s -> %s LISTEN ON %s", conn.RemoteAddr(), req.Addr, addr)
} }
var cc *UDPConn var cc *UDPConn
@ -231,32 +234,32 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) {
if req.Cmd == CmdUdpTun { if req.Cmd == CmdUdpTun {
dgram, err = gosocks5.ReadUDPDatagram(conn) dgram, err = gosocks5.ReadUDPDatagram(conn)
if err != nil { 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 return
} }
glog.V(LINFO).Infof("[socks5-udp] %s >>> %s, length %d", conn.RemoteAddr(), dgram.Header.Addr, len(dgram.Data))
cc = Client(conn, nil) cc = Client(conn, nil)
glog.V(LINFO).Infof("[udp] -> %s, length %d", dgram.Header.Addr, len(dgram.Data))
} else { } else {
b := udpPool.Get().([]byte) b := udpPool.Get().([]byte)
defer udpPool.Put(b) defer udpPool.Put(b)
n, raddr, err := uconn.ReadFromUDP(b) n, raddr, err := uconn.ReadFromUDP(b)
if err != nil { 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 return
} }
dgram, err = gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n])) dgram, err = gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n]))
if err != nil { 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 return
} }
glog.V(LINFO).Infof("[socks5-udp] %s >>> %s, length %d", raddr, dgram.Header.Addr, len(dgram.Data))
cc = Client(uconn, raddr) cc = Client(uconn, raddr)
glog.V(LINFO).Infof("[udp] %s -> %s, length %d", raddr, dgram.Header.Addr, len(dgram.Data))
} }
sc, err := createServerConn(uconn) sc, err := createServerConn(uconn)
if err != nil { if err != nil {
glog.V(LWARNING).Infoln("socks5 udp:", err) glog.V(LWARNING).Infof("[socks5-udp] %s", err)
return return
} }
defer sc.Close() defer sc.Close()
@ -270,7 +273,7 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) {
glog.V(LWARNING).Infoln("socks5 udp:", err) glog.V(LWARNING).Infoln("socks5 udp:", err)
return 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 { if err = cc.WriteUDPTimeout(dgram, time.Second*90); err != nil {
glog.V(LWARNING).Infoln("socks5 udp:", err) glog.V(LWARNING).Infoln("socks5 udp:", err)