From 67a88d032aeda04695d58b2c62a11e31174fe5ae Mon Sep 17 00:00:00 2001 From: mohanson Date: Tue, 6 Aug 2024 14:30:54 +0800 Subject: [PATCH] QUIC --- cmd/daze/main.go | 14 ++++ go.mod | 7 ++ go.sum | 6 ++ protocol/czar/mux_test.go | 4 +- protocol/etch/cert.go | 38 +++++++++ protocol/etch/cmd/main/main.go | 9 +++ protocol/etch/engine.go | 139 +++++++++++++++++++++++++++++++++ protocol/etch/engine_test.go | 95 ++++++++++++++++++++++ 8 files changed, 310 insertions(+), 2 deletions(-) create mode 100644 protocol/etch/cert.go create mode 100644 protocol/etch/cmd/main/main.go create mode 100644 protocol/etch/engine.go create mode 100644 protocol/etch/engine_test.go diff --git a/cmd/daze/main.go b/cmd/daze/main.go index 5047f0a..2130a0f 100644 --- a/cmd/daze/main.go +++ b/cmd/daze/main.go @@ -17,6 +17,7 @@ import ( "github.com/mohanson/daze/protocol/baboon" "github.com/mohanson/daze/protocol/czar" "github.com/mohanson/daze/protocol/dahlia" + "github.com/mohanson/daze/protocol/etch" ) // Conf is acting as package level configuration. @@ -105,6 +106,10 @@ func main() { server := dahlia.NewServer(*flListen, *flExtend, *flCipher) defer server.Close() doa.Nil(server.Run()) + case "etch": + server := etch.NewServer(*flListen, *flCipher) + defer server.Close() + doa.Nil(server.Run()) } if *flGpprof != "" { _ = pprof.Handler @@ -171,6 +176,15 @@ func main() { client := dahlia.NewClient(*flListen, *flServer, *flCipher) defer client.Close() doa.Nil(client.Run()) + case "etch": + client := etch.NewClient(*flServer, *flCipher) + locale := daze.NewLocale(*flListen, daze.NewAimbot(client, &daze.AimbotOption{ + Type: *flFilter, + Rule: *flRulels, + Cidr: *flCIDRls, + })) + defer locale.Close() + doa.Nil(locale.Run()) } if *flGpprof != "" { _ = pprof.Handler diff --git a/go.mod b/go.mod index 7eb7284..62047ce 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,10 @@ module github.com/mohanson/daze go 1.22 + +require golang.org/x/net v0.27.0 + +require ( + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/sys v0.22.0 // indirect +) diff --git a/go.sum b/go.sum index e69de29..55a3df9 100644 --- a/go.sum +++ b/go.sum @@ -0,0 +1,6 @@ +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/protocol/czar/mux_test.go b/protocol/czar/mux_test.go index c775e5e..9293a06 100644 --- a/protocol/czar/mux_test.go +++ b/protocol/czar/mux_test.go @@ -121,11 +121,11 @@ func (t *Tester) Mux() error { break } mux := NewMuxServer(cli) - go func(mux *Mux) { + go func() { for cli := range mux.Accept() { go t.TCPServe(cli) } - }(mux) + }() } }() return nil diff --git a/protocol/etch/cert.go b/protocol/etch/cert.go new file mode 100644 index 0000000..1141b87 --- /dev/null +++ b/protocol/etch/cert.go @@ -0,0 +1,38 @@ +package etch + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "math/rand/v2" + "net" + "time" + + "github.com/mohanson/daze" + "github.com/mohanson/daze/lib/doa" +) + +func GenCert() tls.Certificate { + priv := doa.Try(ecdsa.GenerateKey(elliptic.P256(), &daze.RandomReader{})) + temp := x509.Certificate{ + SerialNumber: big.NewInt(rand.Int64()), + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(365 * 24 * time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}, + } + cert := doa.Try(x509.CreateCertificate(&daze.RandomReader{}, &temp, &temp, &priv.PublicKey, priv)) + certFile := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert}) + pkcs := doa.Try(x509.MarshalPKCS8PrivateKey(priv)) + pkcsFile := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: pkcs}) + return doa.Try(tls.X509KeyPair(certFile, pkcsFile)) +} diff --git a/protocol/etch/cmd/main/main.go b/protocol/etch/cmd/main/main.go new file mode 100644 index 0000000..8689c74 --- /dev/null +++ b/protocol/etch/cmd/main/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/mohanson/daze/protocol/etch" +) + +func main() { + etch.GenCert() +} diff --git a/protocol/etch/engine.go b/protocol/etch/engine.go new file mode 100644 index 0000000..fd34b2c --- /dev/null +++ b/protocol/etch/engine.go @@ -0,0 +1,139 @@ +package etch + +import ( + "context" + "crypto/tls" + "io" + "log" + "math" + + "github.com/mohanson/daze" + "github.com/mohanson/daze/lib/doa" + "github.com/mohanson/daze/protocol/ashe" + "golang.org/x/net/quic" +) + +// Stream is an ordered byte stream. Reads are buffered, writes are not buffered. +type Stream struct { + *quic.Stream +} + +// Write implements the Conn Write method. +func (c *Stream) Write(p []byte) (int, error) { + n, err := c.Stream.Write(p) + c.Stream.Flush() + return n, err +} + +// NewStream returns a new Stream. +func NewStream(s *quic.Stream) *Stream { + return &Stream{s} +} + +// Server implemented the etch protocol. +type Server struct { + Cipher []byte + Closer *quic.Endpoint + Listen string +} + +// Close listener. Calling this function will disconnect all connections. +func (s *Server) Close() error { + if s.Closer != nil { + return s.Closer.Close(context.Background()) + } + return nil +} + +// Serve incoming connections. Parameter cli will be closed automatically when the function exits. +func (s *Server) Serve(ctx *daze.Context, cli io.ReadWriteCloser) error { + spy := &ashe.Server{Cipher: s.Cipher} + return spy.Serve(ctx, cli) +} + +// Run it. +func (s *Server) Run() error { + l, err := quic.Listen("udp", s.Listen, &quic.Config{ + TLSConfig: &tls.Config{ + Certificates: []tls.Certificate{GenCert()}, + MinVersion: tls.VersionTLS13, + }, + }) + if err != nil { + return err + } + s.Closer = l + log.Println("main: listen and serve on", s.Listen) + go func() { + idx := uint32(math.MaxUint32) + for { + mux, err := l.Accept(context.Background()) + if err != nil { + break + } + go func() { + defer mux.Close() + for { + cli, err := mux.AcceptStream(context.Background()) + if err != nil { + break + } + idx++ + ctx := &daze.Context{Cid: idx} + log.Printf("conn: %08x accept", ctx.Cid) + go func() { + cli := NewStream(cli) + defer cli.Close() + if err := s.Serve(ctx, cli); err != nil { + log.Printf("conn: %08x error %s", ctx.Cid, err) + } + log.Printf("conn: %08x closed", ctx.Cid) + }() + } + }() + } + }() + return nil +} + +// NewServer returns a new Server. +func NewServer(listen string, cipher string) *Server { + return &Server{ + Cipher: daze.Salt(cipher), + Listen: listen, + } +} + +// Client implemented the etch protocol. +type Client struct { + Cipher []byte + Quicon *quic.Conn + Server string +} + +// Dial connects to the address on the named network. +func (c *Client) Dial(ctx *daze.Context, network string, address string) (io.ReadWriteCloser, error) { + srv, err := c.Quicon.NewStream(context.Background()) + if err != nil { + return nil, err + } + spy := &ashe.Client{Cipher: c.Cipher} + con, err := spy.Estab(ctx, NewStream(srv), network, address) + return con, err +} + +// NewClient returns a new Client. A secret data needs to be passed in Cipher, as a sign to interface with the Server. +func NewClient(server, cipher string) *Client { + quiced := doa.Try(quic.Listen("udp", ":0", nil)) + quicon := doa.Try(quiced.Dial(context.Background(), "udp", server, &quic.Config{ + TLSConfig: &tls.Config{ + InsecureSkipVerify: true, + MinVersion: tls.VersionTLS13, + }, + })) + return &Client{ + Cipher: daze.Salt(cipher), + Quicon: quicon, + Server: server, + } +} diff --git a/protocol/etch/engine_test.go b/protocol/etch/engine_test.go new file mode 100644 index 0000000..8676393 --- /dev/null +++ b/protocol/etch/engine_test.go @@ -0,0 +1,95 @@ +package etch + +import ( + "io" + "testing" + + "github.com/mohanson/daze" + "github.com/mohanson/daze/lib/doa" +) + +const ( + EchoServerListenOn = "127.0.0.1:28080" + DazeServerListenOn = "127.0.0.1:28081" + Password = "password" +) + +func TestProtocolEtchTCP(t *testing.T) { + remote := daze.NewTester(EchoServerListenOn) + defer remote.Close() + remote.TCP() + + dazeServer := NewServer(DazeServerListenOn, Password) + defer dazeServer.Close() + dazeServer.Run() + + dazeClient := NewClient(DazeServerListenOn, Password) + ctx := &daze.Context{} + cli := doa.Try(dazeClient.Dial(ctx, "tcp", EchoServerListenOn)) + defer cli.Close() + + buf := make([]byte, 2048) + doa.Try(cli.Write([]byte{0x00, 0x00, 0x00, 0x80})) + doa.Try(io.ReadFull(cli, buf[:132])) +} + +func TestProtocolEtchTCPClientClose(t *testing.T) { + remote := daze.NewTester(EchoServerListenOn) + defer remote.Close() + remote.TCP() + + dazeServer := NewServer(DazeServerListenOn, Password) + defer dazeServer.Close() + dazeServer.Run() + + dazeClient := NewClient(DazeServerListenOn, Password) + ctx := &daze.Context{} + cli := doa.Try(dazeClient.Dial(ctx, "tcp", EchoServerListenOn)) + defer cli.Close() + + cli.Close() + _, er1 := cli.Write([]byte{0x01, 0x00, 0x00, 0x00}) + doa.Doa(er1 != nil) + buf := make([]byte, 2048) + _, er2 := io.ReadFull(cli, buf[:1]) + doa.Doa(er2 != nil) +} + +func TestProtocolEtchTCPServerClose(t *testing.T) { + remote := daze.NewTester(EchoServerListenOn) + defer remote.Close() + remote.TCP() + + dazeServer := NewServer(DazeServerListenOn, Password) + defer dazeServer.Close() + dazeServer.Run() + + dazeClient := NewClient(DazeServerListenOn, Password) + ctx := &daze.Context{} + cli := doa.Try(dazeClient.Dial(ctx, "tcp", EchoServerListenOn)) + defer cli.Close() + + buf := make([]byte, 2048) + doa.Try(cli.Write([]byte{0x01, 0x00, 0x00, 0x00})) + _, err := io.ReadFull(cli, buf[:1]) + doa.Doa(err != nil) +} + +func TestProtocolEtchUDP(t *testing.T) { + remote := daze.NewTester(EchoServerListenOn) + defer remote.Close() + remote.UDP() + + dazeServer := NewServer(DazeServerListenOn, Password) + defer dazeServer.Close() + dazeServer.Run() + + dazeClient := NewClient(DazeServerListenOn, Password) + ctx := &daze.Context{} + cli := doa.Try(dazeClient.Dial(ctx, "udp", EchoServerListenOn)) + defer cli.Close() + + buf := make([]byte, 2048) + doa.Try(cli.Write([]byte{0x00, 0x00, 0x00, 0x80})) + doa.Try(io.ReadFull(cli, buf[:132])) +}