From 029611c078d22295f994c9a13c76bef8f4c042b9 Mon Sep 17 00:00:00 2001 From: wweir Date: Mon, 11 Mar 2019 00:04:24 +0800 Subject: [PATCH 1/2] Refactor proxy --- conf/conf_other.go | 5 +- main.go | 12 +- proxy/client.go | 29 +--- proxy/http_proxy.go | 22 +-- proxy/kcp/client.go | 65 --------- proxy/kcp/server.go | 56 -------- proxy/nettype_string.go | 16 --- {parse => proxy/parser}/parse_addr.go | 2 +- {parse => proxy/parser}/sni.go | 2 +- proxy/quic/client.go | 56 -------- proxy/quic/server.go | 55 ------- proxy/quic/util.go | 48 ------- proxy/server.go | 32 +---- {shadow => proxy/shadow}/cipher.go | 0 {shadow => proxy/shadow}/ciphertype_string.go | 0 {shadow => proxy/shadow}/doc.go | 0 {shadow => proxy/shadow}/shadow.go | 0 proxy/tcp/client.go | 26 ---- proxy/tcp/server.go | 35 ----- proxy/transport/kcp.go | 115 +++++++++++++++ proxy/transport/quic.go | 136 ++++++++++++++++++ proxy/transport/tcp.go | 49 +++++++ proxy/transport/util.go | 30 ++++ proxy/util.go | 9 -- 24 files changed, 368 insertions(+), 432 deletions(-) delete mode 100644 proxy/kcp/client.go delete mode 100644 proxy/kcp/server.go delete mode 100644 proxy/nettype_string.go rename {parse => proxy/parser}/parse_addr.go (98%) rename {parse => proxy/parser}/sni.go (99%) delete mode 100644 proxy/quic/client.go delete mode 100644 proxy/quic/server.go delete mode 100644 proxy/quic/util.go rename {shadow => proxy/shadow}/cipher.go (100%) rename {shadow => proxy/shadow}/ciphertype_string.go (100%) rename {shadow => proxy/shadow}/doc.go (100%) rename {shadow => proxy/shadow}/shadow.go (100%) delete mode 100644 proxy/tcp/client.go delete mode 100644 proxy/tcp/server.go create mode 100644 proxy/transport/kcp.go create mode 100644 proxy/transport/quic.go create mode 100644 proxy/transport/tcp.go create mode 100644 proxy/transport/util.go diff --git a/conf/conf_other.go b/conf/conf_other.go index 2a18e7b..1e15b41 100644 --- a/conf/conf_other.go +++ b/conf/conf_other.go @@ -6,11 +6,14 @@ import ( "flag" "os" "path/filepath" + "strings" + + "github.com/wweir/sower/proxy/transport" ) func initArgs() { flag.StringVar(&Conf.ConfigFile, "f", filepath.Dir(os.Args[0])+"/sower.toml", "config file location") - flag.StringVar(&Conf.NetType, "n", "TCP", "proxy net type (QUIC|KCP|TCP)") + flag.StringVar(&Conf.NetType, "n", "TCP", "proxy net type ("+strings.Join(transport.ListTransports(), "|")+")") flag.StringVar(&Conf.Cipher, "C", "AES_128_GCM", "cipher type (AES_128_GCM|AES_192_GCM|AES_256_GCM|CHACHA20_IETF_POLY1305|XCHACHA20_IETF_POLY1305)") flag.StringVar(&Conf.Password, "p", "12345678", "password") flag.StringVar(&Conf.ServerPort, "P", "5533", "server mode listen port") diff --git a/main.go b/main.go index ad04e67..f6e54d6 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "github.com/wweir/sower/conf" "github.com/wweir/sower/dns" "github.com/wweir/sower/proxy" + "github.com/wweir/sower/proxy/transport" ) var version, date string @@ -13,16 +14,21 @@ func main() { conf := conf.Conf glog.Infof("Starting sower(%s %s): %v", version, date, conf) + tran, err := transport.GetTransport(conf.NetType) + if err != nil { + glog.Exitln(err) + } + if conf.ServerAddr == "" { - proxy.StartServer(conf.NetType, conf.ServerPort, conf.Cipher, conf.Password) + proxy.StartServer(tran, conf.ServerPort, conf.Cipher, conf.Password) } else { if conf.HTTPProxy != "" { - go proxy.StartHttpProxy(conf.NetType, conf.ServerAddr, + go proxy.StartHttpProxy(tran, conf.ServerAddr, conf.Cipher, conf.Password, conf.HTTPProxy) } go dns.StartDNS(conf.DNSServer, conf.ClientIP) - proxy.StartClient(conf.NetType, conf.ServerAddr, conf.Cipher, conf.Password, conf.ClientIP) + proxy.StartClient(tran, conf.ServerAddr, conf.Cipher, conf.Password, conf.ClientIP) } } diff --git a/proxy/client.go b/proxy/client.go index e2974bf..d81b873 100644 --- a/proxy/client.go +++ b/proxy/client.go @@ -4,33 +4,12 @@ import ( "net" "github.com/golang/glog" - "github.com/wweir/sower/proxy/kcp" - "github.com/wweir/sower/proxy/quic" - "github.com/wweir/sower/proxy/tcp" - "github.com/wweir/sower/shadow" + "github.com/wweir/sower/proxy/shadow" + "github.com/wweir/sower/proxy/transport" ) -type Client interface { - Dial(server string) (net.Conn, error) -} - -func NewClient(netType string) Client { - switch netType { - case QUIC.String(): - return quic.NewClient() - case KCP.String(): - return kcp.NewClient() - case TCP.String(): - return tcp.NewClient() - default: - glog.Fatalln("invalid net type: " + netType) - return nil - } -} - -func StartClient(netType, server, cipher, password, listenIP string) { +func StartClient(tran transport.Transport, server, cipher, password, listenIP string) { connCh := listenLocal(listenIP, []string{"80", "443"}) - client := NewClient(netType) resolved := false glog.Infoln("Client started.") @@ -47,7 +26,7 @@ func StartClient(netType, server, cipher, password, listenIP string) { } glog.V(1).Infof("new conn from (%s) to (%s)", conn.RemoteAddr(), server) - rc, err := client.Dial(server) + rc, err := tran.Dial(server) if err != nil { conn.Close() glog.Errorln(err) diff --git a/proxy/http_proxy.go b/proxy/http_proxy.go index 41e025a..40ee112 100644 --- a/proxy/http_proxy.go +++ b/proxy/http_proxy.go @@ -9,11 +9,11 @@ import ( "time" "github.com/golang/glog" - "github.com/wweir/sower/shadow" + "github.com/wweir/sower/proxy/shadow" + "github.com/wweir/sower/proxy/transport" ) -func StartHttpProxy(netType, server, cipher, password, addr string) { - client := NewClient(netType) +func StartHttpProxy(tran transport.Transport, server, cipher, password, addr string) { resolved := false srv := &http.Server{ @@ -29,9 +29,9 @@ func StartHttpProxy(netType, server, cipher, password, addr string) { } if r.Method == http.MethodConnect { - httpsProxy(w, r, client, server, cipher, password) + httpsProxy(w, r, tran, server, cipher, password) } else { - httpProxy(w, r, client, server, cipher, password) + httpProxy(w, r, tran, server, cipher, password) } }), // Disable HTTP/2. @@ -42,10 +42,12 @@ func StartHttpProxy(netType, server, cipher, password, addr string) { glog.Fatalln(srv.ListenAndServe()) } -func httpProxy(w http.ResponseWriter, req *http.Request, client Client, server, cipher, password string) { +func httpProxy(w http.ResponseWriter, req *http.Request, + tran transport.Transport, server, cipher, password string) { + roundTripper := &http.Transport{ DialContext: func(context.Context, string, string) (net.Conn, error) { - conn, err := client.Dial(server) + conn, err := tran.Dial(server) if err != nil { return nil, err } @@ -74,7 +76,9 @@ func httpProxy(w http.ResponseWriter, req *http.Request, client Client, server, io.Copy(w, resp.Body) } -func httpsProxy(w http.ResponseWriter, r *http.Request, client Client, server, cipher, password string) { +func httpsProxy(w http.ResponseWriter, r *http.Request, + tran transport.Transport, server, cipher, password string) { + // local conn conn, _, err := w.(http.Hijacker).Hijack() if err != nil { @@ -90,7 +94,7 @@ func httpsProxy(w http.ResponseWriter, r *http.Request, client Client, server, c } // remote conn - rc, err := client.Dial(server) + rc, err := tran.Dial(server) if err != nil { conn.Close() http.Error(w, err.Error(), http.StatusServiceUnavailable) diff --git a/proxy/kcp/client.go b/proxy/kcp/client.go deleted file mode 100644 index 8092e43..0000000 --- a/proxy/kcp/client.go +++ /dev/null @@ -1,65 +0,0 @@ -package kcp - -import ( - "net" - - "github.com/pkg/errors" - kcp "github.com/xtaci/kcp-go" -) - -type client struct { - DataShard int - ParityShard int - DSCP int - SockBuf int - AckNodelay bool - NoDelay int - Interval int - Resend int - NoCongestion int - SndWnd int - RcvWnd int - MTU int -} - -func NewClient() *client { - return &client{ - DataShard: 10, - ParityShard: 3, - DSCP: 0, - SockBuf: 4194304, - NoDelay: 0, - Interval: 50, - Resend: 0, - NoCongestion: 0, - SndWnd: 0, - RcvWnd: 0, - MTU: 1350, - } -} - -func (c *client) Dial(server string) (net.Conn, error) { - conn, err := kcp.DialWithOptions(server, nil, c.DataShard, c.ParityShard) - if err != nil { - return nil, errors.Wrap(err, "dial") - } - - conn.SetStreamMode(true) - conn.SetWriteDelay(false) - conn.SetNoDelay(c.NoDelay, c.Interval, c.Resend, c.NoCongestion) - conn.SetWindowSize(c.SndWnd, c.RcvWnd) - conn.SetMtu(c.MTU) - conn.SetACKNoDelay(c.AckNodelay) - - if err := conn.SetDSCP(c.DSCP); err != nil { - return nil, errors.Wrap(err, "SetDSCP") - } - if err := conn.SetReadBuffer(c.SockBuf); err != nil { - return nil, errors.Wrap(err, "SetReadBuffer") - } - if err := conn.SetWriteBuffer(c.SockBuf); err != nil { - return nil, errors.Wrap(err, "SetWriteBuffer") - } - - return conn, nil -} diff --git a/proxy/kcp/server.go b/proxy/kcp/server.go deleted file mode 100644 index 91a0b50..0000000 --- a/proxy/kcp/server.go +++ /dev/null @@ -1,56 +0,0 @@ -package kcp - -import ( - "net" - - "github.com/golang/glog" - "github.com/pkg/errors" - kcp "github.com/xtaci/kcp-go" -) - -type server struct { - DataShard int - ParityShard int - DSCP int - SockBuf int -} - -func NewServer() *server { - return &server{ - DataShard: 10, - ParityShard: 3, - DSCP: 0, - SockBuf: 4194304, - } -} - -func (s *server) Listen(port string) (<-chan net.Conn, error) { - ln, err := kcp.ListenWithOptions(port, nil, s.DataShard, s.ParityShard) - if err != nil { - return nil, err - } - - if err := ln.SetDSCP(s.DSCP); err != nil { - return nil, errors.Wrap(err, "SetDSCP") - } - if err := ln.SetReadBuffer(s.SockBuf); err != nil { - return nil, errors.Wrap(err, "SetReadBuffer") - } - if err := ln.SetWriteBuffer(s.SockBuf); err != nil { - return nil, errors.Wrap(err, "SetWriteBuffer") - } - - connCh := make(chan net.Conn) - go func() { - for { - conn, err := ln.AcceptKCP() - if err != nil { - glog.Fatalln("KCP listen:", err) - } - - connCh <- conn - } - }() - - return connCh, nil -} diff --git a/proxy/nettype_string.go b/proxy/nettype_string.go deleted file mode 100644 index fed04b6..0000000 --- a/proxy/nettype_string.go +++ /dev/null @@ -1,16 +0,0 @@ -// Code generated by "stringer -type=netType util.go"; DO NOT EDIT. - -package proxy - -import "strconv" - -const _netType_name = "QUICKCPTCP" - -var _netType_index = [...]uint8{0, 4, 7, 10} - -func (i netType) String() string { - if i < 0 || i >= netType(len(_netType_index)-1) { - return "netType(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _netType_name[_netType_index[i]:_netType_index[i+1]] -} diff --git a/parse/parse_addr.go b/proxy/parser/parse_addr.go similarity index 98% rename from parse/parse_addr.go rename to proxy/parser/parse_addr.go index 84ad1f7..d098083 100644 --- a/parse/parse_addr.go +++ b/proxy/parser/parse_addr.go @@ -1,4 +1,4 @@ -package parse +package parser import ( "bufio" diff --git a/parse/sni.go b/proxy/parser/sni.go similarity index 99% rename from parse/sni.go rename to proxy/parser/sni.go index ea76053..aa665d1 100644 --- a/parse/sni.go +++ b/proxy/parser/sni.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package parse +package parser import ( "encoding/binary" diff --git a/proxy/quic/client.go b/proxy/quic/client.go deleted file mode 100644 index 7c28ff4..0000000 --- a/proxy/quic/client.go +++ /dev/null @@ -1,56 +0,0 @@ -package quic - -import ( - "crypto/tls" - "net" - "time" - - quic "github.com/lucas-clemente/quic-go" - "github.com/pkg/errors" - "github.com/wweir/sower/util" -) - -type client struct { - conf *quic.Config - sess quic.Session -} - -func NewClient() *client { - return &client{ - conf: &quic.Config{ - HandshakeTimeout: time.Second, - KeepAlive: true, - IdleTimeout: time.Minute, - }, - } -} - -func (c *client) Dial(server string) (net.Conn, error) { - if c.sess == nil { - if sess, err := quic.DialAddr(server, &tls.Config{InsecureSkipVerify: true}, c.conf); err != nil { - return nil, errors.Wrap(err, "session") - } else { - go func() { - <-sess.Context().Done() - sess.Close() - c.sess = nil - }() - c.sess = sess - } - } - - var stream quic.Stream - if err := util.WithTimeout(func() (err error) { - if stream, err = c.sess.OpenStream(); err != nil { - c.sess = nil - } - return - }, time.Second); err != nil { - return nil, errors.Wrap(err, "stream") - } - - return &streamConn{ - Stream: stream, - sess: c.sess, - }, nil -} diff --git a/proxy/quic/server.go b/proxy/quic/server.go deleted file mode 100644 index 11f688a..0000000 --- a/proxy/quic/server.go +++ /dev/null @@ -1,55 +0,0 @@ -package quic - -import ( - "net" - - "github.com/golang/glog" - quic "github.com/lucas-clemente/quic-go" - "github.com/pkg/errors" -) - -type server struct { - conf *quic.Config -} - -func NewServer() *server { - return &server{ - conf: &quic.Config{ - MaxIncomingStreams: 1024, - }, - } -} - -func (s *server) Listen(port string) (<-chan net.Conn, error) { - ln, err := quic.ListenAddr(port, mockTlsPem(), s.conf) - if err != nil { - return nil, errors.WithStack(err) - } - - connCh := make(chan net.Conn) - go func() { - for { - sess, err := ln.Accept() - if err != nil { - glog.Fatalln(err) - } - go accept(sess, connCh) - } - }() - return connCh, nil -} - -func accept(sess quic.Session, connCh chan<- net.Conn) { - glog.V(1).Infoln("new session from ", sess.RemoteAddr()) - defer sess.Close() - - for { - stream, err := sess.AcceptStream() - if err != nil { - glog.Errorln(err) - return - } - - connCh <- &streamConn{stream, sess} - } -} diff --git a/proxy/quic/util.go b/proxy/quic/util.go deleted file mode 100644 index 8d503d5..0000000 --- a/proxy/quic/util.go +++ /dev/null @@ -1,48 +0,0 @@ -package quic - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "encoding/pem" - "math/big" - "net" - - "github.com/golang/glog" - quic "github.com/lucas-clemente/quic-go" -) - -type streamConn struct { - quic.Stream - sess quic.Session -} - -func (s *streamConn) LocalAddr() net.Addr { - return s.sess.LocalAddr() -} - -func (s *streamConn) RemoteAddr() net.Addr { - return s.sess.RemoteAddr() -} - -func mockTlsPem() *tls.Config { - key, err := rsa.GenerateKey(rand.Reader, 1024) - if err != nil { - glog.Fatalln(err) - } - template := x509.Certificate{SerialNumber: big.NewInt(1)} - certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key) - if err != nil { - glog.Fatalln(err) - } - - certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) - keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}) - - tlsCert, err := tls.X509KeyPair(certPEM, keyPEM) - if err != nil { - glog.Fatalln(err) - } - return &tls.Config{Certificates: []tls.Certificate{tlsCert}} -} diff --git a/proxy/server.go b/proxy/server.go index 7105fe5..badd09e 100644 --- a/proxy/server.go +++ b/proxy/server.go @@ -5,32 +5,12 @@ import ( "strings" "github.com/golang/glog" - "github.com/wweir/sower/parse" - "github.com/wweir/sower/proxy/kcp" - "github.com/wweir/sower/proxy/quic" - "github.com/wweir/sower/proxy/tcp" - "github.com/wweir/sower/shadow" + "github.com/wweir/sower/proxy/parser" + "github.com/wweir/sower/proxy/shadow" + "github.com/wweir/sower/proxy/transport" ) -type Server interface { - Listen(port string) (<-chan net.Conn, error) -} - -func NewServer(netType string) Server { - switch netType { - case QUIC.String(): - return quic.NewServer() - case KCP.String(): - return kcp.NewServer() - case TCP.String(): - return tcp.NewServer() - default: - glog.Fatalln("invalid net type: " + netType) - return nil - } -} - -func StartServer(netType, port, cipher, password string) { +func StartServer(tran transport.Transport, port, cipher, password string) { if port == "" { glog.Fatalln("port must set") } @@ -38,7 +18,7 @@ func StartServer(netType, port, cipher, password string) { port = ":" + port } - connCh, err := NewServer(netType).Listen(port) + connCh, err := tran.Listen(port) if err != nil { glog.Fatalf("listen %v fail: %s", port, err) } @@ -53,7 +33,7 @@ func StartServer(netType, port, cipher, password string) { } func handle(conn net.Conn) { - conn, addr, err := parse.ParseAddr(conn) + conn, addr, err := parser.ParseAddr(conn) if err != nil { conn.Close() glog.Warningln(err) diff --git a/shadow/cipher.go b/proxy/shadow/cipher.go similarity index 100% rename from shadow/cipher.go rename to proxy/shadow/cipher.go diff --git a/shadow/ciphertype_string.go b/proxy/shadow/ciphertype_string.go similarity index 100% rename from shadow/ciphertype_string.go rename to proxy/shadow/ciphertype_string.go diff --git a/shadow/doc.go b/proxy/shadow/doc.go similarity index 100% rename from shadow/doc.go rename to proxy/shadow/doc.go diff --git a/shadow/shadow.go b/proxy/shadow/shadow.go similarity index 100% rename from shadow/shadow.go rename to proxy/shadow/shadow.go diff --git a/proxy/tcp/client.go b/proxy/tcp/client.go deleted file mode 100644 index f0be078..0000000 --- a/proxy/tcp/client.go +++ /dev/null @@ -1,26 +0,0 @@ -package tcp - -import ( - "net" - "time" -) - -type client struct { - DialTimeout time.Duration -} - -func NewClient() *client { - return &client{ - DialTimeout: 5 * time.Second, - } -} - -func (c *client) Dial(server string) (net.Conn, error) { - conn, err := net.DialTimeout("tcp", server, c.DialTimeout) - if err != nil { - return nil, err - } - - conn.(*net.TCPConn).SetKeepAlive(true) - return conn, nil -} diff --git a/proxy/tcp/server.go b/proxy/tcp/server.go deleted file mode 100644 index ad280fe..0000000 --- a/proxy/tcp/server.go +++ /dev/null @@ -1,35 +0,0 @@ -package tcp - -import ( - "net" - - "github.com/golang/glog" -) - -type server struct { -} - -func NewServer() *server { - return &server{} -} - -func (s *server) Listen(port string) (<-chan net.Conn, error) { - ln, err := net.Listen("tcp", port) - if err != nil { - return nil, err - } - - connCh := make(chan net.Conn) - go func() { - for { - conn, err := ln.Accept() - if err != nil { - glog.Fatalln("TCP listen:", err) - } - - conn.(*net.TCPConn).SetKeepAlive(true) - connCh <- conn - } - }() - return connCh, nil -} diff --git a/proxy/transport/kcp.go b/proxy/transport/kcp.go new file mode 100644 index 0000000..301bcd5 --- /dev/null +++ b/proxy/transport/kcp.go @@ -0,0 +1,115 @@ +package transport + +import ( + "net" + + "github.com/golang/glog" + "github.com/pkg/errors" + kcp "github.com/xtaci/kcp-go" +) + +type kcpTran struct { + client + server +} +type client struct { + DataShard int + ParityShard int + DSCP int + SockBuf int + AckNodelay bool + NoDelay int + Interval int + Resend int + NoCongestion int + SndWnd int + RcvWnd int + MTU int +} +type server struct { + DataShard int + ParityShard int + DSCP int + SockBuf int +} + +func init() { + transports["KCP"] = &kcpTran{ + client: client{ + DataShard: 10, + ParityShard: 3, + DSCP: 0, + SockBuf: 4194304, + NoDelay: 0, + Interval: 50, + Resend: 0, + NoCongestion: 0, + SndWnd: 0, + RcvWnd: 0, + MTU: 1350, + }, + server: server{ + DataShard: 10, + ParityShard: 3, + DSCP: 0, + SockBuf: 4194304, + }, + } +} + +func (c *client) Dial(server string) (net.Conn, error) { + conn, err := kcp.DialWithOptions(server, nil, c.DataShard, c.ParityShard) + if err != nil { + return nil, errors.Wrap(err, "dial") + } + + conn.SetStreamMode(true) + conn.SetWriteDelay(false) + conn.SetNoDelay(c.NoDelay, c.Interval, c.Resend, c.NoCongestion) + conn.SetWindowSize(c.SndWnd, c.RcvWnd) + conn.SetMtu(c.MTU) + conn.SetACKNoDelay(c.AckNodelay) + + if err := conn.SetDSCP(c.DSCP); err != nil { + return nil, errors.Wrap(err, "SetDSCP") + } + if err := conn.SetReadBuffer(c.SockBuf); err != nil { + return nil, errors.Wrap(err, "SetReadBuffer") + } + if err := conn.SetWriteBuffer(c.SockBuf); err != nil { + return nil, errors.Wrap(err, "SetWriteBuffer") + } + + return conn, nil +} + +func (s *server) Listen(port string) (<-chan net.Conn, error) { + ln, err := kcp.ListenWithOptions(port, nil, s.DataShard, s.ParityShard) + if err != nil { + return nil, err + } + + if err := ln.SetDSCP(s.DSCP); err != nil { + return nil, errors.Wrap(err, "SetDSCP") + } + if err := ln.SetReadBuffer(s.SockBuf); err != nil { + return nil, errors.Wrap(err, "SetReadBuffer") + } + if err := ln.SetWriteBuffer(s.SockBuf); err != nil { + return nil, errors.Wrap(err, "SetWriteBuffer") + } + + connCh := make(chan net.Conn) + go func() { + for { + conn, err := ln.AcceptKCP() + if err != nil { + glog.Fatalln("KCP listen:", err) + } + + connCh <- conn + } + }() + + return connCh, nil +} diff --git a/proxy/transport/quic.go b/proxy/transport/quic.go new file mode 100644 index 0000000..e43e11f --- /dev/null +++ b/proxy/transport/quic.go @@ -0,0 +1,136 @@ +package transport + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "encoding/pem" + "math/big" + "net" + "time" + + "github.com/golang/glog" + quic "github.com/lucas-clemente/quic-go" + "github.com/pkg/errors" + "github.com/wweir/sower/util" +) + +type quicTran struct { + clientConf *quic.Config + sess quic.Session + + serverConf *quic.Config +} + +func init() { + transports["QUIC"] = &quicTran{ + + clientConf: &quic.Config{ + HandshakeTimeout: time.Second, + KeepAlive: true, + IdleTimeout: time.Minute, + }, + serverConf: &quic.Config{ + MaxIncomingStreams: 1024, + }, + } +} + +func (c *quicTran) Dial(server string) (net.Conn, error) { + if c.sess == nil { + if sess, err := quic.DialAddr(server, &tls.Config{InsecureSkipVerify: true}, c.clientConf); err != nil { + return nil, errors.Wrap(err, "session") + } else { + go func() { + <-sess.Context().Done() + sess.Close() + c.sess = nil + }() + c.sess = sess + } + } + + var stream quic.Stream + if err := util.WithTimeout(func() (err error) { + if stream, err = c.sess.OpenStream(); err != nil { + c.sess = nil + } + return + }, time.Second); err != nil { + return nil, errors.Wrap(err, "stream") + } + + return &streamConn{ + Stream: stream, + sess: c.sess, + }, nil +} + +type streamConn struct { + quic.Stream + sess quic.Session +} + +func (s *streamConn) LocalAddr() net.Addr { + return s.sess.LocalAddr() +} + +func (s *streamConn) RemoteAddr() net.Addr { + return s.sess.RemoteAddr() +} + +func mockTlsPem() *tls.Config { + key, err := rsa.GenerateKey(rand.Reader, 1024) + if err != nil { + glog.Fatalln(err) + } + template := x509.Certificate{SerialNumber: big.NewInt(1)} + certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key) + if err != nil { + glog.Fatalln(err) + } + + certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) + keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}) + + tlsCert, err := tls.X509KeyPair(certPEM, keyPEM) + if err != nil { + glog.Fatalln(err) + } + return &tls.Config{Certificates: []tls.Certificate{tlsCert}} +} + +func (s *quicTran) Listen(port string) (<-chan net.Conn, error) { + ln, err := quic.ListenAddr(port, mockTlsPem(), s.serverConf) + if err != nil { + return nil, errors.WithStack(err) + } + + connCh := make(chan net.Conn) + go func() { + for { + sess, err := ln.Accept() + if err != nil { + glog.Fatalln(err) + } + go accept(sess, connCh) + } + }() + return connCh, nil +} + +func accept(sess quic.Session, connCh chan<- net.Conn) { + glog.V(1).Infoln("new session from ", sess.RemoteAddr()) + defer sess.Close() + + for { + stream, err := sess.AcceptStream() + if err != nil { + glog.Errorln(err) + return + } + + connCh <- &streamConn{stream, sess} + } +} diff --git a/proxy/transport/tcp.go b/proxy/transport/tcp.go new file mode 100644 index 0000000..8127c19 --- /dev/null +++ b/proxy/transport/tcp.go @@ -0,0 +1,49 @@ +package transport + +import ( + "net" + "time" + + "github.com/golang/glog" +) + +type tcp struct { + DialTimeout time.Duration +} + +func init() { + transports["TCP"] = &tcp{ + DialTimeout: 5 * time.Second, + } +} + +func (t *tcp) Dial(server string) (net.Conn, error) { + conn, err := net.DialTimeout("tcp", server, t.DialTimeout) + if err != nil { + return nil, err + } + + conn.(*net.TCPConn).SetKeepAlive(true) + return conn, nil +} + +func (*tcp) Listen(port string) (<-chan net.Conn, error) { + ln, err := net.Listen("tcp", port) + if err != nil { + return nil, err + } + + connCh := make(chan net.Conn) + go func() { + for { + conn, err := ln.Accept() + if err != nil { + glog.Fatalln("TCP listen:", err) + } + + conn.(*net.TCPConn).SetKeepAlive(true) + connCh <- conn + } + }() + return connCh, nil +} diff --git a/proxy/transport/util.go b/proxy/transport/util.go new file mode 100644 index 0000000..15e1229 --- /dev/null +++ b/proxy/transport/util.go @@ -0,0 +1,30 @@ +package transport + +import ( + "net" + + "github.com/pkg/errors" +) + +type Transport interface { + Dial(server string) (net.Conn, error) + Listen(port string) (<-chan net.Conn, error) +} + +var transports = map[string]Transport{} + +func ListTransports() []string { + list := make([]string, 0, len(transports)) + for key := range transports { + list = append(list, key) + } + return list +} + +func GetTransport(netType string) (Transport, error) { + tran, ok := transports[netType] + if !ok { + return nil, errors.New("invalid net type: " + netType) + } + return tran, nil +} diff --git a/proxy/util.go b/proxy/util.go index 9b9efda..f6b9c14 100644 --- a/proxy/util.go +++ b/proxy/util.go @@ -10,15 +10,6 @@ import ( "github.com/golang/glog" ) -//go:generate stringer -type=netType $GOFILE -type netType int - -const ( - QUIC netType = iota - KCP - TCP -) - func relay(conn1, conn2 net.Conn) { wg := &sync.WaitGroup{} exitFlag := new(int32) From 26055c52031af62b1d9514bb3c190dbbbbcb1201 Mon Sep 17 00:00:00 2001 From: wweir Date: Mon, 11 Mar 2019 00:11:07 +0800 Subject: [PATCH 2/2] Refactor deploy --- {deploy => .circleci}/Dockerfile | 0 {deploy => .circleci}/cc.wweir.sower.plist | 0 .circleci/config.yml | 2 +- {deploy => .circleci}/install | 0 {deploy => .circleci}/sower-client.service | 0 {deploy => .circleci}/sower-server.service | 0 {deploy => .circleci}/uninstall | 0 Makefile | 2 +- README.md | 2 +- 9 files changed, 3 insertions(+), 3 deletions(-) rename {deploy => .circleci}/Dockerfile (100%) rename {deploy => .circleci}/cc.wweir.sower.plist (100%) rename {deploy => .circleci}/install (100%) rename {deploy => .circleci}/sower-client.service (100%) rename {deploy => .circleci}/sower-server.service (100%) rename {deploy => .circleci}/uninstall (100%) diff --git a/deploy/Dockerfile b/.circleci/Dockerfile similarity index 100% rename from deploy/Dockerfile rename to .circleci/Dockerfile diff --git a/deploy/cc.wweir.sower.plist b/.circleci/cc.wweir.sower.plist similarity index 100% rename from deploy/cc.wweir.sower.plist rename to .circleci/cc.wweir.sower.plist diff --git a/.circleci/config.yml b/.circleci/config.yml index 634c78a..26d3c9b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,7 +9,7 @@ jobs: name: Prepare Environment command: | cp conf/sower.toml . - cp deploy/* . + cp .circleci/* . mkdir artifacts - run: name: Run Unit Test diff --git a/deploy/install b/.circleci/install similarity index 100% rename from deploy/install rename to .circleci/install diff --git a/deploy/sower-client.service b/.circleci/sower-client.service similarity index 100% rename from deploy/sower-client.service rename to .circleci/sower-client.service diff --git a/deploy/sower-server.service b/.circleci/sower-server.service similarity index 100% rename from deploy/sower-server.service rename to .circleci/sower-server.service diff --git a/deploy/uninstall b/.circleci/uninstall similarity index 100% rename from deploy/uninstall rename to .circleci/uninstall diff --git a/Makefile b/Makefile index c94600f..c9e3375 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ build: "-X main.version=$(shell git describe --tags) \ -X main.date=$(shell date +%Y-%m-%d)" image: - docker build -t sower -f deploy/Dockerfile . + docker build -t sower -f .circleci/Dockerfile . kill: sudo pkill -9 sower || true diff --git a/README.md b/README.md index e2a916f..4c703a3 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ After Deployed, please check your config file, it is placed in `/usr/local/etc/s Auto deploy script support Linux server side and masOS/Linux client side. ```shell -$ bash -c "$(curl -s https://raw.githubusercontent.com/wweir/sower/master/deploy/install)" +$ bash -c "$(curl -s https://raw.githubusercontent.com/wweir/sower/master/.circleci/install)" ``` Then modify the configuration file as needed and set `127.0.0.1` as your first domain name server.