diff --git a/README.md b/README.md index 71b0792..b9ed9d9 100644 --- a/README.md +++ b/README.md @@ -121,7 +121,7 @@ gost -L=:8080 -F=socks://server_ip:1080 注:如果transport已经使用了加密模式(wss, tls),则socks5不会再使用加密方法,防止不必要的双重加密。 #### shadowsocks -gost对shadowsocks的支持是基于[shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go)库。 +gost对shadowsocks加密方法的支持是基于[shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go)库。 服务端: ```bash @@ -133,7 +133,8 @@ gost -L=:8080 -F=ss://aes-128-cfb:123456@server_ip:8338 ``` #### TLS -gost内置了tls证书,如果需要使用其他tls证书,在gost目录放置key.pem(公钥)和cert.pem(私钥)两个文件即可。 +gost内置了tls证书,如果需要使用其他tls证书,在gost目录放置cert.pem(公钥)和key.pem(私钥)两个文件即可。 + SOCKS5 UDP数据处理 ------ diff --git a/conn.go b/conn.go index d20131b..94908f3 100644 --- a/conn.go +++ b/conn.go @@ -16,6 +16,7 @@ import ( "net/url" "strconv" "strings" + "sync" "sync/atomic" "time" ) @@ -24,6 +25,21 @@ var ( connCounter int32 ) +var ( + // tcp buffer pool + tcpPool = sync.Pool{ + New: func() interface{} { + return make([]byte, 16*1024) + }, + } + // udp buffer pool + udpPool = sync.Pool{ + New: func() interface{} { + return make([]byte, 64*1024+262) + }, + } +) + func listenAndServe(arg Args) error { var ln net.Listener var err error @@ -120,7 +136,9 @@ func handleConn(conn net.Conn, arg Args) { // http + socks5 - b := make([]byte, 16*1024) + //b := make([]byte, 16*1024) + b := tcpPool.Get().([]byte) + defer tcpPool.Put(b) n, err := io.ReadAtLeast(conn, b, 2) if err != nil { @@ -214,7 +232,7 @@ func Connect(addr string) (conn net.Conn, err error) { func forwardChain(chain ...Args) (conn net.Conn, end Args, err error) { end = chain[0] - if conn, err = net.DialTimeout("tcp", end.Addr, time.Second*30); err != nil { + if conn, err = net.DialTimeout("tcp", end.Addr, time.Second*90); err != nil { return } c, err := forward(conn, end) diff --git a/main.go b/main.go index 50919ad..9a5aaff 100644 --- a/main.go +++ b/main.go @@ -18,7 +18,7 @@ const ( ) const ( - Version = "2.0-rc2" + Version = "2.0-rc3" ) var ( diff --git a/socks.go b/socks.go index e29ff85..ad04e54 100644 --- a/socks.go +++ b/socks.go @@ -7,6 +7,7 @@ import ( "github.com/ginuerzh/gosocks5" "github.com/golang/glog" "io" + "io/ioutil" "net" "net/url" "strconv" @@ -18,6 +19,10 @@ const ( MethodTLSAuth uint8 = 0x82 // extended method for tls+auth ) +const ( + CmdUdpTun uint8 = 0xf3 // extended method for udp over tcp +) + type clientSelector struct { methods []uint8 user *url.Userinfo @@ -193,7 +198,7 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { } else { serveBind(conn) } - case gosocks5.CmdUdp: + case gosocks5.CmdUdp, CmdUdpTun: glog.V(LINFO).Infoln("[socks5] UDP ASSOCIATE", req.Addr) uconn, err := net.ListenUDP("udp", nil) if err != nil { @@ -211,7 +216,6 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { addr := ToSocksAddr(uconn.LocalAddr()) addr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) - glog.V(LINFO).Infoln("[socks5] UDP listen on", addr) rep := gosocks5.NewReply(gosocks5.Succeeded, addr) if err := rep.Write(conn); err != nil { @@ -219,43 +223,67 @@ func handleSocks5Request(req *gosocks5.Request, conn net.Conn) { return } else { glog.V(LDEBUG).Infoln(rep) + glog.V(LINFO).Infoln("[socks5] UDP listen on", addr) } - cc, dgram, err := createClientConn(conn, uconn) + var cc *UDPConn + var dgram *gosocks5.UDPDatagram + if req.Cmd == CmdUdpTun { + dgram, err = gosocks5.ReadUDPDatagram(conn) + if err != nil { + glog.V(LWARNING).Infoln("socks5 udp:", err) + return + } + 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) + return + } + dgram, err = gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n])) + if err != nil { + glog.V(LWARNING).Infoln("socks5 udp:", err) + return + } + 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) return } - glog.V(LINFO).Infof("[udp] to %s, length %d", dgram.Header.Addr, len(dgram.Data)) + defer sc.Close() - raddr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String()) + if err = sc.WriteUDPTimeout(dgram, time.Second*90); err != nil { + glog.V(LWARNING).Infoln("socks5 udp:", err) + return + } + dgram, err = sc.ReadUDPTimeout(time.Second * 90) if err != nil { glog.V(LWARNING).Infoln("socks5 udp:", err) return } - sc, err := createServerConn(uconn, raddr) - if err != nil { + glog.V(LINFO).Infof("[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) return } - if err = sc.WriteUDPTimeout(dgram, time.Second*30); err != nil { - glog.V(LWARNING).Infoln("socks5 udp:", err) - return + if req.Cmd == gosocks5.CmdUdp { + go TransportUDP(cc, sc) + ioutil.ReadAll(conn) // wait for client exit + glog.V(LINFO).Infoln("[udp] transfer done") + } else { + TransportUDP(cc, sc) } - dgram, err = sc.ReadUDPTimeout(time.Second * 30) - if err != nil { - glog.V(LWARNING).Infoln("socks5 udp:", err) - return - } - glog.V(LINFO).Infof("[udp] from %s, length %d", dgram.Header.Addr, len(dgram.Data)) - - if err = cc.WriteUDPTimeout(dgram, time.Second*30); err != nil { - glog.V(LWARNING).Infoln("socks5 udp:", err) - return - } - - TransportUDP(cc, sc) default: glog.V(LWARNING).Infoln("Unrecognized request: ", req) } @@ -288,6 +316,7 @@ func serveBind(conn net.Conn) error { glog.V(LDEBUG).Infoln(rep) glog.V(LINFO).Infoln("[socks5] BIND on", addr, "OK") + 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 { @@ -319,7 +348,7 @@ func serveBind(conn net.Conn) error { 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).Infoln("[socks5] BIND forward", req.Addr, err) if fconn != nil { fconn.Close() } @@ -334,7 +363,7 @@ func forwardBind(req *gosocks5.Request, conn net.Conn) error { defer fconn.Close() if err := req.Write(fconn); err != nil { - glog.V(LWARNING).Infoln("[socks5] BIND(forward)", err) + glog.V(LWARNING).Infoln("[socks5] BIND forward", err) gosocks5.NewReply(gosocks5.Failure, nil).Write(conn) return err } @@ -343,18 +372,18 @@ func forwardBind(req *gosocks5.Request, conn net.Conn) error { // first reply rep, err := peekReply(conn, fconn) if err != nil { - glog.V(LWARNING).Infoln("[socks5] BIND(forward)", err) + glog.V(LWARNING).Infoln("[socks5] BIND forward", err) return err } - glog.V(LINFO).Infoln("[socks5] BIND(forward) on", rep.Addr, "OK") + 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) + glog.V(LWARNING).Infoln("[socks5] BIND forward accept", err) return err } - glog.V(LINFO).Infoln("[socks5] BIND(forward) accept", rep.Addr) + glog.V(LINFO).Infoln("[socks5] BIND forward accept", rep.Addr) return Transport(conn, fconn) } @@ -377,53 +406,7 @@ func peekReply(dst io.Writer, src io.Reader) (rep *gosocks5.Reply, err error) { return } -func createClientConn(conn net.Conn, uconn *net.UDPConn) (c *UDPConn, dgram *gosocks5.UDPDatagram, err error) { - var raddr *net.UDPAddr - dgramChan := make(chan *gosocks5.UDPDatagram, 1) - errChan := make(chan error, 1) - go func() { - b := make([]byte, 64*1024+262) - - n, addr, err := uconn.ReadFromUDP(b) - if err != nil { - errChan <- err - return - } - raddr = addr - - dgram, err := gosocks5.ReadUDPDatagram(bytes.NewReader(b[:n])) - if err != nil { - errChan <- err - return - } - dgramChan <- dgram - }() - - go func() { - dgram, err := gosocks5.ReadUDPDatagram(conn) - if err != nil { - errChan <- err - return - } - dgramChan <- dgram - }() - - select { - case dgram = <-dgramChan: - if raddr != nil { - glog.V(LINFO).Infoln("[udp] client", raddr) - c = Client(uconn, raddr) - } else { - glog.V(LINFO).Infoln("[udp] tunnel") - c = Client(conn, nil) - } - case err = <-errChan: - } - - return -} - -func createServerConn(uconn *net.UDPConn, addr net.Addr) (c *UDPConn, err error) { +func createServerConn(uconn *net.UDPConn) (c *UDPConn, err error) { if len(forwardArgs) == 0 { c = Server(uconn) return @@ -436,9 +419,9 @@ func createServerConn(uconn *net.UDPConn, addr net.Addr) (c *UDPConn, err error) } return } - glog.V(LINFO).Infoln("forward udp associate") + glog.V(LINFO).Infoln("[udp] forward associate") - req := gosocks5.NewRequest(gosocks5.CmdUdp, nil) + req := gosocks5.NewRequest(CmdUdpTun, nil) if err = req.Write(fconn); err != nil { fconn.Close() return @@ -455,7 +438,7 @@ func createServerConn(uconn *net.UDPConn, addr net.Addr) (c *UDPConn, err error) fconn.Close() return nil, errors.New("Failure") } - glog.V(LINFO).Infoln("forward udp associate, on", rep.Addr, "OK") + glog.V(LINFO).Infoln("[udp] forward associate on", rep.Addr, "OK") c = Server(fconn) return @@ -481,8 +464,11 @@ func PipeUDP(src, dst *UDPConn, ch chan<- error) { if err != nil { break } - // glog.V(LDEBUG).Infof("[udp] addr %s, length %d", dgram.Header.Addr, len(dgram.Data)) - + if src.isClient { + glog.V(LDEBUG).Infof("[udp] -> %s, length %d", dgram.Header.Addr, len(dgram.Data)) + } else { + glog.V(LDEBUG).Infof("[udp] <- %s, length %d", dgram.Header.Addr, len(dgram.Data)) + } if err = dst.WriteUDP(dgram); err != nil { break } @@ -501,9 +487,9 @@ func TransportUDP(cc, sc *UDPConn) (err error) { select { case err = <-wChan: - //log.Println("w exit", err) + // glog.V(LDEBUG).Infoln("w exit", err) case err = <-rChan: - //log.Println("r exit", err) + // glog.V(LDEBUG).Infoln("r exit", err) } return diff --git a/udp.go b/udp.go index e3ce064..7ddeac1 100644 --- a/udp.go +++ b/udp.go @@ -72,7 +72,10 @@ func (c *UDPConn) readUDPClient() (*gosocks5.UDPDatagram, error) { func (c *UDPConn) readUDPServer() (*gosocks5.UDPDatagram, error) { if c.udp != nil { - b := make([]byte, 65535) + // b := make([]byte, 65535) + b := udpPool.Get().([]byte) + defer udpPool.Put(b) + n, addr, err := c.udp.ReadFrom(b) if err != nil { return nil, err diff --git a/ws.go b/ws.go index dafb42c..c5fc21d 100644 --- a/ws.go +++ b/ws.go @@ -129,15 +129,18 @@ func (s *ws) handle(w http.ResponseWriter, r *http.Request) { } func (s *ws) ListenAndServe() error { - http.HandleFunc("/ws", s.handle) - return http.ListenAndServe(s.arg.Addr, nil) + sm := http.NewServeMux() + sm.HandleFunc("/ws", s.handle) + return http.ListenAndServe(s.arg.Addr, sm) } func (s *ws) listenAndServeTLS() error { - http.HandleFunc("/ws", s.handle) + sm := http.NewServeMux() + sm.HandleFunc("/ws", s.handle) server := &http.Server{ Addr: s.arg.Addr, TLSConfig: &tls.Config{Certificates: []tls.Certificate{s.arg.Cert}}, + Handler: sm, } return server.ListenAndServeTLS("", "") }