-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
326 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,10 @@ | ||
module github.com/mohanson/daze | ||
|
||
go 1.23 | ||
|
||
require golang.org/x/net v0.29.0 | ||
|
||
require ( | ||
golang.org/x/crypto v0.27.0 // indirect | ||
golang.org/x/sys v0.25.0 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= | ||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= | ||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= | ||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= | ||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= | ||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= | ||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= | ||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= | ||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= | ||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= | ||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
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 NewCert() 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: doa.Try(time.Parse(time.DateOnly, "1970-01-01")), | ||
NotAfter: doa.Try(time.Parse(time.DateOnly, "9999-12-31")), | ||
KeyUsage: x509.KeyUsageDigitalSignature, | ||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, | ||
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)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package etch | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestProtocolEtchCert(t *testing.T) { | ||
NewCert() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
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 io.Writer. | ||
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{NewCert()}, | ||
MinVersion: tls.VersionTLS13, | ||
}, | ||
MaxIdleTimeout: -1, | ||
}) | ||
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 { | ||
log.Println("main:", err) | ||
break | ||
} | ||
go func() { | ||
defer mux.Close() | ||
for { | ||
cli, err := mux.AcceptStream(context.Background()) | ||
if err != nil { | ||
log.Println("main:", err) | ||
break | ||
} | ||
idx++ | ||
ctx := &daze.Context{Cid: idx} | ||
log.Printf("conn: %08x accept", ctx.Cid) | ||
go func() { | ||
defer cli.Close() | ||
if err := s.Serve(ctx, NewStream(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, | ||
}, | ||
MaxIdleTimeout: -1, | ||
})) | ||
return &Client{ | ||
Cipher: daze.Salt(cipher), | ||
Quicon: quicon, | ||
Server: server, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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[:128])) | ||
} | ||
|
||
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{0x02, 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{0x02, 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[:128])) | ||
} |