322 lines
9.1 KiB
Diff
322 lines
9.1 KiB
Diff
From df0762ce550c33e1cfd423fef95020c41ca770da Mon Sep 17 00:00:00 2001
|
|
From: Irine Sistiana <49315432+IrineSistiana@users.noreply.github.com>
|
|
Date: Fri, 22 Sep 2023 10:39:07 +0800
|
|
Subject: [PATCH 4/9] server: add doq server
|
|
|
|
---
|
|
pkg/server/doq.go | 121 +++++++++++++++++++++++
|
|
plugin/enabled_plugins.go | 20 ++--
|
|
plugin/server/quic_server/quic_server.go | 120 ++++++++++++++++++++++
|
|
3 files changed, 248 insertions(+), 13 deletions(-)
|
|
create mode 100644 pkg/server/doq.go
|
|
create mode 100644 plugin/server/quic_server/quic_server.go
|
|
|
|
diff --git a/pkg/server/doq.go b/pkg/server/doq.go
|
|
new file mode 100644
|
|
index 0000000..8fb5f81
|
|
--- /dev/null
|
|
+++ b/pkg/server/doq.go
|
|
@@ -0,0 +1,121 @@
|
|
+/*
|
|
+ * Copyright (C) 2020-2022, IrineSistiana
|
|
+ *
|
|
+ * This file is part of mosdns.
|
|
+ *
|
|
+ * mosdns is free software: you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation, either version 3 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * mosdns is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
+ */
|
|
+
|
|
+package server
|
|
+
|
|
+import (
|
|
+ "context"
|
|
+ "fmt"
|
|
+ "net"
|
|
+ "net/netip"
|
|
+ "time"
|
|
+
|
|
+ "github.com/IrineSistiana/mosdns/v5/pkg/dnsutils"
|
|
+ "github.com/IrineSistiana/mosdns/v5/pkg/pool"
|
|
+ "github.com/quic-go/quic-go"
|
|
+ "go.uber.org/zap"
|
|
+)
|
|
+
|
|
+const (
|
|
+ defaultQuicIdleTimeout = time.Second * 30
|
|
+ streamReadTimeout = time.Second * 1
|
|
+ quicFirstReadTimeout = time.Millisecond * 500
|
|
+)
|
|
+
|
|
+type DoQServerOpts struct {
|
|
+ Logger *zap.Logger
|
|
+ IdleTimeout time.Duration
|
|
+}
|
|
+
|
|
+// ServeDoQ starts a server at l. It returns if l had an Accept() error.
|
|
+// It always returns a non-nil error.
|
|
+func ServeDoQ(l *quic.Listener, h Handler, opts DoQServerOpts) error {
|
|
+ logger := opts.Logger
|
|
+ if logger == nil {
|
|
+ logger = nopLogger
|
|
+ }
|
|
+ idleTimeout := opts.IdleTimeout
|
|
+ if idleTimeout <= 0 {
|
|
+ idleTimeout = defaultQuicIdleTimeout
|
|
+ }
|
|
+
|
|
+ listenerCtx, cancel := context.WithCancel(context.Background())
|
|
+ defer cancel()
|
|
+ for {
|
|
+ c, err := l.Accept(listenerCtx)
|
|
+ if err != nil {
|
|
+ return fmt.Errorf("unexpected listener err: %w", err)
|
|
+ }
|
|
+
|
|
+ // handle connection
|
|
+ connCtx, cancelConn := context.WithCancel(listenerCtx)
|
|
+ go func() {
|
|
+ defer c.CloseWithError(0, "")
|
|
+ defer cancelConn()
|
|
+
|
|
+ var clientAddr netip.Addr
|
|
+ ta, ok := c.RemoteAddr().(*net.UDPAddr)
|
|
+ if ok {
|
|
+ clientAddr = ta.AddrPort().Addr()
|
|
+ }
|
|
+
|
|
+ firstRead := true
|
|
+ for {
|
|
+ var streamAcceptTimeout time.Duration
|
|
+ if firstRead {
|
|
+ firstRead = false
|
|
+ streamAcceptTimeout = quicFirstReadTimeout
|
|
+ } else {
|
|
+ streamAcceptTimeout = idleTimeout
|
|
+ }
|
|
+ streamAcceptCtx, cancelStreamAccept := context.WithTimeout(connCtx, streamAcceptTimeout)
|
|
+ stream, err := c.AcceptStream(streamAcceptCtx)
|
|
+ cancelStreamAccept()
|
|
+ if err != nil {
|
|
+ return
|
|
+ }
|
|
+
|
|
+ // Handle stream.
|
|
+ // For doq, one stream, one query.
|
|
+ go func() {
|
|
+ defer stream.Close()
|
|
+
|
|
+ // Avoid fragmentation attack.
|
|
+ stream.SetReadDeadline(time.Now().Add(streamReadTimeout))
|
|
+ req, _, err := dnsutils.ReadMsgFromTCP(stream)
|
|
+ if err != nil {
|
|
+ return
|
|
+ }
|
|
+ queryMeta := QueryMeta{
|
|
+ ClientAddr: clientAddr,
|
|
+ ServerName: c.ConnectionState().TLS.ServerName,
|
|
+ }
|
|
+
|
|
+ resp := h.Handle(connCtx, req, queryMeta, pool.PackTCPBuffer)
|
|
+ if resp == nil {
|
|
+ return
|
|
+ }
|
|
+ if _, err := stream.Write(*resp); err != nil {
|
|
+ logger.Warn("failed to write response", zap.Stringer("client", c.RemoteAddr()), zap.Error(err))
|
|
+ }
|
|
+ }()
|
|
+ }
|
|
+ }()
|
|
+ }
|
|
+}
|
|
diff --git a/plugin/enabled_plugins.go b/plugin/enabled_plugins.go
|
|
index 199587c..0f7531b 100644
|
|
--- a/plugin/enabled_plugins.go
|
|
+++ b/plugin/enabled_plugins.go
|
|
@@ -21,12 +21,11 @@ package plugin
|
|
|
|
// data providers
|
|
import (
|
|
+ // data provider
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/data_provider/domain_set"
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/data_provider/ip_set"
|
|
-)
|
|
|
|
-// matches
|
|
-import (
|
|
+ // matcher
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/matcher/client_ip"
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/matcher/cname"
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/matcher/env"
|
|
@@ -39,10 +38,8 @@ import (
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/matcher/random"
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/matcher/rcode"
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/matcher/resp_ip"
|
|
-)
|
|
|
|
-// executables
|
|
-import (
|
|
+ // executable
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/executable/arbitrary"
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/executable/black_hole"
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/executable/cache"
|
|
@@ -62,16 +59,13 @@ import (
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/executable/sequence/fallback"
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/executable/sleep"
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/executable/ttl"
|
|
-)
|
|
|
|
-// other
|
|
-import (
|
|
- _ "github.com/IrineSistiana/mosdns/v5/plugin/mark" // executable and matcher
|
|
-)
|
|
+ // executable and matcher
|
|
+ _ "github.com/IrineSistiana/mosdns/v5/plugin/mark"
|
|
|
|
-// servers
|
|
-import (
|
|
+ // server
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/server/http_server"
|
|
+ _ "github.com/IrineSistiana/mosdns/v5/plugin/server/quic_server"
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/server/tcp_server"
|
|
_ "github.com/IrineSistiana/mosdns/v5/plugin/server/udp_server"
|
|
)
|
|
diff --git a/plugin/server/quic_server/quic_server.go b/plugin/server/quic_server/quic_server.go
|
|
new file mode 100644
|
|
index 0000000..8a5a4c1
|
|
--- /dev/null
|
|
+++ b/plugin/server/quic_server/quic_server.go
|
|
@@ -0,0 +1,120 @@
|
|
+/*
|
|
+ * Copyright (C) 2020-2022, IrineSistiana
|
|
+ *
|
|
+ * This file is part of mosdns.
|
|
+ *
|
|
+ * mosdns is free software: you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation, either version 3 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * mosdns is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU General Public License
|
|
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
+ */
|
|
+
|
|
+package quic_server
|
|
+
|
|
+import (
|
|
+ "crypto/tls"
|
|
+ "errors"
|
|
+ "fmt"
|
|
+ "net"
|
|
+ "time"
|
|
+
|
|
+ "github.com/IrineSistiana/mosdns/v5/coremain"
|
|
+ "github.com/IrineSistiana/mosdns/v5/pkg/server"
|
|
+ "github.com/IrineSistiana/mosdns/v5/pkg/utils"
|
|
+ "github.com/IrineSistiana/mosdns/v5/plugin/server/server_utils"
|
|
+ "github.com/quic-go/quic-go"
|
|
+)
|
|
+
|
|
+const PluginType = "quic_server"
|
|
+
|
|
+func init() {
|
|
+ coremain.RegNewPluginFunc(PluginType, Init, func() any { return new(Args) })
|
|
+}
|
|
+
|
|
+type Args struct {
|
|
+ Entry string `yaml:"entry"`
|
|
+ Listen string `yaml:"listen"`
|
|
+ Cert string `yaml:"cert"`
|
|
+ Key string `yaml:"key"`
|
|
+ IdleTimeout int `yaml:"idle_timeout"`
|
|
+}
|
|
+
|
|
+func (a *Args) init() {
|
|
+ utils.SetDefaultNum(&a.IdleTimeout, 30)
|
|
+}
|
|
+
|
|
+type QuicServer struct {
|
|
+ args *Args
|
|
+
|
|
+ l *quic.Listener
|
|
+}
|
|
+
|
|
+func (s *QuicServer) Close() error {
|
|
+ return s.l.Close()
|
|
+}
|
|
+
|
|
+func Init(bp *coremain.BP, args any) (any, error) {
|
|
+ return StartServer(bp, args.(*Args))
|
|
+}
|
|
+
|
|
+func StartServer(bp *coremain.BP, args *Args) (*QuicServer, error) {
|
|
+ dh, err := server_utils.NewHandler(bp, args.Entry)
|
|
+ if err != nil {
|
|
+ return nil, fmt.Errorf("failed to init dns handler, %w", err)
|
|
+ }
|
|
+
|
|
+ // Init tls
|
|
+ if len(args.Key) == 0 || len(args.Cert) == 0 {
|
|
+ return nil, errors.New("quic server requires a tls certificate")
|
|
+ }
|
|
+ tlsConfig := new(tls.Config)
|
|
+ if err := server.LoadCert(tlsConfig, args.Cert, args.Key); err != nil {
|
|
+ return nil, fmt.Errorf("failed to read tls cert, %w", err)
|
|
+ }
|
|
+ tlsConfig.NextProtos = []string{"doq"}
|
|
+
|
|
+ uc, err := net.ListenPacket("udp", args.Listen)
|
|
+ if err != nil {
|
|
+ return nil, fmt.Errorf("failed to listen socket, %w", err)
|
|
+ }
|
|
+
|
|
+ idleTimeout := time.Duration(args.IdleTimeout) * time.Second
|
|
+
|
|
+ quicConfig := &quic.Config{
|
|
+ MaxIdleTimeout: idleTimeout,
|
|
+ InitialStreamReceiveWindow: 4 * 1024,
|
|
+ MaxStreamReceiveWindow: 4 * 1024,
|
|
+ InitialConnectionReceiveWindow: 8 * 1024,
|
|
+ MaxConnectionReceiveWindow: 16 * 1024,
|
|
+ Allow0RTT: false,
|
|
+ }
|
|
+
|
|
+ qt := &quic.Transport{
|
|
+ Conn: uc,
|
|
+ }
|
|
+
|
|
+ quicListener, err := qt.Listen(tlsConfig, quicConfig)
|
|
+ if err != nil {
|
|
+ qt.Close()
|
|
+ return nil, fmt.Errorf("failed to listen quic, %w", err)
|
|
+ }
|
|
+
|
|
+ go func() {
|
|
+ defer quicListener.Close()
|
|
+ serverOpts := server.DoQServerOpts{Logger: bp.L(), IdleTimeout: idleTimeout}
|
|
+ err := server.ServeDoQ(quicListener, dh, serverOpts)
|
|
+ bp.M().GetSafeClose().SendCloseSignal(err)
|
|
+ }()
|
|
+ return &QuicServer{
|
|
+ args: args,
|
|
+ l: quicListener,
|
|
+ }, nil
|
|
+}
|
|
--
|
|
2.34.8
|
|
|