diff --git a/README.md b/README.md index 0dd8510..57b4938 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ scheme://[bind_address]:port/[host]:hostport > bind_address:port - 本地监听地址 -> host:hostport - 远程监听地址 +> host:hostport - 远程地址 当在bind_address:port上收到连接信息,则会(通过转发链)与host:hostport建立连接,创建一条数据通道。 diff --git a/conn.go b/conn.go index c3c7d92..7cc1bea 100644 --- a/conn.go +++ b/conn.go @@ -52,10 +52,12 @@ func listenAndServe(arg Args) error { case "tls": // tls connection ln, err = tls.Listen("tcp", arg.Addr, &tls.Config{Certificates: []tls.Certificate{arg.Cert}}) - case "tcp": // TCP port forwarding + case "tcp": // Local TCP port forwarding return listenAndServeTcpForward(arg) - case "udp": // UDP port forwarding + case "udp": // Local UDP port forwarding return listenAndServeUdpForward(arg) + case "rtcp": // Remote TCP port forwarding + return serveRTcpForward(arg) default: ln, err = net.Listen("tcp", arg.Addr) } @@ -76,6 +78,49 @@ func listenAndServe(arg Args) error { } } +func serveRTcpForward(arg Args) error { + if arg.Forward == "" { + ln, err := net.Listen("tcp", arg.Addr) + if err != nil { + return err + } + defer ln.Close() + + for { + conn, err := ln.Accept() + if err != nil { + glog.V(LWARNING).Infoln(err) + continue + } + + tc := conn.(*net.TCPConn) + tc.SetKeepAlive(true) + tc.SetKeepAlivePeriod(time.Second * 60) + + go handleRTcpForwardConn(conn, arg) + } + } else { + retry := 0 + for { + conn, err := Connect(arg.Forward) + if err != nil { + glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", arg.Addr, arg.Forward, err) + time.Sleep((1 << uint(retry)) * time.Second) + if retry < 5 { + retry++ + } + continue + } + retry = 0 + + if err := connectRTcpForward(conn, arg); err != nil { + conn.Close() + time.Sleep(10 * time.Second) + } + } + } +} + func listenAndServeTcpForward(arg Args) error { ln, err := net.Listen("tcp", arg.Addr) if err != nil { @@ -122,10 +167,10 @@ func listenAndServeUdpForward(arg Args) error { func handleConn(conn net.Conn, arg Args) { atomic.AddInt32(&connCounter, 1) - glog.V(LINFO).Infof("%s connected, connections: %d", + glog.V(LDEBUG).Infof("%s connected, connections: %d", conn.RemoteAddr(), atomic.LoadInt32(&connCounter)) - if glog.V(LINFO) { + if glog.V(LDEBUG) { defer func() { glog.Infof("%s disconnected, connections: %d", conn.RemoteAddr(), atomic.LoadInt32(&connCounter)) @@ -153,7 +198,7 @@ func handleConn(conn net.Conn, arg Args) { case "http": req, err := http.ReadRequest(bufio.NewReader(conn)) if err != nil { - glog.V(LWARNING).Infoln("http:", err) + glog.V(LWARNING).Infoln("[http]", err) return } handleHttpRequest(req, conn, arg) @@ -162,7 +207,7 @@ func handleConn(conn net.Conn, arg Args) { conn = gosocks5.ServerConn(conn, selector) req, err := gosocks5.ReadRequest(conn) if err != nil { - glog.V(LWARNING).Infoln("socks5:", err) + glog.V(LWARNING).Infoln("[socks5] request:", err) return } handleSocks5Request(req, conn) @@ -177,7 +222,7 @@ func handleConn(conn net.Conn, arg Args) { n, err := io.ReadAtLeast(conn, b, 2) if err != nil { - glog.V(LWARNING).Infoln("client:", err) + glog.V(LWARNING).Infoln("[client]", err) return } @@ -186,26 +231,26 @@ func handleConn(conn net.Conn, arg Args) { length := 2 + mn if n < length { if _, err := io.ReadFull(conn, b[n:length]); err != nil { - glog.V(LWARNING).Infoln("socks5:", err) + glog.V(LWARNING).Infoln("[socks5]", err) return } } methods := b[2 : 2+mn] method := selector.Select(methods...) if _, err := conn.Write([]byte{gosocks5.Ver5, method}); err != nil { - glog.V(LWARNING).Infoln("socks5 select:", err) + glog.V(LWARNING).Infoln("[socks5] select:", err) return } c, err := selector.OnSelected(method, conn) if err != nil { - glog.V(LWARNING).Infoln("socks5 onselected:", err) + glog.V(LWARNING).Infoln("[socks5] onselected:", err) return } conn = c req, err := gosocks5.ReadRequest(conn) if err != nil { - glog.V(LWARNING).Infoln("socks5 request:", err) + glog.V(LWARNING).Infoln("[socks5] request:", err) return } handleSocks5Request(req, conn) @@ -214,7 +259,7 @@ func handleConn(conn net.Conn, arg Args) { req, err := http.ReadRequest(bufio.NewReader(newReqReader(b[:n], conn))) if err != nil { - glog.V(LWARNING).Infoln("http:", err) + glog.V(LWARNING).Infoln("[http]", err) return } handleHttpRequest(req, conn, arg) @@ -247,7 +292,7 @@ func Connect(addr string) (conn net.Conn, err error) { addr += ":80" } if len(forwardArgs) == 0 { - return net.DialTimeout("tcp", addr, time.Second*60) + return net.DialTimeout("tcp", addr, time.Second*90) } var end Args @@ -306,7 +351,7 @@ func forward(conn net.Conn, arg Args) (net.Conn, error) { if trans == "" { // default is tcp trans = "tcp" } - glog.Infof("forward: %s/%s %s", proto, trans, arg.Addr) + glog.V(LDEBUG).Infof("forward: %s/%s %s", proto, trans, arg.Addr) } var tlsUsed bool diff --git a/forward.go b/forward.go index 8059606..ade9655 100644 --- a/forward.go +++ b/forward.go @@ -1,6 +1,7 @@ package main import ( + "errors" "github.com/ginuerzh/gosocks5" "github.com/golang/glog" "net" @@ -10,6 +11,7 @@ import ( func handleTcpForward(conn net.Conn, arg Args) { defer conn.Close() + if !strings.Contains(arg.Forward, ":") { arg.Forward += ":22" // default is ssh service } @@ -21,9 +23,9 @@ func handleTcpForward(conn net.Conn, arg Args) { } defer c.Close() - glog.V(LINFO).Infof("[tcp-forward] %s <-> %s OK", conn.RemoteAddr(), arg.Forward) + glog.V(LINFO).Infof("[tcp-forward] %s <-> %s", conn.RemoteAddr(), arg.Forward) Transport(conn, c) - glog.V(LINFO).Infof("[tcp-forward] %s >-< %s DISCONNECTED", conn.RemoteAddr(), arg.Forward) + glog.V(LINFO).Infof("[tcp-forward] %s >-< %s", conn.RemoteAddr(), arg.Forward) } func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Args) { @@ -51,7 +53,7 @@ func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Ar 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)) + glog.V(LDEBUG).Infof("[udp-forward] %s >>> %s length %d", raddr, arg.Forward, len(data)) b := udpPool.Get().([]byte) defer udpPool.Put(b) @@ -61,12 +63,12 @@ func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Ar 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) + glog.V(LDEBUG).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) + glog.V(LINFO).Infof("[udp-forward] %s >-< %s", raddr, arg.Forward) return } @@ -99,29 +101,179 @@ func handleUdpForward(conn *net.UDPConn, raddr *net.UDPAddr, data []byte, arg Ar 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) + glog.V(LINFO).Infof("[udp-forward] %s <-> %s ASSOCIATE ON %s", raddr, arg.Forward, rep.Addr) dgram := gosocks5.NewUDPDatagram( gosocks5.NewUDPHeader(uint16(len(data)), 0, ToSocksAddr(faddr)), data) - fconn.SetWriteDeadline(time.Now().Add(time.Second * 60)) + fconn.SetWriteDeadline(time.Now().Add(time.Second * 90)) 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)) + glog.V(LDEBUG).Infof("[udp-forward] %s >>> %s length %d", raddr, arg.Forward, len(data)) - fconn.SetReadDeadline(time.Now().Add(time.Second * 60)) + fconn.SetReadDeadline(time.Now().Add(time.Second * 90)) 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, dgram.Header.Addr, len(dgram.Data)) + glog.V(LDEBUG).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) + glog.V(LINFO).Infof("[udp-forward] %s >-< %s", raddr, arg.Forward) +} + +func handleRTcpForwardConn(conn net.Conn, arg Args) { + defer conn.Close() + + req, err := gosocks5.ReadRequest(conn) + if err != nil { + glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", conn.RemoteAddr(), arg.Addr, err) + return + } + bindAddr, _ := net.ResolveTCPAddr("tcp", req.Addr.String()) + ln, err := net.ListenTCP("tcp", bindAddr) + if err != nil { + glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) + rep := gosocks5.NewReply(gosocks5.Failure, nil) + if err := rep.Write(conn); err != nil { + glog.V(LWARNING).Infof("[rtcp] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) + } + return + } + + addr := ToSocksAddr(ln.Addr()) + addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) + + rep := gosocks5.NewReply(gosocks5.Succeeded, addr) + if err := rep.Write(conn); err != nil { + glog.V(LWARNING).Infof("[rtcp] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) + ln.Close() + return + } + glog.V(LINFO).Infof("[rtcp] %s - %s BIND ON %s OK", conn.RemoteAddr(), req.Addr, addr) + + lnChan := make(chan net.Conn, 1) + go func() { + defer close(lnChan) + c, err := ln.AcceptTCP() + if err != nil { + // glog.V(LWARNING).Infof("[rtcp] %s <- %s ACCEPT : %s", conn.RemoteAddr(), addr, err) + return + } + lnChan <- c + }() + + peerChan := make(chan *gosocks5.Reply, 1) + go func() { + defer close(peerChan) + reply, err := gosocks5.ReadReply(conn) + if err != nil { + return + } + peerChan <- reply + }() + + var pconn net.Conn + + for { + select { + case c := <-lnChan: + ln.Close() // only accept one peer + if c == nil { + if err := gosocks5.NewReply(gosocks5.Failure, nil).Write(conn); err != nil { + glog.V(LWARNING).Infoln("[rtcp] %s <- %s : %s", conn.RemoteAddr(), addr, err) + } + glog.V(LWARNING).Infof("[rtcp] %s >-< %s : %s", conn.RemoteAddr(), addr) + return + } + glog.V(LINFO).Infof("[rtcp] %s <- %s PEER %s ACCEPTED", conn.RemoteAddr(), addr, c.RemoteAddr()) + gosocks5.NewReply(gosocks5.Succeeded, ToSocksAddr(c.RemoteAddr())).Write(conn) + pconn = c + lnChan = nil + ln = nil + case reply := <-peerChan: + if reply == nil { + if ln != nil { + ln.Close() + } + if pconn != nil { + pconn.Close() + } + glog.V(LWARNING).Infof("[rtcp] %s >-< %s", conn.RemoteAddr(), addr) + return + } + goto out + } + } + +out: + defer pconn.Close() + + glog.V(LINFO).Infof("[rtcp] %s <-> %s", conn.RemoteAddr(), pconn.RemoteAddr()) + Transport(conn, pconn) + glog.V(LINFO).Infof("[rtcp] %s >-< %s", conn.RemoteAddr(), pconn.RemoteAddr()) +} + +func connectRTcpForward(conn net.Conn, arg Args) error { + glog.V(LINFO).Infof("[rtcp] %s - %s", arg.Addr, arg.Forward) + + addr, _ := net.ResolveTCPAddr("tcp", arg.Bind) + req := gosocks5.NewRequest(gosocks5.CmdBind, ToSocksAddr(addr)) + if err := req.Write(conn); err != nil { + glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", arg.Addr, arg.Forward, err) + return err + } + + // first reply, bind status + rep, err := gosocks5.ReadReply(conn) + if err != nil { + glog.V(LWARNING).Infof("[rtcp] %s <- %s : %s", arg.Addr, arg.Forward, err) + return err + } + if rep.Rep != gosocks5.Succeeded { + glog.V(LWARNING).Infof("[rtcp] %s <- %s : bind on %s failure", arg.Addr, arg.Forward, arg.Bind) + return errors.New("Bind on " + arg.Bind + " failure") + } + glog.V(LINFO).Infof("[rtcp] %s - %s BIND ON %s OK", arg.Addr, arg.Forward, arg.Bind) + + // second reply, peer connection + rep, err = gosocks5.ReadReply(conn) + if err != nil { + glog.V(LWARNING).Infof("[rtcp] %s <- %s : %s", arg.Addr, arg.Forward, err) + return err + } + if rep.Rep != gosocks5.Succeeded { + glog.V(LWARNING).Infof("[rtcp] %s <- %s : peer connect failure", arg.Addr, arg.Forward) + return errors.New("peer connect failure") + } + + glog.V(LINFO).Infof("[rtcp] %s <- %s PEER %s CONNECTED", conn.RemoteAddr(), req.Addr, rep.Addr) + + go func() { + defer conn.Close() + + lconn, err := net.Dial("tcp", arg.Addr) + if err != nil { + glog.V(LWARNING).Infof("[rtcp] %s <- %s : %s", arg.Addr, arg.Forward, err) + return + } + defer lconn.Close() + + if err := gosocks5.NewReply(gosocks5.Succeeded, nil).Write(conn); err != nil { + glog.V(LWARNING).Infof("[rtcp] %s -> %s : %s", arg.Addr, arg.Forward, err) + return + } + + glog.V(LINFO).Infof("[rtcp] %s <-> %s", arg.Addr, arg.Forward) + Transport(lconn, conn) + glog.V(LINFO).Infof("[rtcp] %s >-< %s", arg.Addr, arg.Forward) + }() + + return nil } diff --git a/http.go b/http.go index 44d2a75..0d45960 100644 --- a/http.go +++ b/http.go @@ -73,9 +73,9 @@ func handleHttpRequest(req *http.Request, conn net.Conn, arg Args) { } } - glog.V(LINFO).Infof("[http] %s <-> %s OK", conn.RemoteAddr(), req.Host) + glog.V(LINFO).Infof("[http] %s <-> %s", conn.RemoteAddr(), req.Host) Transport(conn, c) - glog.V(LINFO).Infof("[http] %s >-< %s DISCONNECTED", conn.RemoteAddr(), req.Host) + glog.V(LINFO).Infof("[http] %s >-< %s", conn.RemoteAddr(), req.Host) } func basicAuth(authInfo string) (username, password string, ok bool) { diff --git a/socks.go b/socks.go index 611e5d2..7c10511 100644 --- a/socks.go +++ b/socks.go @@ -6,7 +6,8 @@ import ( "errors" "github.com/ginuerzh/gosocks5" "github.com/golang/glog" - "io" + //"os/exec" + //"io" "io/ioutil" "net" "net/url" @@ -124,10 +125,10 @@ func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Con req, err := gosocks5.ReadUserPassRequest(conn) if err != nil { - glog.V(LWARNING).Infoln("socks5 auth:", err) + glog.V(LWARNING).Infoln("[socks5-auth]", err) return nil, err } - glog.V(LDEBUG).Infoln(req.String()) + glog.V(LDEBUG).Infoln("[socks5]", req.String()) var username, password string if selector.user != nil { @@ -138,18 +139,18 @@ func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Con if (username != "" && req.Username != username) || (password != "" && req.Password != password) { resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure) if err := resp.Write(conn); err != nil { - glog.V(LWARNING).Infoln("socks5 auth:", err) + glog.V(LWARNING).Infoln("[socks5-auth]", err) return nil, err } - glog.V(LDEBUG).Infoln(resp) - glog.V(LWARNING).Infoln("socks5: proxy authentication required") + glog.V(LDEBUG).Infoln("[socks5]", resp) + glog.V(LWARNING).Infoln("[socks5-auth] proxy authentication required") return nil, gosocks5.ErrAuthFailure } resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded) if err := resp.Write(conn); err != nil { - glog.V(LWARNING).Infoln("socks5 auth:", err) + glog.V(LWARNING).Infoln("[socks5-auth]", err) return nil, err } glog.V(LDEBUG).Infoln(resp) @@ -162,7 +163,7 @@ func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Con } func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { - glog.V(LDEBUG).Infof("[socks5-connect] %s -> %s\n%s", conn.RemoteAddr(), req.Addr, req) + glog.V(LDEBUG).Infof("[socks5] %s -> %s\n%s", conn.RemoteAddr(), req.Addr, req) switch req.Cmd { case gosocks5.CmdConnect: @@ -188,18 +189,21 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { } glog.V(LDEBUG).Infof("[socks5-connect] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) - glog.V(LINFO).Infof("[socks5-connect] %s <-> %s OK", conn.RemoteAddr(), req.Addr) + glog.V(LINFO).Infof("[socks5-connect] %s <-> %s", conn.RemoteAddr(), req.Addr) Transport(conn, tconn) - glog.V(LINFO).Infof("[socks5-connect] %s >-< %s DISCONNECTED", conn.RemoteAddr(), req.Addr) + glog.V(LINFO).Infof("[socks5-connect] %s >-< %s", conn.RemoteAddr(), req.Addr) case gosocks5.CmdBind: glog.V(LINFO).Infof("[socks5-bind] %s - %s", conn.RemoteAddr(), req.Addr) if len(forwardArgs) > 0 { forwardBind(req, conn) } else { - serveBind(conn) + tc := conn.(*net.TCPConn) + tc.SetKeepAlive(true) + tc.SetKeepAlivePeriod(time.Second * 60) + + serveBind(req, 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) @@ -288,119 +292,175 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { TransportUDP(cc, sc) } default: - glog.V(LWARNING).Infoln("Unrecognized request: ", req) + glog.V(LWARNING).Infoln("[socks5] Unrecognized request:", req.Cmd) } } -func serveBind(conn net.Conn) error { - l, err := net.ListenTCP("tcp", nil) +func serveBind(req *gosocks5.Request, conn net.Conn) error { + bindAddr, _ := net.ResolveTCPAddr("tcp", req.Addr.String()) + ln, err := net.ListenTCP("tcp", bindAddr) if err != nil { - glog.V(LWARNING).Infoln("socks5 bind listen:", err) - - rep := gosocks5.NewReply(gosocks5.Failure, nil) - if err := rep.Write(conn); err != nil { - glog.V(LWARNING).Infoln("socks5 bind listen:", err) - } else { - glog.V(LDEBUG).Infoln(rep) + glog.V(LWARNING).Infof("[socks5-bind] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) + if bindAddr != nil { + ln, err = net.ListenTCP("tcp", nil) + } + if err != nil { + glog.V(LWARNING).Infof("[socks5-bind] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) + rep := gosocks5.NewReply(gosocks5.Failure, nil) + if err := rep.Write(conn); err != nil { + glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) + } else { + glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) + } + return err } - return err } - addr := ToSocksAddr(l.Addr()) + addr := ToSocksAddr(ln.Addr()) // Issue: may not reachable when host has two interfaces addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) rep := gosocks5.NewReply(gosocks5.Succeeded, addr) if err := rep.Write(conn); err != nil { - glog.V(LWARNING).Infoln("socks5 bind:", err) - l.Close() + glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) + ln.Close() return err } - glog.V(LDEBUG).Infoln(rep) - glog.V(LINFO).Infoln("[socks5] BIND on", addr, "OK") + glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) + glog.V(LINFO).Infof("[socks5-bind] %s - %s BIND ON %s OK", conn.RemoteAddr(), req.Addr, addr) - l.SetDeadline(time.Now().Add(time.Minute * 30)) // wait 30 minutes at most - tconn, err := l.AcceptTCP() - l.Close() // only accept one peer - if err != nil { - glog.V(LWARNING).Infoln("socks5 bind accept:", err) - - rep = gosocks5.NewReply(gosocks5.Failure, nil) - if err := rep.Write(conn); err != nil { - glog.V(LWARNING).Infoln("socks5 bind accept:", err) - } else { - glog.V(LDEBUG).Infoln(rep) + lnChan := make(chan net.Conn, 1) + go func() { + defer close(lnChan) + c, err := ln.AcceptTCP() + if err != nil { + return + } + lnChan <- c + }() + + peerChan := make(chan error, 1) + go func() { + defer close(peerChan) + _, err := ioutil.ReadAll(conn) + if err != nil { + if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() { + return + } + peerChan <- err + } + }() + + var pconn net.Conn + + for { + select { + case c := <-lnChan: + ln.Close() // only accept one peer + if c == nil { + if err := gosocks5.NewReply(gosocks5.Failure, nil).Write(conn); err != nil { + glog.V(LWARNING).Infoln("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), addr, err) + } + glog.V(LWARNING).Infof("[socks5-bind] %s >-< %s", conn.RemoteAddr(), addr) + return errors.New("accept error") + } + // glog.V(LINFO).Infof("[socks5-bind] %s <- %s PEER %s ACCEPTED", conn.RemoteAddr(), req.Addr, c.RemoteAddr()) + // gosocks5.NewReply(gosocks5.Succeeded, ToSocksAddr(c.RemoteAddr())).Write(conn) + pconn = c + lnChan = nil + ln = nil + conn.SetReadDeadline(time.Now()) // timeout right now ,so we can break out of blocking + case err := <-peerChan: + if err != nil || pconn == nil { + if ln != nil { + ln.Close() + } + if pconn != nil { + pconn.Close() + } + glog.V(LWARNING).Infof("[socks5-bind] %s >-< %s", conn.RemoteAddr(), addr) + return err + } + goto out } - return err } - defer tconn.Close() - addr = ToSocksAddr(tconn.RemoteAddr()) - glog.V(LINFO).Infoln("[socks5] BIND accept", addr) +out: + defer pconn.Close() + conn.SetReadDeadline(time.Time{}) - rep = gosocks5.NewReply(gosocks5.Succeeded, addr) + paddr := ToSocksAddr(pconn.RemoteAddr()) + glog.V(LINFO).Infof("[socks5-bind] %s <- %s PEER %s ACCEPTED", conn.RemoteAddr(), addr, paddr) + + rep = gosocks5.NewReply(gosocks5.Succeeded, paddr) if err := rep.Write(conn); err != nil { - glog.V(LWARNING).Infoln("socks5 bind accept:", err) + glog.V(LWARNING).Infof("[socks5 bind] %s <- %s : %s", conn.RemoteAddr(), addr, err) return err } - glog.V(LDEBUG).Infoln(rep) + glog.V(LDEBUG).Infof("[socks5 bind] %s <- %s\n%s", conn.RemoteAddr(), addr, rep) - return Transport(conn, tconn) + glog.V(LINFO).Infof("[socks5-bind] %s <-> %s", conn.RemoteAddr(), paddr) + defer glog.V(LINFO).Infof("[socks5-bind] %s >-< %s", conn.RemoteAddr(), paddr) + return Transport(conn, pconn) } func forwardBind(req *gosocks5.Request, conn net.Conn) error { fconn, _, err := forwardChain(forwardArgs...) if err != nil { - glog.V(LWARNING).Infoln("[socks5] BIND forward", req.Addr, err) + glog.V(LWARNING).Infof("[socks5-bind] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) if fconn != nil { fconn.Close() } rep := gosocks5.NewReply(gosocks5.Failure, nil) if err := rep.Write(conn); err != nil { - glog.V(LWARNING).Infoln("socks5 bind forward:", err) + glog.V(LWARNING).Infof("[socks5-bind] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err) } else { - glog.V(LDEBUG).Infoln(rep) + glog.V(LDEBUG).Infof("[socks5-bind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, rep) } return err } defer fconn.Close() if err := req.Write(fconn); err != nil { - glog.V(LWARNING).Infoln("[socks5] BIND forward", err) + glog.V(LWARNING).Infoln("[socks5-bind] %s -> %s : %s", conn.RemoteAddr(), req.Addr, err) gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) return err } - glog.V(LDEBUG).Infoln(req) + glog.V(LDEBUG).Infof("[socks5-bind] %s -> %s\n%s", conn.RemoteAddr(), req.Addr, req) - // first reply - rep, err := peekReply(conn, fconn) - if err != nil { - glog.V(LWARNING).Infoln("[socks5] BIND forward", err) - return err - } - glog.V(LINFO).Infoln("[socks5] BIND forward on", rep.Addr, "OK") - - // second reply - rep, err = peekReply(conn, fconn) - if err != nil { - glog.V(LWARNING).Infoln("[socks5] BIND forward accept", err) - return err - } - glog.V(LINFO).Infoln("[socks5] BIND forward accept", rep.Addr) + /* + // first reply + rep, err := peekReply(conn, fconn) + if err != nil { + glog.V(LWARNING).Infoln("[socks5] BIND forward", err) + return err + } + glog.V(LINFO).Infoln("[socks5] BIND forward on", rep.Addr, "OK") + // second reply + rep, err = peekReply(conn, fconn) + if err != nil { + glog.V(LWARNING).Infoln("[socks5] BIND forward accept", err) + return err + } + glog.V(LINFO).Infoln("[socks5] BIND forward accept", rep.Addr) + */ + glog.V(LINFO).Infof("[socks5-bind] %s <-> %s", conn.RemoteAddr(), req.Addr) + defer glog.V(LINFO).Infof("[socks5-bind] %s >-< %s", conn.RemoteAddr(), req.Addr) return Transport(conn, fconn) } -func peekReply(dst io.Writer, src io.Reader) (rep *gosocks5.Reply, err error) { +/* +func peekReply(dst, src net.Conn) (rep *gosocks5.Reply, err error) { rep, err = gosocks5.ReadReply(src) if err != nil { - glog.V(LWARNING).Infoln(err) + glog.V(LWARNING).Infof("[socks5-bind] FORWARD %s <- : %s", dst.RemoteAddr(), err) rep = gosocks5.NewReply(gosocks5.Failure, nil) } if err = rep.Write(dst); err != nil { return } - glog.V(LDEBUG).Infoln(rep) + glog.V(LDEBUG).Infof("[socks5-bind] FORWARD %s <-\n%s", dst.RemoteAddr(), rep) if rep.Rep != gosocks5.Succeeded { err = errors.New("Failure") @@ -408,6 +468,7 @@ func peekReply(dst io.Writer, src io.Reader) (rep *gosocks5.Reply, err error) { return } +*/ func createServerConn(uconn *net.UDPConn) (c *UDPConn, err error) { if len(forwardArgs) == 0 { @@ -448,13 +509,17 @@ func createServerConn(uconn *net.UDPConn) (c *UDPConn, err error) { } func ToSocksAddr(addr net.Addr) *gosocks5.Addr { - host, port, _ := net.SplitHostPort(addr.String()) - p, _ := strconv.Atoi(port) - + host := "" + port := 0 + if addr != nil { + h, p, _ := net.SplitHostPort(addr.String()) + host = h + port, _ = strconv.Atoi(p) + } return &gosocks5.Addr{ Type: gosocks5.AddrIPv4, Host: host, - Port: uint16(p), + Port: uint16(port), } } diff --git a/util.go b/util.go index f2d8279..a24d68a 100644 --- a/util.go +++ b/util.go @@ -22,11 +22,12 @@ func (ss *strSlice) Set(value string) error { // admin:123456@localhost:8080 type Args struct { - Addr string // host:port - Protocol string // protocol: http/socks(5)/ss - Transport string // transport: ws(s)/tls/tcp/udp - Forward string // forward address, used by tcp/udp port forwarding - User *url.Userinfo + Addr string // host:port + Protocol string // protocol: http/socks(5)/ss + Transport string // transport: ws(s)/tls/tcp/udp/rtcp/rudp + Forward string // forward address, used by local tcp/udp port forwarding + Bind string // remote binding port, used by remote tcp/udp port forwarding + User *url.Userinfo // authentication for proxy Cert tls.Certificate // tls certificate } @@ -52,10 +53,9 @@ func parseArgs(ss []string) (args []Args) { } arg := Args{ - Addr: u.Host, - User: u.User, - Cert: tlsCert, - Forward: strings.Trim(u.EscapedPath(), "/"), + Addr: u.Host, + User: u.User, + Cert: tlsCert, } schemes := strings.Split(u.Scheme, "+") @@ -76,8 +76,14 @@ func parseArgs(ss []string) (args []Args) { switch arg.Transport { case "ws", "wss", "tls": - case "tcp", "udp": // started from v2.1, tcp and udp are for port forwarding - arg.Protocol = "" + case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding + arg.Forward = strings.Trim(u.EscapedPath(), "/") + case "rtcp", "rudp": // started from v2.1, rtcp and rudp are for remote port forwarding\ + if a := strings.Split(strings.Trim(u.EscapedPath(), "/"), ":"); len(a) == 3 { + arg.Forward = a[0] + ":" + a[1] + arg.Bind = ":" + a[2] + glog.V(LINFO).Infoln(arg.Forward, arg.Bind) + } default: arg.Transport = "" }