From e0b8e54619d0caa1b1f79753bc80e07845da787e Mon Sep 17 00:00:00 2001 From: "rui.zheng" Date: Thu, 20 Oct 2016 15:34:09 +0800 Subject: [PATCH] experimental quic support --- gost.go | 2 +- node.go | 2 +- quic.go | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ server.go | 4 +++ ss.go | 40 ++++++++++++++++++++++++++++ 5 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 quic.go diff --git a/gost.go b/gost.go index 71215a8..ef98ce6 100644 --- a/gost.go +++ b/gost.go @@ -11,7 +11,7 @@ import ( ) const ( - Version = "2.2-rc2" + Version = "2.3-dev" ) // Log level for glog diff --git a/node.go b/node.go index 79be88c..8a4f043 100644 --- a/node.go +++ b/node.go @@ -57,7 +57,7 @@ func ParseProxyNode(s string) (node ProxyNode, err error) { } switch node.Transport { - case "ws", "wss", "tls", "http2": + case "ws", "wss", "tls", "http2", "ssu", "quic": case "https": node.Protocol = "http" node.Transport = "tls" diff --git a/quic.go b/quic.go new file mode 100644 index 0000000..0d97061 --- /dev/null +++ b/quic.go @@ -0,0 +1,80 @@ +package gost + +import ( + "bufio" + "crypto/tls" + "github.com/golang/glog" + "github.com/lucas-clemente/quic-go/h2quic" + "io" + "net/http" + "net/http/httputil" +) + +type QuicServer struct { + Base *ProxyServer + Handler http.Handler + TLSConfig *tls.Config +} + +func NewQuicServer(base *ProxyServer) *QuicServer { + return &QuicServer{Base: base} +} + +func (s *QuicServer) ListenAndServeTLS(config *tls.Config) error { + server := &h2quic.Server{ + Server: &http.Server{ + Addr: s.Base.Node.Addr, + Handler: s.Handler, + TLSConfig: config, + }, + } + if server.Handler == nil { + server.Handler = http.HandlerFunc(s.HandleRequest) + } + return server.ListenAndServe() +} + +func (s *QuicServer) HandleRequest(w http.ResponseWriter, req *http.Request) { + target := req.Host + glog.V(LINFO).Infof("[quic] %s %s - %s %s", req.Method, req.RemoteAddr, target, req.Proto) + + if glog.V(LDEBUG) { + dump, _ := httputil.DumpRequest(req, false) + glog.Infoln(string(dump)) + } + + c, err := s.Base.Chain.Dial(target) + if err != nil { + glog.V(LWARNING).Infof("[quic] %s -> %s : %s", req.RemoteAddr, target, err) + w.WriteHeader(http.StatusServiceUnavailable) + return + } + defer c.Close() + + glog.V(LINFO).Infof("[quic] %s <-> %s", req.RemoteAddr, target) + + req.Header.Set("Connection", "Keep-Alive") + if err = req.Write(c); err != nil { + glog.V(LWARNING).Infof("[quic] %s -> %s : %s", req.RemoteAddr, target, err) + return + } + + resp, err := http.ReadResponse(bufio.NewReader(c), req) + if err != nil { + glog.V(LWARNING).Infoln(err) + return + } + defer resp.Body.Close() + + for k, v := range resp.Header { + for _, vv := range v { + w.Header().Add(k, vv) + } + } + w.WriteHeader(resp.StatusCode) + if _, err := io.Copy(flushWriter{w}, resp.Body); err != nil { + glog.V(LWARNING).Infof("[quic] %s <- %s : %s", req.RemoteAddr, target, err) + } + + glog.V(LINFO).Infof("[quic] %s >-< %s", req.RemoteAddr, target) +} diff --git a/server.go b/server.go index dd2e727..8ca5597 100644 --- a/server.go +++ b/server.go @@ -80,6 +80,10 @@ func (s *ProxyServer) Serve() error { return NewRTcpForwardServer(s).Serve() case "rudp": // Remote UDP port forwarding return NewRUdpForwardServer(s).Serve() + case "ssu": // shadowsocks udp relay + return NewShadowUdpServer(s).ListenAndServe() + case "quic": + return NewQuicServer(s).ListenAndServeTLS(s.TLSConfig) default: ln, err = net.Listen("tcp", node.Addr) } diff --git a/ss.go b/ss.go index 41afff8..48fa6e4 100644 --- a/ss.go +++ b/ss.go @@ -65,6 +65,46 @@ func (s *ShadowServer) Serve() { glog.V(LINFO).Infof("[ss] %s >-< %s", s.conn.RemoteAddr(), addr) } +type ShadowUdpServer struct { + Base *ProxyServer + Handler func(conn *net.UDPConn, addr *net.UDPAddr, data []byte) +} + +func NewShadowUdpServer(base *ProxyServer) *ShadowUdpServer { + return &ShadowUdpServer{Base: base} +} + +func (s *ShadowUdpServer) ListenAndServe() error { + laddr, err := net.ResolveUDPAddr("udp", s.Base.Node.Addr) + if err != nil { + return err + } + lconn, err := net.ListenUDP("udp", laddr) + if err != nil { + return err + } + defer lconn.Close() + + if s.Handler == nil { + s.Handler = s.HandleConn + } + + for { + b := make([]byte, LargeBufferSize) + n, addr, err := lconn.ReadFromUDP(b) + if err != nil { + glog.V(LWARNING).Infoln(err) + continue + } + + go s.Handler(lconn, addr, b[:n]) + } +} + +func (s *ShadowUdpServer) HandleConn(conn *net.UDPConn, addr *net.UDPAddr, data []byte) { + +} + // This function is copied from shadowsocks library with some modification. func (s *ShadowServer) getRequest() (host string, ota bool, err error) { // buf size should at least have the same size with the largest possible