From 492421db2840a16e5974f92fc781e93b354fcc3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oto=20=C5=A0=C5=A5=C3=A1va?= Date: Mon, 31 Jul 2023 08:32:13 +0200 Subject: [PATCH 01/10] echo: add echo server This commit adds an echo server that reflects queries back at the sender. May be used for determining the baseline performance of QUIC (or, at least, of the `quic-go` implementation in combination with the client's QUIC implementation). --- .gitignore | 1 + README.md | 24 ++++++- cmd/echo/main.go | 165 +++++++++++++++++++++++++++++++++++++++++++++++ go.sum | 141 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 328 insertions(+), 3 deletions(-) create mode 100644 cmd/echo/main.go create mode 100644 go.sum diff --git a/.gitignore b/.gitignore index 1ce0c4a..1ffcc80 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # binaries /client +/echo /proxy # testing certs diff --git a/README.md b/README.md index 5813649..818b40a 100644 --- a/README.md +++ b/README.md @@ -85,17 +85,35 @@ ns1.com. 25 IN RRSIG A 13 2 26 20190325121645 20190323121645 44688 ns1.com. xJK5 ; EDNS: version 0; flags: do; udp: 512 ``` +## Echo server + +This codebase also includes an echo server, i.e. a server that, for each stream, +reads its whole contents, and reflects them back to the client. + +To build the echo server, use the following command: + +``` +go build ./cmd/echo +``` + +The echo server may be run the same way as the proxy, except that it does not +accept a `-backend` option, since it does not forward queries anywhere. + +``` +sudo ./echo +``` + ## Troubleshooting Note that this is an experimental code built on top of an experimental protocol. -The server and client in this repository use the same QUIC library +The servers and client in this repository use the same QUIC library and therefore they should be compatible. However, if a different client is used, the handshake may fail on the version negotiation. We suggest to check packet capture first when the client is unable to connect. -The proxy also logs information about accepted connections and streams which -can be used to inspect the sequence of events: +The proxy and the echo server also log information about accepted connections +and streams, which can be used to inspect the sequence of events: ``` $ sudo ./proxy -listen 127.0.0.1:853 -cert cert.pem -key key.pem -backend 8.8.4.4:53 diff --git a/cmd/echo/main.go b/cmd/echo/main.go new file mode 100644 index 0000000..3b05a43 --- /dev/null +++ b/cmd/echo/main.go @@ -0,0 +1,165 @@ +package main + +import ( + "context" + "crypto/tls" + "flag" + "fmt" + "io" + "os" + "os/signal" + "sync" + "syscall" + + "github.com/go-kit/kit/log" + quic "github.com/lucas-clemente/quic-go" + "github.com/oklog/run" +) + +func main() { + l := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) + l = log.WithPrefix(l, "ts", log.DefaultTimestampUTC) + + var g run.Group + + // proxy code loop + { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + g.Add(func() error { + return loop(l, ctx) + }, func(error) { + cancel() + }) + } + + // signal termination + { + sigterm := make(chan os.Signal, 1) + g.Add(func() error { + signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM) + if sig, ok := <-sigterm; ok { + l.Log("msg", "stopping the proxy", "signal", sig.String()) + } + return nil + }, func(error) { + signal.Stop(sigterm) + close(sigterm) + }) + } + + err := g.Run() + if err != nil { + l.Log("msg", "terminating after error", "err", err) + os.Exit(1) + } +} + +func loop(l log.Logger, ctx context.Context) error { + var ( + addr string + tlsCert string + tlsKey string + ) + + flag.StringVar(&addr, "listen", "127.0.0.1:853", "UDP address to listen on.") + flag.StringVar(&tlsCert, "cert", "cert.pem", "TLS certificate path.") + flag.StringVar(&tlsKey, "key", "key.pem", "TLS key path.") + + flag.Parse() + + cert, err := tls.LoadX509KeyPair(tlsCert, tlsKey) + if err != nil { + return fmt.Errorf("load certificate: %w", err) + } + + tls := tls.Config{ + Certificates: []tls.Certificate{cert}, + NextProtos: []string{"doq"}, + } + + listener, err := quic.ListenAddr(addr, &tls, nil) + if err != nil { + return fmt.Errorf("listen: %w", err) + } + defer listener.Close() + + l.Log("msg", "listening for clients", "addr", addr) + + wg := sync.WaitGroup{} + + for { + session, err := listener.Accept(ctx) + if err != nil { + wg.Wait() + return fmt.Errorf("accept connection: %w", err) + } + + l := log.With(l, "client", session.RemoteAddr()) + wg.Add(1) + go func() { + handleClient(l, ctx, session) + wg.Done() + }() + } + +} + +func handleClient(l log.Logger, ctx context.Context, session quic.Connection) { + l.Log("msg", "session accepted") + + var ( + err error + wg sync.WaitGroup = sync.WaitGroup{} + ) + + defer func() { + msg := "" + if err != nil { + msg = err.Error() + } + session.CloseWithError(0, msg) + + l.Log("msg", "session closed") + }() + + for { + stream, err := session.AcceptStream(ctx) + if err != nil { + break + } + + l := log.With(l, "stream_id", stream.StreamID()) + l.Log("msg", "stream accepted") + + wg.Add(1) + go func() { + defer func() { + wg.Done() + l.Log("msg", "stream closed") + }() + + if err := handleStream(stream); err != nil { + l.Log("msg", "stream failure", "err", err) + } + }() + } + + wg.Wait() +} + +func handleStream(stream quic.Stream) error { + defer stream.Close() + + data, err := io.ReadAll(stream) + if err != nil { + return fmt.Errorf("read all: %w", err) + } + + _, err = stream.Write(data) + if err != nil { + return fmt.Errorf("send response: %w", err) + } + return nil +} diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..6177adc --- /dev/null +++ b/go.sum @@ -0,0 +1,141 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= +github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/lucas-clemente/quic-go v0.29.2 h1:O8Mt0O6LpvEW+wfC40vZdcw0DngwYzoxq5xULZNzSI8= +github.com/lucas-clemente/quic-go v0.29.2/go.mod h1:g6/h9YMmLuU54tL1gW25uIi3VlBp3uv+sBihplIuskE= +github.com/marten-seemann/qtls-go1-18 v0.1.4 h1:ogomB+lWV3Vmwiu6RTwDVTMGx+9j7SEi98e8QB35Its= +github.com/marten-seemann/qtls-go1-18 v0.1.4/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= +github.com/marten-seemann/qtls-go1-19 v0.1.2 h1:ZevAEqKXH0bZmoOBPiqX2h5rhQ7cbZi+X+rlq2JUbCE= +github.com/marten-seemann/qtls-go1-19 v0.1.2/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= +github.com/miekg/dns v1.1.51 h1:0+Xg7vObnhrz/4ZCZcZh7zPXlmU0aveS2HDBd0m0qSo= +github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= +golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From c644a649f4a8d62101c00c42831f07d4aa338d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oto=20=C5=A0=C5=A5=C3=A1va?= Date: Mon, 31 Jul 2023 09:20:50 +0200 Subject: [PATCH 02/10] echo: replace io.ReadAll with stream.Read ReadAll is apparently SLOW for our purposes. Reading into a fixed-size buffer seems to be much better. --- cmd/echo/main.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/cmd/echo/main.go b/cmd/echo/main.go index 3b05a43..5d0f589 100644 --- a/cmd/echo/main.go +++ b/cmd/echo/main.go @@ -140,7 +140,7 @@ func handleClient(l log.Logger, ctx context.Context, session quic.Connection) { l.Log("msg", "stream closed") }() - if err := handleStream(stream); err != nil { + if err := handleStream(l, stream); err != nil { l.Log("msg", "stream failure", "err", err) } }() @@ -149,15 +149,16 @@ func handleClient(l log.Logger, ctx context.Context, session quic.Connection) { wg.Wait() } -func handleStream(stream quic.Stream) error { - defer stream.Close() - - data, err := io.ReadAll(stream) - if err != nil { - return fmt.Errorf("read all: %w", err) +func handleStream(l log.Logger, stream quic.Stream) error { + data := make([]byte, 2048) + n, err := stream.Read(data) + if err == io.EOF { + defer stream.Close() + } else if err != nil { + return fmt.Errorf("read query: %w", err) } - _, err = stream.Write(data) + _, err = stream.Write(data[:n]) if err != nil { return fmt.Errorf("send response: %w", err) } From 7c764f9b5b95e5252454fbc5d4d5d3430dd1128b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oto=20=C5=A0=C5=A5=C3=A1va?= Date: Thu, 7 Sep 2023 12:15:25 +0200 Subject: [PATCH 03/10] Exclude go.sum --- .gitignore | 3 ++ go.sum | 141 ----------------------------------------------------- 2 files changed, 3 insertions(+), 141 deletions(-) delete mode 100644 go.sum diff --git a/.gitignore b/.gitignore index 1ffcc80..fbcaaea 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ # testing certs /cert.pem /key.pem + +# Go sum +go.sum diff --git a/go.sum b/go.sum deleted file mode 100644 index 6177adc..0000000 --- a/go.sum +++ /dev/null @@ -1,141 +0,0 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= -github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= -github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= -github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= -github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/lucas-clemente/quic-go v0.29.2 h1:O8Mt0O6LpvEW+wfC40vZdcw0DngwYzoxq5xULZNzSI8= -github.com/lucas-clemente/quic-go v0.29.2/go.mod h1:g6/h9YMmLuU54tL1gW25uIi3VlBp3uv+sBihplIuskE= -github.com/marten-seemann/qtls-go1-18 v0.1.4 h1:ogomB+lWV3Vmwiu6RTwDVTMGx+9j7SEi98e8QB35Its= -github.com/marten-seemann/qtls-go1-18 v0.1.4/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/marten-seemann/qtls-go1-19 v0.1.2 h1:ZevAEqKXH0bZmoOBPiqX2h5rhQ7cbZi+X+rlq2JUbCE= -github.com/marten-seemann/qtls-go1-19 v0.1.2/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= -github.com/miekg/dns v1.1.51 h1:0+Xg7vObnhrz/4ZCZcZh7zPXlmU0aveS2HDBd0m0qSo= -github.com/miekg/dns v1.1.51/go.mod h1:2Z9d3CP1LQWihRZUf29mQ19yDThaI4DAYzte2CaQW5c= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= -github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= -golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From d0861f91e4674cfe36960bec4922c4694584b58f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oto=20=C5=A0=C5=A5=C3=A1va?= Date: Fri, 8 Sep 2023 10:33:29 +0200 Subject: [PATCH 04/10] server: code deduplication --- cmd/echo/main.go | 141 +++--------------------------------------- cmd/proxy/main.go | 147 +++++--------------------------------------- go.mod | 2 +- server/server.go | 152 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 265 deletions(-) create mode 100644 server/server.go diff --git a/cmd/echo/main.go b/cmd/echo/main.go index 5d0f589..ef4fa8c 100644 --- a/cmd/echo/main.go +++ b/cmd/echo/main.go @@ -1,155 +1,32 @@ package main import ( - "context" - "crypto/tls" "flag" "fmt" "io" - "os" - "os/signal" - "sync" - "syscall" "github.com/go-kit/kit/log" quic "github.com/lucas-clemente/quic-go" - "github.com/oklog/run" + "github.com/ns1/doq-proxy/server" ) func main() { - l := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) - l = log.WithPrefix(l, "ts", log.DefaultTimestampUTC) - - var g run.Group - - // proxy code loop - { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - g.Add(func() error { - return loop(l, ctx) - }, func(error) { - cancel() - }) - } - - // signal termination - { - sigterm := make(chan os.Signal, 1) - g.Add(func() error { - signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM) - if sig, ok := <-sigterm; ok { - l.Log("msg", "stopping the proxy", "signal", sig.String()) - } - return nil - }, func(error) { - signal.Stop(sigterm) - close(sigterm) - }) - } - - err := g.Run() - if err != nil { - l.Log("msg", "terminating after error", "err", err) - os.Exit(1) - } + server.Main(genParams, handleStream) } -func loop(l log.Logger, ctx context.Context) error { - var ( - addr string - tlsCert string - tlsKey string - ) +func genParams() server.Params { + var params server.Params - flag.StringVar(&addr, "listen", "127.0.0.1:853", "UDP address to listen on.") - flag.StringVar(&tlsCert, "cert", "cert.pem", "TLS certificate path.") - flag.StringVar(&tlsKey, "key", "key.pem", "TLS key path.") + flag.StringVar(¶ms.Addr, "listen", "127.0.0.1:853", "UDP address to listen on.") + flag.StringVar(¶ms.TlsCert, "cert", "cert.pem", "TLS certificate path.") + flag.StringVar(¶ms.TlsKey, "key", "key.pem", "TLS key path.") flag.Parse() - cert, err := tls.LoadX509KeyPair(tlsCert, tlsKey) - if err != nil { - return fmt.Errorf("load certificate: %w", err) - } - - tls := tls.Config{ - Certificates: []tls.Certificate{cert}, - NextProtos: []string{"doq"}, - } - - listener, err := quic.ListenAddr(addr, &tls, nil) - if err != nil { - return fmt.Errorf("listen: %w", err) - } - defer listener.Close() - - l.Log("msg", "listening for clients", "addr", addr) - - wg := sync.WaitGroup{} - - for { - session, err := listener.Accept(ctx) - if err != nil { - wg.Wait() - return fmt.Errorf("accept connection: %w", err) - } - - l := log.With(l, "client", session.RemoteAddr()) - wg.Add(1) - go func() { - handleClient(l, ctx, session) - wg.Done() - }() - } - -} - -func handleClient(l log.Logger, ctx context.Context, session quic.Connection) { - l.Log("msg", "session accepted") - - var ( - err error - wg sync.WaitGroup = sync.WaitGroup{} - ) - - defer func() { - msg := "" - if err != nil { - msg = err.Error() - } - session.CloseWithError(0, msg) - - l.Log("msg", "session closed") - }() - - for { - stream, err := session.AcceptStream(ctx) - if err != nil { - break - } - - l := log.With(l, "stream_id", stream.StreamID()) - l.Log("msg", "stream accepted") - - wg.Add(1) - go func() { - defer func() { - wg.Done() - l.Log("msg", "stream closed") - }() - - if err := handleStream(l, stream); err != nil { - l.Log("msg", "stream failure", "err", err) - } - }() - } - - wg.Wait() + return params } -func handleStream(l log.Logger, stream quic.Stream) error { +func handleStream(l log.Logger, stream quic.Stream, baton any) error { data := make([]byte, 2048) n, err := stream.Read(data) if err == io.EOF { diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index 73346f8..b7c251d 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -1,9 +1,7 @@ package main import ( - "context" "crypto/rand" - "crypto/tls" "encoding/binary" "errors" "flag" @@ -11,155 +9,40 @@ import ( "io" "net" "os" - "os/signal" - "sync" - "syscall" "time" "github.com/go-kit/kit/log" quic "github.com/lucas-clemente/quic-go" "github.com/miekg/dns" - "github.com/oklog/run" + + server "github.com/ns1/doq-proxy/server" ) func main() { - l := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) - l = log.WithPrefix(l, "ts", log.DefaultTimestampUTC) - - var g run.Group - - // proxy code loop - { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - g.Add(func() error { - return loop(l, ctx) - }, func(error) { - cancel() - }) - } - - // signal termination - { - sigterm := make(chan os.Signal, 1) - g.Add(func() error { - signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM) - if sig, ok := <-sigterm; ok { - l.Log("msg", "stopping the proxy", "signal", sig.String()) - } - return nil - }, func(error) { - signal.Stop(sigterm) - close(sigterm) - }) - } - - err := g.Run() - if err != nil { - l.Log("msg", "terminating after error", "err", err) - os.Exit(1) - } + server.Main(genParams, handleStream) } -func loop(l log.Logger, ctx context.Context) error { - var ( - addr string - tlsCert string - tlsKey string - backend string - ) +func genParams() server.Params { + var params server.Params - flag.StringVar(&addr, "listen", "127.0.0.1:853", "UDP address to listen on.") - flag.StringVar(&tlsCert, "cert", "cert.pem", "TLS certificate path.") - flag.StringVar(&tlsKey, "key", "key.pem", "TLS key path.") + flag.StringVar(¶ms.Addr, "listen", "127.0.0.1:853", "UDP address to listen on.") + flag.StringVar(¶ms.TlsCert, "cert", "cert.pem", "TLS certificate path.") + flag.StringVar(¶ms.TlsKey, "key", "key.pem", "TLS key path.") + + var backend string flag.StringVar(&backend, "backend", "8.8.4.4:53", "IP of backend server.") + params.Baton = backend flag.Parse() - cert, err := tls.LoadX509KeyPair(tlsCert, tlsKey) - if err != nil { - return fmt.Errorf("load certificate: %w", err) - } - - tls := tls.Config{ - Certificates: []tls.Certificate{cert}, - NextProtos: []string{"doq"}, - } - - listener, err := quic.ListenAddr(addr, &tls, nil) - if err != nil { - return fmt.Errorf("listen: %w", err) - } - defer listener.Close() - - l.Log("msg", "listening for clients", "addr", addr) - - wg := sync.WaitGroup{} - - for { - session, err := listener.Accept(ctx) - if err != nil { - wg.Wait() - return fmt.Errorf("accept connection: %w", err) - } - - l := log.With(l, "client", session.RemoteAddr()) - wg.Add(1) - go func() { - handleClient(l, ctx, session, backend) - wg.Done() - }() - } - -} - -func handleClient(l log.Logger, ctx context.Context, session quic.Connection, backend string) { - l.Log("msg", "session accepted") - - var ( - err error - wg sync.WaitGroup = sync.WaitGroup{} - ) - - defer func() { - msg := "" - if err != nil { - msg = err.Error() - } - session.CloseWithError(0, msg) - - l.Log("msg", "session closed") - }() - - for { - stream, err := session.AcceptStream(ctx) - if err != nil { - break - } - - l := log.With(l, "stream_id", stream.StreamID()) - l.Log("msg", "stream accepted") - - wg.Add(1) - go func() { - defer func() { - wg.Done() - l.Log("msg", "stream closed") - }() - - if err := handleStream(stream, backend); err != nil { - l.Log("msg", "stream failure", "err", err) - } - }() - } - - wg.Wait() + return params } -func handleStream(stream quic.Stream, backend string) error { +func handleStream(l log.Logger, stream quic.Stream, data any) error { defer stream.Close() + backend := data.(string) + wireLength := make([]byte, 2) _, err := io.ReadFull(stream, wireLength) if err != nil { diff --git a/go.mod b/go.mod index cfb1e2e..73bad99 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/ns1/doq-proxy -go 1.17 +go 1.18 require ( github.com/go-kit/kit v0.12.0 diff --git a/server/server.go b/server/server.go new file mode 100644 index 0000000..9b76f96 --- /dev/null +++ b/server/server.go @@ -0,0 +1,152 @@ +package server + +import ( + "context" + "crypto/tls" + "fmt" + "sync" + "os" + "os/signal" + "syscall" + + "github.com/go-kit/kit/log" + quic "github.com/lucas-clemente/quic-go" + "github.com/oklog/run" +) + +type Params struct { + Addr string + TlsCert string + TlsKey string + Baton any +} + +type ParamsGenerator func() Params +type StreamHandler func(l log.Logger, stream quic.Stream, baton any) error + +func Main(pg ParamsGenerator, sh StreamHandler) { + l := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) + l = log.WithPrefix(l, "ts", log.DefaultTimestampUTC) + + var g run.Group + + // proxy code loop + { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + g.Add(func() error { + params := pg() + return loop(l, ctx, sh, params.Addr, params.TlsCert, + params.TlsKey, params.Baton) + }, func(error) { + cancel() + }) + } + + // signal termination + { + sigterm := make(chan os.Signal, 1) + g.Add(func() error { + signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM) + if sig, ok := <-sigterm; ok { + l.Log("msg", "stopping the proxy", "signal", sig.String()) + } + return nil + }, func(error) { + signal.Stop(sigterm) + close(sigterm) + }) + } + + err := g.Run() + if err != nil { + l.Log("msg", "terminating after error", "err", err) + os.Exit(1) + } +} + +func loop(l log.Logger, ctx context.Context, sh StreamHandler, + addr string, tlsCert string, tlsKey string, + baton any) error { + + cert, err := tls.LoadX509KeyPair(tlsCert, tlsKey) + if err != nil { + return fmt.Errorf("load certificate: %w", err) + } + + tls := tls.Config{ + Certificates: []tls.Certificate{cert}, + NextProtos: []string{"doq"}, + } + + listener, err := quic.ListenAddr(addr, &tls, nil) + if err != nil { + return fmt.Errorf("listen: %w", err) + } + defer listener.Close() + + l.Log("msg", "listening for clients", "addr", addr) + + wg := sync.WaitGroup{} + + for { + session, err := listener.Accept(ctx) + if err != nil { + wg.Wait() + return fmt.Errorf("accept connection: %w", err) + } + + l := log.With(l, "client", session.RemoteAddr()) + wg.Add(1) + go func() { + handleClient(l, ctx, session, sh, baton) + wg.Done() + }() + } +} + +func handleClient(l log.Logger, ctx context.Context, session quic.Connection, + sh StreamHandler, baton any) { + + l.Log("msg", "session accepted") + + var ( + err error + wg sync.WaitGroup = sync.WaitGroup{} + ) + + defer func() { + msg := "" + if err != nil { + msg = err.Error() + } + session.CloseWithError(0, msg) + + l.Log("msg", "session closed") + }() + + for { + stream, err := session.AcceptStream(ctx) + if err != nil { + break + } + + l := log.With(l, "stream_id", stream.StreamID()) + l.Log("msg", "stream accepted") + + wg.Add(1) + go func() { + defer func() { + wg.Done() + l.Log("msg", "stream closed") + }() + + if err := sh(l, stream, baton); err != nil { + l.Log("msg", "stream failure", "err", err) + } + }() + } + + wg.Wait() +} From c5b094e4f7ca606671f82b00a18be2049271bd55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oto=20=C5=A0=C5=A5=C3=A1va?= Date: Mon, 27 Nov 2023 15:33:46 +0100 Subject: [PATCH 05/10] Update quic-go, enable 0-RTT, more error logging --- cmd/client/main.go | 2 +- cmd/echo/main.go | 4 ++-- cmd/proxy/main.go | 4 ++-- go.mod | 35 ++++++++++++++--------------------- server/server.go | 18 ++++++++++++++---- 5 files changed, 33 insertions(+), 30 deletions(-) diff --git a/cmd/client/main.go b/cmd/client/main.go index de7ccee..453f5ee 100644 --- a/cmd/client/main.go +++ b/cmd/client/main.go @@ -11,7 +11,7 @@ import ( "sync" "time" - "github.com/lucas-clemente/quic-go" + "github.com/quic-go/quic-go" "github.com/miekg/dns" ) diff --git a/cmd/echo/main.go b/cmd/echo/main.go index ef4fa8c..5e3cbd7 100644 --- a/cmd/echo/main.go +++ b/cmd/echo/main.go @@ -5,8 +5,8 @@ import ( "fmt" "io" - "github.com/go-kit/kit/log" - quic "github.com/lucas-clemente/quic-go" + "github.com/go-kit/log" + quic "github.com/quic-go/quic-go" "github.com/ns1/doq-proxy/server" ) diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index b7c251d..5248498 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -11,8 +11,8 @@ import ( "os" "time" - "github.com/go-kit/kit/log" - quic "github.com/lucas-clemente/quic-go" + "github.com/go-kit/log" + quic "github.com/quic-go/quic-go" "github.com/miekg/dns" server "github.com/ns1/doq-proxy/server" diff --git a/go.mod b/go.mod index 73bad99..b2e5c20 100644 --- a/go.mod +++ b/go.mod @@ -3,30 +3,23 @@ module github.com/ns1/doq-proxy go 1.18 require ( - github.com/go-kit/kit v0.12.0 - github.com/lucas-clemente/quic-go v0.29.2 + github.com/go-kit/log v0.2.1 github.com/miekg/dns v1.1.51 github.com/oklog/run v1.1.0 - google.golang.org/protobuf v1.28.0 // indirect + github.com/quic-go/quic-go v0.40.0 ) require ( - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-kit/log v0.2.1 // indirect - github.com/go-logfmt/logfmt v0.6.0 // indirect - github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect - github.com/golang/mock v1.6.0 // indirect - github.com/marten-seemann/qtls-go1-18 v0.1.4 // indirect - github.com/marten-seemann/qtls-go1-19 v0.1.2 // indirect - github.com/nxadm/tail v1.4.8 // indirect - github.com/onsi/ginkgo v1.16.5 // indirect - github.com/stretchr/testify v1.7.0 // indirect - golang.org/x/crypto v0.6.0 // indirect - golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect - golang.org/x/mod v0.8.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/tools v0.6.0 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect - gopkg.in/yaml.v3 v3.0.0 // indirect + github.com/go-logfmt/logfmt v0.5.1 // indirect + github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/onsi/ginkgo/v2 v2.9.5 // indirect + github.com/quic-go/qtls-go1-20 v0.4.1 // indirect + go.uber.org/mock v0.3.0 // indirect + golang.org/x/crypto v0.4.0 // indirect + golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect + golang.org/x/mod v0.11.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/tools v0.9.1 // indirect ) diff --git a/server/server.go b/server/server.go index 9b76f96..d6bf4cb 100644 --- a/server/server.go +++ b/server/server.go @@ -4,13 +4,15 @@ import ( "context" "crypto/tls" "fmt" - "sync" + "math" "os" "os/signal" + "sync" "syscall" + "time" - "github.com/go-kit/kit/log" - quic "github.com/lucas-clemente/quic-go" + "github.com/go-kit/log" + quic "github.com/quic-go/quic-go" "github.com/oklog/run" ) @@ -78,9 +80,16 @@ func loop(l log.Logger, ctx context.Context, sh StreamHandler, tls := tls.Config{ Certificates: []tls.Certificate{cert}, NextProtos: []string{"doq"}, + MinVersion: tls.VersionTLS13, + } + + quic_conf := quic.Config{ + MaxIncomingStreams: math.MaxInt64, + MaxIdleTimeout: 10 * time.Second, + Allow0RTT: true, } - listener, err := quic.ListenAddr(addr, &tls, nil) + listener, err := quic.ListenAddrEarly(addr, &tls, &quic_conf) if err != nil { return fmt.Errorf("listen: %w", err) } @@ -120,6 +129,7 @@ func handleClient(l log.Logger, ctx context.Context, session quic.Connection, msg := "" if err != nil { msg = err.Error() + l.Log("msg", "session failure", "err", err) } session.CloseWithError(0, msg) From fcc76c7ac6917ed456671356179b1bbc78dde277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oto=20=C5=A0=C5=A5=C3=A1va?= Date: Wed, 29 Nov 2023 14:09:32 +0100 Subject: [PATCH 06/10] Restore the limit on maximum concurrent streams --- server/server.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/server.go b/server/server.go index d6bf4cb..948005a 100644 --- a/server/server.go +++ b/server/server.go @@ -4,7 +4,6 @@ import ( "context" "crypto/tls" "fmt" - "math" "os" "os/signal" "sync" @@ -84,7 +83,6 @@ func loop(l log.Logger, ctx context.Context, sh StreamHandler, } quic_conf := quic.Config{ - MaxIncomingStreams: math.MaxInt64, MaxIdleTimeout: 10 * time.Second, Allow0RTT: true, } From e63ec777021602536cc4cf74458e2dab6adba5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oto=20=C5=A0=C5=A5=C3=A1va?= Date: Thu, 7 Dec 2023 15:51:37 +0100 Subject: [PATCH 07/10] echo: QR-bit-setting DNS mode --- cmd/echo/main.go | 87 ++++++++++++++++++++++++++++++++++++++++++----- cmd/proxy/main.go | 12 +++---- 2 files changed, 85 insertions(+), 14 deletions(-) diff --git a/cmd/echo/main.go b/cmd/echo/main.go index 5e3cbd7..7b735ba 100644 --- a/cmd/echo/main.go +++ b/cmd/echo/main.go @@ -1,12 +1,15 @@ package main import ( + "encoding/binary" "flag" "fmt" "io" "github.com/go-kit/log" quic "github.com/quic-go/quic-go" + "github.com/miekg/dns" + "github.com/ns1/doq-proxy/server" ) @@ -16,28 +19,96 @@ func main() { func genParams() server.Params { var params server.Params + var dns bool = true flag.StringVar(¶ms.Addr, "listen", "127.0.0.1:853", "UDP address to listen on.") flag.StringVar(¶ms.TlsCert, "cert", "cert.pem", "TLS certificate path.") flag.StringVar(¶ms.TlsKey, "key", "key.pem", "TLS key path.") + flag.BoolVar(&dns, "dns", true, "If true, validates the traffic as DNS (default).") flag.Parse() + params.Baton = dns + return params } -func handleStream(l log.Logger, stream quic.Stream, baton any) error { - data := make([]byte, 2048) - n, err := stream.Read(data) - if err == io.EOF { - defer stream.Close() - } else if err != nil { - return fmt.Errorf("read query: %w", err) +func handleDnsStream(l log.Logger, stream quic.Stream) error { + defer stream.Close() + + wireLength := make([]byte, 2) + _, err := io.ReadFull(stream, wireLength) + if err != nil { + return fmt.Errorf("read query length: %w", err) + } + + length := binary.BigEndian.Uint16(wireLength) + + wireQuery := make([]byte, length) + _, err = io.ReadFull(stream, wireQuery) + if err != nil { + return fmt.Errorf("read query payload: %w", err) + } + + msg := dns.Msg{} + err = msg.Unpack(wireQuery) + if err != nil { + return fmt.Errorf("could not decode query: %w", err) } - _, err = stream.Write(data[:n]) + if msg.MsgHdr.Response { + l.Log("msg", "QR bit already set") + } + + msg.MsgHdr.Response = true + + bundle := make([]byte, 0) + responseWire, err := msg.Pack() + if err != nil { + return fmt.Errorf("could not encode response: %w", err) + } + + bundle = binary.BigEndian.AppendUint16(bundle, uint16(len(responseWire))) + bundle = append(bundle, responseWire...) + + _, err = stream.Write(bundle) if err != nil { return fmt.Errorf("send response: %w", err) } + return nil } + +func handleDumbStream(l log.Logger, stream quic.Stream) error { + for { + end := false + data := make([]byte, 2048) + n, err := stream.Read(data) + if err == io.EOF { + end = true + } else if err != nil { + return fmt.Errorf("read query: %w", err) + } + + _, err = stream.Write(data[:n]) + if err != nil { + return fmt.Errorf("send response: %w", err) + } + + if end { + stream.Close() + break + } + } + + return nil +} + +func handleStream(l log.Logger, stream quic.Stream, baton any) error { + dns := baton.(bool) + if dns { + return handleDnsStream(l, stream) + } else { + return handleDumbStream(l, stream) + } +} diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index 5248498..2d40b8b 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -15,7 +15,7 @@ import ( quic "github.com/quic-go/quic-go" "github.com/miekg/dns" - server "github.com/ns1/doq-proxy/server" + "github.com/ns1/doq-proxy/server" ) func main() { @@ -24,24 +24,24 @@ func main() { func genParams() server.Params { var params server.Params + var backend string flag.StringVar(¶ms.Addr, "listen", "127.0.0.1:853", "UDP address to listen on.") flag.StringVar(¶ms.TlsCert, "cert", "cert.pem", "TLS certificate path.") flag.StringVar(¶ms.TlsKey, "key", "key.pem", "TLS key path.") - - var backend string flag.StringVar(&backend, "backend", "8.8.4.4:53", "IP of backend server.") - params.Baton = backend flag.Parse() + params.Baton = backend + return params } -func handleStream(l log.Logger, stream quic.Stream, data any) error { +func handleStream(l log.Logger, stream quic.Stream, baton any) error { defer stream.Close() - backend := data.(string) + backend := baton.(string) wireLength := make([]byte, 2) _, err := io.ReadFull(stream, wireLength) From 70f8e5a83b69ff96215e5da2a75e1ca696c6f14c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oto=20=C5=A0=C5=A5=C3=A1va?= Date: Thu, 7 Dec 2023 16:29:08 +0100 Subject: [PATCH 08/10] Refactor and deduplicate server parameters using generics --- cmd/echo/main.go | 21 +++------------- cmd/proxy/main.go | 22 +++-------------- server/server.go | 63 ++++++++++++++++++++++++++++++----------------- 3 files changed, 49 insertions(+), 57 deletions(-) diff --git a/cmd/echo/main.go b/cmd/echo/main.go index 7b735ba..95e2f12 100644 --- a/cmd/echo/main.go +++ b/cmd/echo/main.go @@ -14,23 +14,11 @@ import ( ) func main() { - server.Main(genParams, handleStream) + server.Main(genFlags, handleStream) } -func genParams() server.Params { - var params server.Params - var dns bool = true - - flag.StringVar(¶ms.Addr, "listen", "127.0.0.1:853", "UDP address to listen on.") - flag.StringVar(¶ms.TlsCert, "cert", "cert.pem", "TLS certificate path.") - flag.StringVar(¶ms.TlsKey, "key", "key.pem", "TLS key path.") - flag.BoolVar(&dns, "dns", true, "If true, validates the traffic as DNS (default).") - - flag.Parse() - - params.Baton = dns - - return params +func genFlags(dns *bool) { + flag.BoolVar(dns, "dns", true, "If true, validates the traffic as DNS.") } func handleDnsStream(l log.Logger, stream quic.Stream) error { @@ -104,8 +92,7 @@ func handleDumbStream(l log.Logger, stream quic.Stream) error { return nil } -func handleStream(l log.Logger, stream quic.Stream, baton any) error { - dns := baton.(bool) +func handleStream(l log.Logger, stream quic.Stream, dns bool) error { if dns { return handleDnsStream(l, stream) } else { diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index 2d40b8b..3af83ba 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -19,30 +19,16 @@ import ( ) func main() { - server.Main(genParams, handleStream) + server.Main(genFlags, handleStream) } -func genParams() server.Params { - var params server.Params - var backend string - - flag.StringVar(¶ms.Addr, "listen", "127.0.0.1:853", "UDP address to listen on.") - flag.StringVar(¶ms.TlsCert, "cert", "cert.pem", "TLS certificate path.") - flag.StringVar(¶ms.TlsKey, "key", "key.pem", "TLS key path.") - flag.StringVar(&backend, "backend", "8.8.4.4:53", "IP of backend server.") - - flag.Parse() - - params.Baton = backend - - return params +func genFlags(backend *string) { + flag.StringVar(backend, "backend", "8.8.4.4:53", "IP of backend server.") } -func handleStream(l log.Logger, stream quic.Stream, baton any) error { +func handleStream(l log.Logger, stream quic.Stream, backend string) error { defer stream.Close() - backend := baton.(string) - wireLength := make([]byte, 2) _, err := io.ReadFull(stream, wireLength) if err != nil { diff --git a/server/server.go b/server/server.go index 948005a..ef11dcd 100644 --- a/server/server.go +++ b/server/server.go @@ -3,6 +3,7 @@ package server import ( "context" "crypto/tls" + "flag" "fmt" "os" "os/signal" @@ -15,31 +16,49 @@ import ( "github.com/oklog/run" ) -type Params struct { - Addr string - TlsCert string - TlsKey string - Baton any -} +// Adds specific flags for the server type - e.g. proxy takes a string parameter +// containing the backend address. baton is the memory into which the parameters +// are to be stored - the result is then passed to the corresponding +// StreamHandler. +type FlagsGenerator[T any] func(baton *T) -type ParamsGenerator func() Params -type StreamHandler func(l log.Logger, stream quic.Stream, baton any) error +// Handles data for the QUIC stream. The baton parameter is of a server-specific +// type. +type StreamHandler[T any] func(l log.Logger, stream quic.Stream, baton T) error -func Main(pg ParamsGenerator, sh StreamHandler) { +// Starts the DNS-over-QUIC server. T is the type of parameters for the specific +// server - e.g. proxy has a string parameter containing the backend address. +func Main[T any](flagsGenerator FlagsGenerator[T], sh StreamHandler[T]) { l := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout)) l = log.WithPrefix(l, "ts", log.DefaultTimestampUTC) - var g run.Group + var group run.Group // proxy code loop { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - g.Add(func() error { - params := pg() - return loop(l, ctx, sh, params.Addr, params.TlsCert, - params.TlsKey, params.Baton) + group.Add(func() error { + var ( + addr string + tlsCert string + tlsKey string + baton T + ) + + flag.StringVar(&addr, "listen", + "127.0.0.1:853", "UDP address to listen on.") + flag.StringVar(&tlsCert, "cert", + "cert.pem", "TLS certificate path.") + flag.StringVar(&tlsKey, "key", + "key.pem", "TLS key path.") + if flagsGenerator != nil { + flagsGenerator(&baton) + } + flag.Parse() + + return loop(l, ctx, sh, addr, tlsCert, tlsKey, baton) }, func(error) { cancel() }) @@ -48,7 +67,7 @@ func Main(pg ParamsGenerator, sh StreamHandler) { // signal termination { sigterm := make(chan os.Signal, 1) - g.Add(func() error { + group.Add(func() error { signal.Notify(sigterm, syscall.SIGINT, syscall.SIGTERM) if sig, ok := <-sigterm; ok { l.Log("msg", "stopping the proxy", "signal", sig.String()) @@ -60,16 +79,16 @@ func Main(pg ParamsGenerator, sh StreamHandler) { }) } - err := g.Run() + err := group.Run() if err != nil { l.Log("msg", "terminating after error", "err", err) os.Exit(1) } } -func loop(l log.Logger, ctx context.Context, sh StreamHandler, - addr string, tlsCert string, tlsKey string, - baton any) error { +func loop[T any](l log.Logger, ctx context.Context, sh StreamHandler[T], + addr string, tlsCert string, tlsKey string, + baton T) error { cert, err := tls.LoadX509KeyPair(tlsCert, tlsKey) if err != nil { @@ -113,9 +132,9 @@ func loop(l log.Logger, ctx context.Context, sh StreamHandler, } } -func handleClient(l log.Logger, ctx context.Context, session quic.Connection, - sh StreamHandler, baton any) { - +func handleClient[T any](l log.Logger, ctx context.Context, + session quic.Connection, sh StreamHandler[T], + baton T) { l.Log("msg", "session accepted") var ( From b681397812c0d65764b9fc30ceb10a90dcb01b91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oto=20=C5=A0=C5=A5=C3=A1va?= Date: Thu, 7 Dec 2023 16:51:15 +0100 Subject: [PATCH 09/10] server: add option to specify TLS key log file This allows developers to analyze the traffic using Wireshark, even when it is encrypted. --- server/server.go | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/server/server.go b/server/server.go index ef11dcd..f1eaac2 100644 --- a/server/server.go +++ b/server/server.go @@ -44,21 +44,24 @@ func Main[T any](flagsGenerator FlagsGenerator[T], sh StreamHandler[T]) { addr string tlsCert string tlsKey string + keyLog string baton T ) - flag.StringVar(&addr, "listen", - "127.0.0.1:853", "UDP address to listen on.") - flag.StringVar(&tlsCert, "cert", - "cert.pem", "TLS certificate path.") - flag.StringVar(&tlsKey, "key", - "key.pem", "TLS key path.") + flag.StringVar(&addr, "listen", "127.0.0.1:853", + "UDP address to listen on.") + flag.StringVar(&tlsCert, "cert", "cert.pem", + "TLS certificate path.") + flag.StringVar(&tlsKey, "key", "key.pem", + "TLS key path.") + flag.StringVar(&keyLog, "keylog", "", + "TLS key log file (e.g. for Wireshark analysis) - none if empty") if flagsGenerator != nil { flagsGenerator(&baton) } flag.Parse() - return loop(l, ctx, sh, addr, tlsCert, tlsKey, baton) + return loop(l, ctx, sh, addr, tlsCert, tlsKey, keyLog, baton) }, func(error) { cancel() }) @@ -87,7 +90,7 @@ func Main[T any](flagsGenerator FlagsGenerator[T], sh StreamHandler[T]) { } func loop[T any](l log.Logger, ctx context.Context, sh StreamHandler[T], - addr string, tlsCert string, tlsKey string, + addr string, tlsCert string, tlsKey string, keyLog string, baton T) error { cert, err := tls.LoadX509KeyPair(tlsCert, tlsKey) @@ -101,6 +104,16 @@ func loop[T any](l log.Logger, ctx context.Context, sh StreamHandler[T], MinVersion: tls.VersionTLS13, } + if keyLog != "" { + keyLogFile, err := os.OpenFile(keyLog, os.O_APPEND | os.O_CREATE | os.O_WRONLY, 0755) + if err != nil { + return fmt.Errorf("open keylog file: %w", err) + } + defer keyLogFile.Close() + tls.KeyLogWriter = keyLogFile + } + + quic_conf := quic.Config{ MaxIdleTimeout: 10 * time.Second, Allow0RTT: true, From 2272da06909ac8b04b59819ef669471d3950d2aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oto=20=C5=A0=C5=A5=C3=A1va?= Date: Wed, 13 Dec 2023 11:13:58 +0100 Subject: [PATCH 10/10] server: random stream reset option (opt-in) For testing purposes, a -reset option that randomly resets streams. --- server/server.go | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/server/server.go b/server/server.go index f1eaac2..a2d3db3 100644 --- a/server/server.go +++ b/server/server.go @@ -5,6 +5,8 @@ import ( "crypto/tls" "flag" "fmt" + "math" + "math/rand" "os" "os/signal" "sync" @@ -16,6 +18,8 @@ import ( "github.com/oklog/run" ) +const DoqUnspecifiedError = 0x5 + // Adds specific flags for the server type - e.g. proxy takes a string parameter // containing the backend address. baton is the memory into which the parameters // are to be stored - the result is then passed to the corresponding @@ -45,6 +49,7 @@ func Main[T any](flagsGenerator FlagsGenerator[T], sh StreamHandler[T]) { tlsCert string tlsKey string keyLog string + randomReset float64 baton T ) @@ -56,12 +61,22 @@ func Main[T any](flagsGenerator FlagsGenerator[T], sh StreamHandler[T]) { "TLS key path.") flag.StringVar(&keyLog, "keylog", "", "TLS key log file (e.g. for Wireshark analysis) - none if empty") + flag.Float64Var(&randomReset, "reset", 0.0, + "Float between 0 and 1 determining the chance that a stream will be randomly reset") if flagsGenerator != nil { flagsGenerator(&baton) } flag.Parse() - return loop(l, ctx, sh, addr, tlsCert, tlsKey, keyLog, baton) + if randomReset < 0.0 || randomReset > 1.0 { + return fmt.Errorf("random-reset value %v is not between 0 and 1", + randomReset) + } + + resetThreshold := uint32(randomReset * math.MaxUint32) + + return loop(l, ctx, sh, addr, tlsCert, tlsKey, keyLog, + resetThreshold, baton) }, func(error) { cancel() }) @@ -91,7 +106,7 @@ func Main[T any](flagsGenerator FlagsGenerator[T], sh StreamHandler[T]) { func loop[T any](l log.Logger, ctx context.Context, sh StreamHandler[T], addr string, tlsCert string, tlsKey string, keyLog string, - baton T) error { + resetThreshold uint32, baton T) error { cert, err := tls.LoadX509KeyPair(tlsCert, tlsKey) if err != nil { @@ -139,7 +154,7 @@ func loop[T any](l log.Logger, ctx context.Context, sh StreamHandler[T], l := log.With(l, "client", session.RemoteAddr()) wg.Add(1) go func() { - handleClient(l, ctx, session, sh, baton) + handleClient(l, ctx, session, sh, resetThreshold, baton) wg.Done() }() } @@ -147,7 +162,7 @@ func loop[T any](l log.Logger, ctx context.Context, sh StreamHandler[T], func handleClient[T any](l log.Logger, ctx context.Context, session quic.Connection, sh StreamHandler[T], - baton T) { + resetThreshold uint32, baton T) { l.Log("msg", "session accepted") var ( @@ -182,6 +197,16 @@ func handleClient[T any](l log.Logger, ctx context.Context, l.Log("msg", "stream closed") }() + if resetThreshold > 0 { + r := rand.Uint32() + if r < resetThreshold { + stream.CancelRead(DoqUnspecifiedError) + stream.CancelWrite(DoqUnspecifiedError) + stream.Close() + return + } + } + if err := sh(l, stream, baton); err != nil { l.Log("msg", "stream failure", "err", err) }