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
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

View File

@ -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)
}
}

21
http.go
View File

@ -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) {

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) {
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)