diff --git a/README.md b/README.md index b9ed9d9..0dd8510 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ gost - GO Simple Tunnel * socks5代理支持tls协商加密 * Tunnel UDP over TCP * 兼容shadowsocks协议 +* 支持端口转发(2.1及以上版本) 二进制文件下载:https://github.com/ginuerzh/gost/releases @@ -20,13 +21,16 @@ Google讨论组: https://groups.google.com/d/forum/go-gost 参数说明 ------ -#### -L和-F参数格式 +#### 代理及转发链 + +适用于-L和-F参数 + ```bash [scheme://][user:pass@host]:port ``` scheme分为两部分: protocol+transport -protocol: 代理协议类型(http, socks5, shadowsocks), transport: 数据传输方式(tcp, ws, wss, tls), 二者可以任意组合,或单独使用。 +protocol: 代理协议类型(http, socks5, shadowsocks), transport: 数据传输方式(ws, wss, tls), 二者可以任意组合,或单独使用: > http - 作为http代理: http://:8080 @@ -40,6 +44,21 @@ protocol: 代理协议类型(http, socks5, shadowsocks), transport: 数据传输 > ss - 作为shadowsocks服务,ss://aes-256-cfb:123456@:8080 +#### 本地端口转发 + +适用于-L参数 + +```bash +scheme://[bind_address]:port/[host]:hostport +``` +> scheme - 端口转发类型: tcp, udp + +> bind_address:port - 本地监听地址 + +> host:hostport - 远程监听地址 + +当在bind_address:port上收到连接信息,则会(通过转发链)与host:hostport建立连接,创建一条数据通道。 + #### 开启日志 > -logtostderr : 输出到控制台 @@ -90,6 +109,22 @@ gost -L=:8080 -F=http+tls://192.168.1.1:443 -F=socks+ws://192.168.1.2:1080 -F=ss ``` gost按照-F设置顺序通过转发链将请求最终转发给a.b.c.d:NNNN处理,每一个转发代理可以是任意http(s)/socks5/shadowsocks类型代理。 +#### 本地端口转发(TCP) + +```bash +gost -L=tcp://:2222/192.168.1.1:22 -F=... +``` +将本地TCP端口2222上的数据(通过转发链)转发到192.168.1.1:22上。 + +#### 本地端口转发(UDP) + +```bash +gost -L=udp://:5353/192.168.1.1:53 -F=... +``` +将本地UDP端口5353上的数据(通过转发链)转发到192.168.1.1:53上。 + +**注: 如果有转发链,则转发链的末端(最后一个-F参数)必须是gost socks5类型代理。** + 加密机制 ------ #### HTTP diff --git a/forward.go b/forward.go index 1979fa7..8059606 100644 --- a/forward.go +++ b/forward.go @@ -5,11 +5,15 @@ import ( "github.com/golang/glog" "net" "strings" + "time" ) func handleTcpForward(conn net.Conn, arg Args) { defer conn.Close() - glog.V(LINFO).Infof("[tcp-forward] %s -> %s", conn.RemoteAddr(), arg.Forward) + if !strings.Contains(arg.Forward, ":") { + arg.Forward += ":22" // default is ssh service + } + glog.V(LINFO).Infof("[tcp-forward] %s - %s", conn.RemoteAddr(), arg.Forward) c, err := Connect(arg.Forward) if err != nil { glog.V(LWARNING).Infof("[tcp-forward] %s -> %s : %s", conn.RemoteAddr(), arg.Forward, err) @@ -24,9 +28,48 @@ func handleTcpForward(conn net.Conn, arg Args) { func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Args) { if !strings.Contains(arg.Forward, ":") { - arg.Forward += ":53" + arg.Forward += ":53" // default is dns service } - glog.V(LINFO).Infof("[udp-forward] %s -> %s", raddr, arg.Forward) + + faddr, err := net.ResolveUDPAddr("udp", arg.Forward) + 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", raddr, faddr) + + if len(forwardArgs) == 0 { + lconn, err := net.ListenUDP("udp", nil) + if err != nil { + glog.V(LWARNING).Infof("[udp-forward] %s -> %s : %s", raddr, arg.Forward, err) + return + } + defer lconn.Close() + + if _, err := lconn.WriteToUDP(data, faddr); 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)) + + 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", raddr, arg.Forward, err) + return + } + glog.V(LINFO).Infof("[udp-forward] %s <<< %s length %d", raddr, addr, n) + + if _, err := conn.WriteToUDP(b[:n], raddr); err != nil { + glog.V(LWARNING).Infof("[udp-forward] %s <- %s : %s", raddr, arg.Forward, err) + } + glog.V(LINFO).Infof("[udp-forward] %s >-< %s DONE", raddr, arg.Forward) + return + } + fconn, _, err := forwardChain(forwardArgs...) if err != nil { glog.V(LWARNING).Infof("[udp-forward] %s -> %s : %s", raddr, arg.Forward, err) @@ -58,27 +101,27 @@ func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Ar } 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) + gosocks5.NewUDPHeader(uint16(len(data)), 0, ToSocksAddr(faddr)), data) + + fconn.SetWriteDeadline(time.Now().Add(time.Second * 60)) 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)) + fconn.SetReadDeadline(time.Now().Add(time.Second * 60)) 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)) + glog.V(LINFO).Infof("[udp-forward] %s <<< %s length %d", raddr, dgram.Header.Addr, 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) } + + glog.V(LINFO).Infof("[udp-forward] %s >-< %s DONE", raddr, arg.Forward) } diff --git a/http.go b/http.go index 8feb5d1..44d2a75 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).Infof("[http] %s -> %s", conn.RemoteAddr(), req.Host) + glog.V(LINFO).Infof("[http] %s - %s", conn.RemoteAddr(), req.Host) var username, password string if arg.User != nil { diff --git a/socks.go b/socks.go index 8b7cc11..611e5d2 100644 --- a/socks.go +++ b/socks.go @@ -166,7 +166,7 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { switch req.Cmd { case gosocks5.CmdConnect: - glog.V(LINFO).Infof("[socks5-connect] %s -> %s", conn.RemoteAddr(), req.Addr) + glog.V(LINFO).Infof("[socks5-connect] %s - %s", conn.RemoteAddr(), req.Addr) tconn, err := Connect(req.Addr.String()) if err != nil { @@ -192,7 +192,7 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { Transport(conn, tconn) glog.V(LINFO).Infof("[socks5-connect] %s >-< %s DISCONNECTED", conn.RemoteAddr(), req.Addr) case gosocks5.CmdBind: - glog.V(LINFO).Infof("[socks5-bind] %s -> %s", conn.RemoteAddr(), req.Addr) + glog.V(LINFO).Infof("[socks5-bind] %s - %s", conn.RemoteAddr(), req.Addr) if len(forwardArgs) > 0 { forwardBind(req, conn) @@ -202,7 +202,7 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { glog.V(LINFO).Infof("[socks5-bind] %s >-< %s DISCONNECTED", conn.RemoteAddr(), req.Addr) case gosocks5.CmdUdp, CmdUdpTun: // TODO: udp tunnel <-> forward chain - glog.V(LINFO).Infof("[socks5-udp] %s -> %s ASSOCIATE", conn.RemoteAddr(), req.Addr) + 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).Infof("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err)