diff --git a/README.md b/README.md index aa087da..8676d95 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ gost -L=:8080 -F=socks://server_ip:1080 如果两端都是gost(如上)则数据传输会被加密(使用tls或tls-auth方法),否则使用标准socks5进行通讯(no-auth或user/pass方法)。 #### shadowsocks -gost对shadowsocks的支持是基于[shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go)库。 +gost对shadowsocks加密方法的支持是基于[shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go)库。 服务端: ```bash @@ -130,7 +130,7 @@ gost -L=ss://aes-128-cfb:123456@:8338 gost -L=:8080 -F=ss://aes-128-cfb:123456@server_ip:8338 ``` -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..e7e22a6 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 { diff --git a/socks.go b/socks.go index e29ff85..1b6ae77 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,42 +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) - 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)) + 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] tunnel to %s, length %d", dgram.Header.Addr, len(dgram.Data)) + } else { + b := udpPool.Get().([]byte) + defer udpPool.Put(b) - raddr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String()) - if err != nil { - glog.V(LWARNING).Infoln("socks5 udp:", err) - return + 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, raddr) + + sc, err := createServerConn(uconn) if err != nil { glog.V(LWARNING).Infoln("socks5 udp:", err) return } - if err = sc.WriteUDPTimeout(dgram, time.Second*30); err != nil { + if err = sc.WriteUDPTimeout(dgram, time.Second*60); err != nil { glog.V(LWARNING).Infoln("socks5 udp:", err) return } - dgram, err = sc.ReadUDPTimeout(time.Second * 30) + dgram, err = sc.ReadUDPTimeout(time.Second * 60) 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 { + if err = cc.WriteUDPTimeout(dgram, time.Second*60); err != nil { glog.V(LWARNING).Infoln("socks5 udp:", err) return } + if req.Cmd == gosocks5.CmdUdp { + go func() { + ioutil.ReadAll(conn) + cc.Close() + sc.Close() + glog.V(LINFO).Infoln("[udp] transfer done") + }() + } TransportUDP(cc, sc) default: glog.V(LWARNING).Infoln("Unrecognized request: ", req) @@ -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 } @@ -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 @@ -477,13 +460,13 @@ func PipeUDP(src, dst *UDPConn, ch chan<- error) { for { var dgram *gosocks5.UDPDatagram - dgram, err = src.ReadUDP() + dgram, err = src.ReadUDPTimeout(time.Second * 60) if err != nil { break } - // glog.V(LDEBUG).Infof("[udp] addr %s, length %d", dgram.Header.Addr, len(dgram.Data)) + glog.V(LDEBUG).Infof("[udp] addr %s, length %d", dgram.Header.Addr, len(dgram.Data)) - if err = dst.WriteUDP(dgram); err != nil { + if err = dst.WriteUDPTimeout(dgram, time.Second*60); err != nil { break } } @@ -501,9 +484,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