From 3c4fe105cda6d222bd17f143b7829ffd73dfcd35 Mon Sep 17 00:00:00 2001 From: Aleksei Zhukov Date: Wed, 9 Mar 2022 18:49:53 -0800 Subject: [PATCH 001/171] Proxy protocol support for SMTP and IMAP --- docs/reference/endpoints/imap.md | 19 +++++ docs/reference/endpoints/smtp.md | 15 ++++ go.mod | 1 + go.sum | 2 + internal/endpoint/imap/imap.go | 15 +++- internal/endpoint/smtp/smtp.go | 23 ++++-- internal/proxy_protocol/proxy_protocol.go | 86 ++++++++++++++++++++++ tests/smtp_test.go | 89 +++++++++++++++++++++++ 8 files changed, 238 insertions(+), 12 deletions(-) create mode 100644 internal/proxy_protocol/proxy_protocol.go diff --git a/docs/reference/endpoints/imap.md b/docs/reference/endpoints/imap.md index 06247c32..943291a0 100644 --- a/docs/reference/endpoints/imap.md +++ b/docs/reference/endpoints/imap.md @@ -40,6 +40,25 @@ tls cert.crt key.key { See [TLS configuration / Server](/reference/tls/#server-side) for details. +**Syntax**: proxy_protocol _trusted ips..._ { ... }
+**Default**: not enabled + +Enable use of HAProxy PROXY protocol. Supports both v1 and v2 protocols. +If a list of trusted IP addresses or subnets is provided, only connections +from those will be trusted. + +TLS for the channel between the proxies and maddy can be configured +using a 'tls' directive: +``` +proxy_protocol { + trust 127.0.0.1 ::1 192.168.0.1/24 + tls &proxy_tls +} +``` +Note that the top-level 'tls' directive is not inherited here. If you +need TLS on top of the PROXY protocol, securing the protocol header, +you must declare TLS explicitly. + **Syntax**: io\_debug _boolean_
**Default**: no diff --git a/docs/reference/endpoints/smtp.md b/docs/reference/endpoints/smtp.md index cd99df9b..8849d25f 100644 --- a/docs/reference/endpoints/smtp.md +++ b/docs/reference/endpoints/smtp.md @@ -58,6 +58,21 @@ tls cert.crt key.key { See [TLS configuration / Server](/reference/tls/#server-side) for details. +**Syntax**: proxy_protocol _trusted ips..._ { ... }
+**Default**: not enabled + +Enable use of HAProxy PROXY protocol. Supports both v1 and v2 protocols. +If a list of trusted IP addresses or subnets is provided, only connections +from those will be trusted. + +TLS for the channel between the proxies and maddy can be configured +using a 'tls' directive: +``` +proxy_protocol { + trust 127.0.0.1 ::1 192.168.0.1/24 + tls &proxy_tls +} +``` **Syntax**: io\_debug _boolean_
**Default**: no diff --git a/go.mod b/go.mod index 2b3f9a16..1aaad679 100644 --- a/go.mod +++ b/go.mod @@ -73,6 +73,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.18.3 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/c0va23/go-proxyprotocol v0.9.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/digitalocean/godo v1.96.0 // indirect diff --git a/go.sum b/go.sum index ddf6797a..868d84de 100644 --- a/go.sum +++ b/go.sum @@ -236,6 +236,8 @@ github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLj github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/c0va23/go-proxyprotocol v0.9.1 h1:5BCkp0fDJOhzzH1lhjUgHhmZz9VvRMMif1U2D31hb34= +github.com/c0va23/go-proxyprotocol v0.9.1/go.mod h1:TNjUV+llvk8TvWJxlPYAeAYZgSzT/iicNr3nWBWX320= github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE= github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/internal/endpoint/imap/imap.go b/internal/endpoint/imap/imap.go index c7aac5e8..d047e882 100644 --- a/internal/endpoint/imap/imap.go +++ b/internal/endpoint/imap/imap.go @@ -44,14 +44,16 @@ import ( "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/auth" "github.com/foxcpp/maddy/internal/authz" + "github.com/foxcpp/maddy/internal/proxy_protocol" "github.com/foxcpp/maddy/internal/updatepipe" ) type Endpoint struct { - addrs []string - serv *imapserver.Server - listeners []net.Listener - Store module.Storage + addrs []string + serv *imapserver.Server + listeners []net.Listener + proxyProtocol *proxy_protocol.ProxyProtocol + Store module.Storage tlsConfig *tls.Config listenersWg sync.WaitGroup @@ -90,6 +92,7 @@ func (endp *Endpoint) Init(cfg *config.Map) error { }) cfg.Custom("storage", false, true, nil, modconfig.StorageDirective, &endp.Store) cfg.Custom("tls", true, true, nil, tls2.TLSDirective, &endp.tlsConfig) + cfg.Custom("proxy_protocol", false, false, nil, proxy_protocol.ProxyProtocolDirective, &endp.proxyProtocol) cfg.Bool("insecure_auth", false, false, &insecureAuth) cfg.Bool("io_debug", false, false, &ioDebug) cfg.Bool("io_errors", false, false, &ioErrors) @@ -167,6 +170,10 @@ func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { l = tls.NewListener(l, endp.tlsConfig) } + if endp.proxyProtocol != nil { + l = proxy_protocol.NewListener(l, endp.proxyProtocol, endp.Log) + } + endp.listeners = append(endp.listeners, l) endp.listenersWg.Add(1) diff --git a/internal/endpoint/smtp/smtp.go b/internal/endpoint/smtp/smtp.go index a322b6d4..e01ae57a 100644 --- a/internal/endpoint/smtp/smtp.go +++ b/internal/endpoint/smtp/smtp.go @@ -46,18 +46,20 @@ import ( "github.com/foxcpp/maddy/internal/authz" "github.com/foxcpp/maddy/internal/limits" "github.com/foxcpp/maddy/internal/msgpipeline" + "github.com/foxcpp/maddy/internal/proxy_protocol" "golang.org/x/net/idna" ) type Endpoint struct { - saslAuth auth.SASLAuth - serv *smtp.Server - name string - addrs []string - listeners []net.Listener - pipeline *msgpipeline.MsgPipeline - resolver dns.Resolver - limits *limits.Group + saslAuth auth.SASLAuth + serv *smtp.Server + name string + addrs []string + listeners []net.Listener + proxyProtocol *proxy_protocol.ProxyProtocol + pipeline *msgpipeline.MsgPipeline + resolver dns.Resolver + limits *limits.Group buffer func(r io.Reader) (buffer.Buffer, error) @@ -263,6 +265,7 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { return autoBufferMode(1*1024*1024 /* 1 MiB */, path), nil }, bufferModeDirective, &endp.buffer) cfg.Custom("tls", true, endp.name != "lmtp", nil, tls2.TLSDirective, &endp.serv.TLSConfig) + cfg.Custom("proxy_protocol", false, false, nil, proxy_protocol.ProxyProtocolDirective, &endp.proxyProtocol) cfg.Bool("insecure_auth", endp.name == "lmtp", false, &endp.serv.AllowInsecureAuth) cfg.Int("smtp_max_line_length", false, false, 4000, &endp.serv.MaxLineLength) cfg.Bool("io_debug", false, false, &ioDebug) @@ -350,6 +353,10 @@ func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { l = tls.NewListener(l, endp.serv.TLSConfig) } + if endp.proxyProtocol != nil { + l = proxy_protocol.NewListener(l, endp.proxyProtocol, endp.Log) + } + endp.listeners = append(endp.listeners, l) endp.listenersWg.Add(1) diff --git a/internal/proxy_protocol/proxy_protocol.go b/internal/proxy_protocol/proxy_protocol.go new file mode 100644 index 00000000..1a3a7873 --- /dev/null +++ b/internal/proxy_protocol/proxy_protocol.go @@ -0,0 +1,86 @@ +package proxy_protocol + +import ( + "crypto/tls" + "net" + "strings" + + "github.com/c0va23/go-proxyprotocol" + "github.com/foxcpp/maddy/framework/config" + tls2 "github.com/foxcpp/maddy/framework/config/tls" + "github.com/foxcpp/maddy/framework/log" +) + +type ProxyProtocol struct { + trust []net.IPNet + tlsConfig *tls.Config +} + +func ProxyProtocolDirective(_ *config.Map, node config.Node) (interface{}, error) { + p := ProxyProtocol{} + + childM := config.NewMap(nil, node) + var trustList []string + + childM.StringList("trust", false, false, nil, &trustList) + childM.Custom("tls", true, false, nil, tls2.TLSDirective, &p.tlsConfig) + + if _, err := childM.Process(); err != nil { + return nil, err + } + + if len(node.Args)> 0 { + if trustList == nil { + trustList = make([]string, 0) + } + trustList = append(trustList, node.Args...) + } + + for _, trust := range trustList { + if !strings.Contains(trust, "/") { + trust += "/32" + } + _, ipNet, err := net.ParseCIDR(trust) + if err != nil { + return nil, err + } + p.trust = append(p.trust, *ipNet) + } + + return &p, nil +} + +func NewListener(inner net.Listener, p *ProxyProtocol, logger log.Logger) net.Listener { + var listener net.Listener + + sourceChecker := func(upstream net.Addr) (bool, error) { + if tcpAddr, ok := upstream.(*net.TCPAddr); ok { + if len(p.trust) == 0 { + return true, nil + } + for _, trusted := range p.trust { + if trusted.Contains(tcpAddr.IP) { + return true, nil + } + } + } else if _, ok := upstream.(*net.UnixAddr); ok { + // UNIX local socket connection, always trusted + return true, nil + } + + logger.Printf("proxy_protocol: connection from untrusted source %s", upstream) + return false, nil + } + + listener = proxyprotocol.NewDefaultListener(inner). + WithLogger(proxyprotocol.LoggerFunc(func(format string, v ...interface{}) { + logger.Debugf("proxy_protocol: "+format, v...) + })). + WithSourceChecker(sourceChecker) + + if p.tlsConfig != nil { + listener = tls.NewListener(listener, p.tlsConfig) + } + + return listener +} diff --git a/tests/smtp_test.go b/tests/smtp_test.go index 85a5173a..7e5eda16 100644 --- a/tests/smtp_test.go +++ b/tests/smtp_test.go @@ -23,6 +23,7 @@ package tests_test import ( "errors" + "fmt" "io/ioutil" "path/filepath" "strings" @@ -68,6 +69,94 @@ func TestCheckRequireTLS(tt *testing.T) { conn.ExpectPattern("221 *") } +func TestProxyProtocolTrustedSource(tt *testing.T) { + tt.Parallel() + t := tests.NewT(tt) + t.DNS(map[string]mockdns.Zone{ + "one.maddy.test.": { + TXT: []string{"v=spf1 ip4:127.0.0.17 -all"}, + }, + }) + t.Port("smtp") + t.Config(` + smtp tcp://127.0.0.1:{env:TEST_PORT_smtp} { + hostname mx.maddy.test + tls off + + proxy_protocol { + trust ` + tests.DefaultSourceIP.String() + ` ::1/128 + tls off + } + + defer_sender_reject no + + check { + spf { + enforce_early yes + fail_action reject + } + } + + deliver_to dummy + } + `) + t.Run(1) + defer t.Close() + + conn := t.Conn("smtp") + defer conn.Close() + conn.Writeln(fmt.Sprintf("PROXY TCP4 127.0.0.17 %s 12345 %d", tests.DefaultSourceIP.String(), t.Port("smtp"))) + conn.SMTPNegotation("localhost", nil, nil) + conn.Writeln("MAIL FROM:") + conn.ExpectPattern("250 *") + conn.Writeln("QUIT") + conn.ExpectPattern("221 *") +} + +func TestProxyProtocolUntrustedSource(tt *testing.T) { + tt.Parallel() + t := tests.NewT(tt) + t.DNS(map[string]mockdns.Zone{ + "one.maddy.test.": { + TXT: []string{"v=spf1 ip4:127.0.0.17 -all"}, + }, + }) + t.Port("smtp") + t.Config(` + smtp tcp://127.0.0.1:{env:TEST_PORT_smtp} { + hostname mx.maddy.test + tls off + + proxy_protocol { + trust fe80::bad/128 + tls off + } + + defer_sender_reject no + + check { + spf { + enforce_early yes + fail_action reject + } + } + + deliver_to dummy + } + `) + t.Run(1) + defer t.Close() + + conn := t.Conn("smtp") + defer conn.Close() + conn.Writeln(fmt.Sprintf("PROXY TCP4 127.0.0.17 %s 12345 %d", tests.DefaultSourceIP.String(), t.Port("smtp"))) + conn.SMTPNegotation("localhost", nil, nil) + conn.Writeln("MAIL FROM:") + conn.ExpectPattern("550 *") + conn.Writeln("QUIT") + conn.ExpectPattern("221 *") +} + func TestCheckSPF(tt *testing.T) { tt.Parallel() t := tests.NewT(tt) From 6401870244333537a396fee20abe90cc1efc0861 Mon Sep 17 00:00:00 2001 From: Litrop Date: 2023年4月29日 10:12:39 +0000 Subject: [PATCH 002/171] ServerName is also used by StartTLS. --- internal/auth/ldap/ldap.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/auth/ldap/ldap.go b/internal/auth/ldap/ldap.go index acf5683d..04cfe9f9 100644 --- a/internal/auth/ldap/ldap.go +++ b/internal/auth/ldap/ldap.go @@ -147,8 +147,8 @@ func (a *Auth) newConn() (*ldap.Conn, error) { return nil, fmt.Errorf("auth.ldap: invalid server URL: %w", err) } hostname := parsedURL.Host + a.tlsCfg.ServerName = strings.Split(hostname, ":")[0] tlsCfg = a.tlsCfg.Clone() - a.tlsCfg.ServerName = hostname conn, err = ldap.DialURL(u, ldap.DialWithDialer(a.dialer), ldap.DialWithTLSConfig(tlsCfg)) if err != nil { From 681976cc7bd3f20d43f4adaad44ea36842a48906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: 2023年5月23日 09:32:51 +0300 Subject: [PATCH 003/171] Fix typos --- contrib/kubernetes/chart/README.md | 6 +++--- dist/README.md | 2 +- docs/internals/unicode.md | 2 +- docs/multiple-domains.md | 4 ++-- docs/reference/auth/netauth.md | 2 +- docs/reference/checks/command.md | 2 +- docs/reference/checks/dkim.md | 2 +- docs/reference/checks/dnsbl.md | 2 +- docs/reference/checks/misc.md | 4 ++-- docs/reference/checks/spf.md | 12 ++++++------ docs/reference/endpoints/imap.md | 2 +- docs/reference/endpoints/smtp.md | 8 ++++---- docs/reference/modifiers/dkim.md | 2 +- docs/reference/storage/imap-filters.md | 4 ++-- docs/reference/targets/queue.md | 6 +++--- docs/reference/targets/remote.md | 6 +++--- docs/seclevels.md | 10 +++++----- docs/third-party/dovecot.md | 4 ++-- docs/third-party/mailman3.md | 2 +- docs/third-party/smtp-servers.md | 2 +- docs/tutorials/alias-to-remote.md | 2 +- internal/README.md | 2 +- 22 files changed, 44 insertions(+), 44 deletions(-) diff --git a/contrib/kubernetes/chart/README.md b/contrib/kubernetes/chart/README.md index 58d175fe..14e2e74b 100644 --- a/contrib/kubernetes/chart/README.md +++ b/contrib/kubernetes/chart/README.md @@ -9,7 +9,7 @@ load balancer in front of the nodes. ## Requirement -In order to run maddy properly, you need to have TLS secret undet name maddy present in the cluster. If you have commercial +In order to run maddy properly, you need to have TLS secret under name maddy present in the cluster. If you have commercial certificate, you can create it by the following command: ```sh @@ -20,9 +20,9 @@ If you use cert-manager, just create the secret under name maddy. ## Replication -Default for this chart is 1 replica of maddy. If you try to increse this, you will probably get an error because of +Default for this chart is 1 replica of maddy. If you try to increase this, you will probably get an error because of the busy ports 25, 143, 587, etc. We do not support this feature at the moment, so please use just 1 replica. Like said -at the begining of this document, multiple replicas would probably require to switch do DaemonSet which would further require +at the beginning of this document, multiple replicas would probably require to switch do DaemonSet which would further require to have TCP load balancer and shared storage between all replicas. This is not supported by this chart, sorry. This chart is used on one node cluster and then installation is straight forward, like described bellow, but if you have multiple node cluster, please use taints and tolerations to select the desired node. This chart supports tolerations to diff --git a/dist/README.md b/dist/README.md index 60e1cff9..d057f0ef 100644 --- a/dist/README.md +++ b/dist/README.md @@ -22,7 +22,7 @@ Additionally, unit files apply strict sandboxing, limiting maddy permissions on the system to a bare minimum. Subset of these options makes it impossible for privileged authentication helper binaries to gain required permissions, so you may have to disable it when using system account-based authentication with -maddy running as a unprivilieged user. +maddy running as a unprivileged user. ## fail2ban configuration diff --git a/docs/internals/unicode.md b/docs/internals/unicode.md index 1a7e2a20..21ef8b05 100644 --- a/docs/internals/unicode.md +++ b/docs/internals/unicode.md @@ -93,4 +93,4 @@ mentioned above). Clients that want to implement proper handling for Unicode strings may assume maddy does not handle them properly in e.g. SEARCH commands and so such clients -may download messsages and process them locally. +may download messages and process them locally. diff --git a/docs/multiple-domains.md b/docs/multiple-domains.md index 6d7d07b2..f910213f 100644 --- a/docs/multiple-domains.md +++ b/docs/multiple-domains.md @@ -28,7 +28,7 @@ the [introduction tutorial](tutorials/setting-up.md). Also note that you do not really need a separate TLS certificate for each managed domain. You can have one hostname e.g. mail.example.org set as an MX -record for mulitple domains. +record for multiple domains. **If you want multiple domains to share username namespace**, you should change several more options. @@ -53,7 +53,7 @@ maddy imap-acct create user@example.com "user"**, you can set `storage_map` in IMAP endpoint and `delivery_map` in storage backend to use `email_locapart`: ``` -straoge.imapsql local_mailboxes { +storage.imapsql local_mailboxes { ... delivery_map email_localpart # deliver "user@*" to "user" } diff --git a/docs/reference/auth/netauth.md b/docs/reference/auth/netauth.md index 84458e43..074b74c8 100644 --- a/docs/reference/auth/netauth.md +++ b/docs/reference/auth/netauth.md @@ -37,7 +37,7 @@ auth.netauth {} OPTIONAL. -Group that entities must posess to be able to use maddy services. +Group that entities must possess to be able to use maddy services. This can be used to provide email to just a subset of the entities present in NetAuth. diff --git a/docs/reference/checks/command.md b/docs/reference/checks/command.md index 909b7b07..3cbb4892 100644 --- a/docs/reference/checks/command.md +++ b/docs/reference/checks/command.md @@ -127,5 +127,5 @@ the message pipeline action. Two codes are defined implicitly, exit code 1 causes the message to be rejected with a permanent error, exit code 2 causes the message to be quarantined. Both -action can be overriden using the 'code' directive. +action can be overridden using the 'code' directive. diff --git a/docs/reference/checks/dkim.md b/docs/reference/checks/dkim.md index cd3ff896..5aa2dc34 100644 --- a/docs/reference/checks/dkim.md +++ b/docs/reference/checks/dkim.md @@ -19,7 +19,7 @@ check.dkim { **Syntax**: debug _boolean_
**Default**: global directive value -Log both successfull and unsuccessful check executions instead of just +Log both successful and unsuccessful check executions instead of just unsuccessful. **Syntax**: required\_fields _string..._
diff --git a/docs/reference/checks/dnsbl.md b/docs/reference/checks/dnsbl.md index 74cf361b..bb3615b9 100644 --- a/docs/reference/checks/dnsbl.md +++ b/docs/reference/checks/dnsbl.md @@ -4,7 +4,7 @@ The check.dnsbl module implements checking of source IP and hostnames against a of DNS-based Blackhole lists (DNSBLs). Its configuration consists of module configuration directives and a set -of blocks specifing lists to use and kind of lookups to perform on them. +of blocks specifying lists to use and kind of lookups to perform on them. ``` check.dnsbl { diff --git a/docs/reference/checks/misc.md b/docs/reference/checks/misc.md index ac520b80..25e1ff63 100644 --- a/docs/reference/checks/misc.md +++ b/docs/reference/checks/misc.md @@ -15,8 +15,8 @@ Action to take when check fails. See Check actions for details. **Syntax**: debug _boolean_
**Default**: global directive value -Log both sucessfull and unsucessfull check executions instead of just -unsucessfull. +Log both successful and unsuccessful check executions instead of just +unsuccessful. ## require\_mx\_record diff --git a/docs/reference/checks/spf.md b/docs/reference/checks/spf.md index ebc71afd..83bc81b0 100644 --- a/docs/reference/checks/spf.md +++ b/docs/reference/checks/spf.md @@ -46,7 +46,7 @@ Enable verbose logging for check.spf. Make policy decision on MAIL FROM stage (before the message body is received). This makes it impossible to apply DMARC override (see above). -**Syntax**: none\_action reject|qurantine|ignore
+**Syntax**: none\_action reject|quarantine|ignore
**Default**: ignore Action to take when SPF policy evaluates to a 'none' result. @@ -54,7 +54,7 @@ Action to take when SPF policy evaluates to a 'none' result. See [https://tools.ietf.org/html/rfc7208#section-2.6](https://tools.ietf.org/html/rfc7208#section-2.6) for meaning of SPF results. -**Syntax**: neutral\_action reject|qurantine|ignore
+**Syntax**: neutral\_action reject|quarantine|ignore
**Default**: ignore Action to take when SPF policy evaluates to a 'neutral' result. @@ -62,22 +62,22 @@ Action to take when SPF policy evaluates to a 'neutral' result. See [https://tools.ietf.org/html/rfc7208#section-2.6](https://tools.ietf.org/html/rfc7208#section-2.6) for meaning of SPF results. -**Syntax**: fail\_action reject|qurantine|ignore
+**Syntax**: fail\_action reject|quarantine|ignore
**Default**: quarantine Action to take when SPF policy evaluates to a 'fail' result. -**Syntax**: softfail\_action reject|qurantine|ignore
+**Syntax**: softfail\_action reject|quarantine|ignore
**Default**: ignore Action to take when SPF policy evaluates to a 'softfail' result. -**Syntax**: permerr\_action reject|qurantine|ignore
+**Syntax**: permerr\_action reject|quarantine|ignore
**Default**: reject Action to take when SPF policy evaluates to a 'permerror' result. -**Syntax**: temperr\_action reject|qurantine|ignore
+**Syntax**: temperr\_action reject|quarantine|ignore
**Default**: reject Action to take when SPF policy evaluates to a 'temperror' result. diff --git a/docs/reference/endpoints/imap.md b/docs/reference/endpoints/imap.md index 06247c32..41e4f2f3 100644 --- a/docs/reference/endpoints/imap.md +++ b/docs/reference/endpoints/imap.md @@ -31,7 +31,7 @@ imap tcp://0.0.0.0:143 tls://0.0.0.0:993 { **Default**: global directive value TLS certificate & key to use. Fine-tuning of other TLS properties is possible -by specifing a configuration block and options inside it: +by specifying a configuration block and options inside it: ``` tls cert.crt key.key { protocols tls1.2 tls1.3 diff --git a/docs/reference/endpoints/smtp.md b/docs/reference/endpoints/smtp.md index cd99df9b..6ddbba1f 100644 --- a/docs/reference/endpoints/smtp.md +++ b/docs/reference/endpoints/smtp.md @@ -49,7 +49,7 @@ Server name to use in SMTP banner. **Default**: global directive value TLS certificate & key to use. Fine-tuning of other TLS properties is possible -by specifing a configuration block and options inside it: +by specifying a configuration block and options inside it: ``` tls cert.crt key.key { protocols tls1.2 tls1.3 @@ -111,7 +111,7 @@ clients that don't expect an error early in session. **Default**: 5 Amount of RCPT-time errors that should be logged. Further errors will be -handled silently. This is to prevent log flooding during email dictonary +handled silently. This is to prevent log flooding during email dictionary attacks (address probing). **Syntax**: max\_received _integer_
@@ -202,7 +202,7 @@ for all messages ("all"), per-sender IP ("ip"), per-sender domain ("source") or per-recipient domain ("destination"). Having a scope other than "all" means that the restriction will be enforced independently for each group determined by scope. E.g. "ip rate 20" means that the same IP cannot send more than 20 -messages in a scond. "destination concurrency 5" means that no more than 5 +messages per second. "destination concurrency 5" means that no more than 5 messages can be sent in parallel to a single domain. **Note**: At the moment, SMTP endpoint on its own does not support per-recipient @@ -233,7 +233,7 @@ messages can enter the server through both endpoints in one second. # Submission module (submission) Module 'submission' implements all functionality of the 'smtp' module and adds -certain message preprocessing on top of it, additionaly authentication is +certain message preprocessing on top of it, additionally authentication is always required. 'submission' module checks whether addresses in header fields From, Sender, To, diff --git a/docs/reference/modifiers/dkim.md b/docs/reference/modifiers/dkim.md index a61f783b..44e212ee 100644 --- a/docs/reference/modifiers/dkim.md +++ b/docs/reference/modifiers/dkim.md @@ -195,5 +195,5 @@ require\_sender\_match checks. Only first address will be checked, however. Sign emails from subdomains using a top domain key. -Allows only one domain to be specified (can be workarounded using modify.dkim +Allows only one domain to be specified (can be worked around by using modify.dkim multiple times). diff --git a/docs/reference/storage/imap-filters.md b/docs/reference/storage/imap-filters.md index 3ddc7ef4..b125a07e 100644 --- a/docs/reference/storage/imap-filters.md +++ b/docs/reference/storage/imap-filters.md @@ -6,7 +6,7 @@ modifying IMAP-specific message attributes. In particular, it allows code to change target folder and add IMAP flags (keywords) to the message. There is no way to reject message using IMAP filters, this should be done -eariler in SMTP pipeline logic. Quarantined messages are not processed +earlier in SMTP pipeline logic. Quarantined messages are not processed by IMAP filters and are unconditionally delivered to Junk folder (or other folder with \Junk special-use attribute). @@ -44,7 +44,7 @@ access to the SMTP envelope recipient (before and after any rewrites), Note that if you use provided systemd units on Linux, maddy executable is sandboxed - all commands will be executed with heavily restricted filesystem -acccess and other privileges. Notably, /tmp is isolated and all directories +access and other privileges. Notably, /tmp is isolated and all directories except for /var/lib/maddy and /run/maddy are read-only. You will need to modify systemd unit if your command needs more privileges. diff --git a/docs/reference/targets/queue.md b/docs/reference/targets/queue.md index c4e5beb6..cc25db1a 100644 --- a/docs/reference/targets/queue.md +++ b/docs/reference/targets/queue.md @@ -56,9 +56,9 @@ limits amount of messages tried to be delivered concurrently. **Default**: 20 Attempt delivery up to _integer_ times. Note that no more attempts will be done -is permanent error occured during previous attempt. +is permanent error occurred during previous attempt. -Delay before the next attempt will be increased exponentally using the +Delay before the next attempt will be increased exponentially using the following formula: 15mins \* 1.2 ^ (n - 1) where n is the attempt number. This gives you approximately the following sequence of delays: 18mins, 21mins, 25mins, 31mins, 37mins, 44mins, 53mins, 64mins, ... @@ -67,7 +67,7 @@ This gives you approximately the following sequence of delays: **Default**: not specified This configuration contains pipeline configuration to be used for generated DSN -(Delivery Status Notifiaction) messages. +(Delivery Status Notification) messages. If this is block is not present in configuration, DSNs will not be generated. Note, however, this is not what you want most of the time. diff --git a/docs/reference/targets/remote.md b/docs/reference/targets/remote.md index dae7b8da..f69a6de2 100644 --- a/docs/reference/targets/remote.md +++ b/docs/reference/targets/remote.md @@ -138,7 +138,7 @@ mtasts { ``` If the mx\_auth directive is not specified, no mechanisms are enabled. Note -that, however, this makes outbound SMTP vulnerable to a numberous downgrade +that, however, this makes outbound SMTP vulnerable to a numerous downgrade attacks and hence not recommended. It is possible to share the same set of policies for multiple 'remote' module @@ -201,9 +201,9 @@ Filesystem directory to use for policies caching if 'cache' is set to 'fs'. Checks whether MX records are signed. Sets MX level to "dnssec" is they are. -maddy does not validate DNSSEC signatures on its own. Instead it reslies on +maddy does not validate DNSSEC signatures on its own. Instead it relies on the upstream resolver to do so by causing lookup to fail when verification -fails and setting the AD flag for signed and verfified zones. As a safety +fails and setting the AD flag for signed and verified zones. As a safety measure, if the resolver is not 127.0.0.1 or ::1, the AD flag is ignored. DNSSEC is currently not supported on Windows and other platforms that do not diff --git a/docs/seclevels.md b/docs/seclevels.md index 94e1a829..984be4a4 100644 --- a/docs/seclevels.md +++ b/docs/seclevels.md @@ -45,7 +45,7 @@ maddy defines two values indicating how "secure" delivery of message will be: - TLS security level These values correspond to the problems described above. On delivery, the -estabilished connection to the remote server is "ranked" using these values and +established connection to the remote server is "ranked" using these values and then they are compared against a number of policies (including local configuration). If the effective value is lower than the required one, the connection is closed and next candidate server is used. If all connections fail @@ -67,14 +67,14 @@ attacks - MX level: None. MX candidate was returned as a result of DNS lookup for the recipient domain, no additional checks done. - MX level: MTA-STS. Used MX matches the MTA-STS policy published by the - recepient domain (even one in testing mode). + recipient domain (even one in testing mode). - MX level: DNSSEC. MX record is signed. -- TLS level: None. Plaintext connection was estabilished, TLS is not available +- TLS level: None. Plaintext connection was established, TLS is not available or failed. -- TLS level: Encrypted. TLS connection was estabilished, the server certificate +- TLS level: Encrypted. TLS connection was established, the server certificate failed X.509 and DANE verification. -- TLS level: Authenticated. TLS connection was estabilished, the server +- TLS level: Authenticated. TLS connection was established, the server certificate passes X.509 **or** DANE verification. **Note 1:** Persistent attacker able to control network connection can diff --git a/docs/third-party/dovecot.md b/docs/third-party/dovecot.md index c922f577..22d51c32 100644 --- a/docs/third-party/dovecot.md +++ b/docs/third-party/dovecot.md @@ -1,7 +1,7 @@ # Dovecot Builtin maddy IMAP server may not match your requirements in terms of -performance, reliabilty or anything. For this reason it is possible to +performance, reliability or anything. For this reason it is possible to integrate it with any external IMAP server that implements necessary protocols. Here is how to do it for Dovecot. @@ -69,7 +69,7 @@ smtp tcp://127.0.0.1:587 { deliver_to &remote_queue } ``` -And configure IMAP servers's Submission service to forward outbound messages +And configure IMAP server's Submission service to forward outbound messages there. Depending on how Submission service is implemented you may also need to route diff --git a/docs/third-party/mailman3.md b/docs/third-party/mailman3.md index 27f63677..a29d71e1 100644 --- a/docs/third-party/mailman3.md +++ b/docs/third-party/mailman3.md @@ -20,7 +20,7 @@ lmtp_port: 8024 After that, you will need to configure maddy to send messages to Mailman. -The preferrable way of doing so is destination_in and table.regexp: +The preferable way of doing so is destination_in and table.regexp: ``` msgpipeline local_routing { destination_in regexp "first-mailinglist(-(bounces\+.*|confirm\+.*|join|leave|owner|request|subscribe|unsubscribe))?@lists.example.org" { diff --git a/docs/third-party/smtp-servers.md b/docs/third-party/smtp-servers.md index 813dcf1f..599a00d6 100644 --- a/docs/third-party/smtp-servers.md +++ b/docs/third-party/smtp-servers.md @@ -43,7 +43,7 @@ lmtp unix:/run/maddy/lmtp.sock { Look up documentation for your SMTP server on how to make it send messages using LMTP to /run/maddy/lmtp.sock. -To handle authentiation for Submission (client-server SMTP) SMTP server +To handle authentication for Submission (client-server SMTP) SMTP server needs to access credentials database used by maddy. maddy implements server side of Dovecot authentication protocol so you can use it if SMTP server implements "Dovecot SASL" client. diff --git a/docs/tutorials/alias-to-remote.md b/docs/tutorials/alias-to-remote.md index 0b0601b5..ddbc76b9 100644 --- a/docs/tutorials/alias-to-remote.md +++ b/docs/tutorials/alias-to-remote.md @@ -88,7 +88,7 @@ msgpipeline local_routing { ## Bounce handling Once the message is delivered to `remote_queue`, it will follow the usual path -for outbound delivery, including queueing and multiple attempts. This also +for outbound delivery, including queuing and multiple attempts. This also means bounce messages will be generated on failures. When accepting messages from arbitrary senders via the 25 port, the DSN recipient will be whatever sender specifies in the MAIL FROM command. This is prone to [collateral spam] diff --git a/internal/README.md b/internal/README.md index 882eed44..da5d57c9 100644 --- a/internal/README.md +++ b/internal/README.md @@ -4,7 +4,7 @@ maddy source tree Main maddy code base lives here. No packages are intended to be used in third-party software hence API is not stable. -Subdirectories are organised as follows: +Subdirectories are organized as follows: ``` / auxiliary libraries From 420e85ce96be5e9cc1518e1b32fb1e5b03eaa5a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: 2023年5月24日 08:20:12 +0300 Subject: [PATCH 004/171] Fix precise_casefold -> precis_casefold and .md formatting --- docs/reference/checks/authorize_sender.md | 3 ++- docs/reference/global-config.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/reference/checks/authorize_sender.md b/docs/reference/checks/authorize_sender.md index 9999a356..b65ea2d0 100644 --- a/docs/reference/checks/authorize_sender.md +++ b/docs/reference/checks/authorize_sender.md @@ -94,7 +94,8 @@ Normalization function to apply to authorization username before further processing. Available options: -- `auto` `precis_casefold_email` for valid emails, `precise_casefold` otherwise. + +- `auto` `precis_casefold_email` for valid emails, `precis_casefold` otherwise. - `precis_casefold_email` PRECIS UsernameCaseMapped profile + U-labels form for domain - `precis_casefold` PRECIS UsernameCaseMapped profile for the entire string - `precis_email` PRECIS UsernameCasePreserved profile + U-labels form for domain diff --git a/docs/reference/global-config.md b/docs/reference/global-config.md index faf24410..db477bf5 100644 --- a/docs/reference/global-config.md +++ b/docs/reference/global-config.md @@ -65,7 +65,8 @@ Normalization function to apply to SASL usernames before mapping them to storage accounts. Available options: -- `auto` `precis_casefold_email` for valid emails, `precise_casefold` otherwise. + +- `auto` `precis_casefold_email` for valid emails, `precis_casefold` otherwise. - `precis_casefold_email` PRECIS UsernameCaseMapped profile + U-labels form for domain - `precis_casefold` PRECIS UsernameCaseMapped profile for the entire string - `precis_email` PRECIS UsernameCasePreserved profile + U-labels form for domain From de756c8dc52b69efa511c8340aa29af76c407f93 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2023年5月29日 22:19:01 +0300 Subject: [PATCH 005/171] tls/acme: Add support for DNS-01 domain delegation See #588. --- docs/reference/tls-acme.md | 17 ++++++++++++++++- internal/tls/acme/acme.go | 24 ++++++++++++++---------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/docs/reference/tls-acme.md b/docs/reference/tls-acme.md index 891795ec..3dc803a5 100644 --- a/docs/reference/tls-acme.md +++ b/docs/reference/tls-acme.md @@ -20,7 +20,13 @@ smtp tcp://127.0.0.1:25 { You can also use a global `tls` directive to use automatically obtained certificates for all endpoints: ``` -tls &local_tls +tls { + loader acme { + email maddy-acme@example.org + agreed + challenge dns-01 + } +} ``` Currently the only supported challenge is dns-01 one therefore @@ -87,6 +93,15 @@ back to the one configured via 'ca' option. This avoids rate limit issues with production CA. +**Syntax:** override\_domain _domain_
+**Default:** not set + +Override the domain to set the TXT record on for DNS-01 challenge. +This is to delegate the challenge to a different domain. + +See https://www.eff.org/deeplinks/2018/02/technical-deep-dive-securing-automation-acme-dns-challenge-validation +for explanation why this might be useful. + **Syntax:** email _str_
**Default:** not set diff --git a/internal/tls/acme/acme.go b/internal/tls/acme/acme.go index 96c4a0f7..70eb05b8 100644 --- a/internal/tls/acme/acme.go +++ b/internal/tls/acme/acme.go @@ -39,15 +39,16 @@ func New(_, instName string, _, inlineArgs []string) (module.Module, error) { func (l *Loader) Init(cfg *config.Map) error { var ( - hostname string - extraNames []string - storePath string - caPath string - testCAPath string - email string - agreed bool - challenge string - provider certmagic.ACMEDNSProvider + hostname string + extraNames []string + storePath string + caPath string + testCAPath string + email string + agreed bool + challenge string + overrideDomain string + provider certmagic.ACMEDNSProvider ) cfg.Bool("debug", true, false, &l.log.Debug) cfg.String("hostname", true, true, "", &hostname) @@ -60,6 +61,8 @@ func (l *Loader) Init(cfg *config.Map) error { certmagic.LetsEncryptStagingCA, &testCAPath) cfg.String("email", false, false, "", &email) + cfg.String("override_domain", false, false, + "", &overrideDomain) cfg.Bool("agreed", false, false, &agreed) cfg.Enum("challenge", false, true, []string{"dns-01"}, "dns-01", &challenge) @@ -107,7 +110,8 @@ func (l *Loader) Init(cfg *config.Map) error { return fmt.Errorf("tls.loader.acme: dns-01 challenge requires a configured DNS provider") } mngr.DNS01Solver = &certmagic.DNS01Solver{ - DNSProvider: provider, + DNSProvider: provider, + OverrideDomain: overrideDomain, } default: return fmt.Errorf("tls.loader.acme: challenge not supported") From b5aa5931ebd9b6474aabf380d0aec54e56e50087 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2023年6月27日 19:08:20 +0300 Subject: [PATCH 006/171] cfgparser: Do not interpret absolute paths relatively to the config dir See #592. --- framework/cfgparser/imports.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/framework/cfgparser/imports.go b/framework/cfgparser/imports.go index 97f11ac9..d8276d22 100644 --- a/framework/cfgparser/imports.go +++ b/framework/cfgparser/imports.go @@ -79,7 +79,10 @@ func (ctx *parseContext) resolveImport(node Node, name string, expansionDepth in return subtree, nil } - file := filepath.Join(filepath.Dir(ctx.fileLocation), name) + file := name + if !filepath.IsAbs(name) { + file = filepath.Join(filepath.Dir(ctx.fileLocation), name) + } src, err := os.Open(file) if err != nil { if os.IsNotExist(err) { From 448aa07402ca6a81203df0c1a87127f8574c449c Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2023年6月27日 19:09:33 +0300 Subject: [PATCH 007/171] Update all version requirements to Go 1.19 0.7.0 actually requires 1.19. --- Dockerfile | 2 +- docs/tutorials/building-from-source.md | 8 +- go.mod | 115 ++++++++++---------- go.sum | 142 +++++++++++++++++++++++++ 4 files changed, 206 insertions(+), 61 deletions(-) diff --git a/Dockerfile b/Dockerfile index 08ad58e4..1fe12901 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.18-alpine AS build-env +FROM golang:1.19-alpine AS build-env RUN set -ex && \ apk upgrade --no-cache --available && \ diff --git a/docs/tutorials/building-from-source.md b/docs/tutorials/building-from-source.md index 4f06d5ba..3b3d9291 100644 --- a/docs/tutorials/building-from-source.md +++ b/docs/tutorials/building-from-source.md @@ -6,7 +6,7 @@ You need C toolchain, Go toolchain and Make: On Debian-based system this should work: ``` -apt-get install golang-1.18 gcc libc6-dev make +apt-get install golang-1.19 gcc libc6-dev make ``` Additionally, if you want manual pages, you should also have scdoc installed. @@ -20,8 +20,8 @@ available in some distributions (*cough* Debian *cough*). It should not be hard to grab a recent built toolchain from golang.org: ``` -wget "https://dl.google.com/go/go1.18.9.linux-amd64.tar.gz" -tar xf "go1.18.19.linux-amd64.tar.gz" +wget "https://dl.google.com/go/go1.19.9.linux-amd64.tar.gz" +tar xf "go1.19.19.linux-amd64.tar.gz" export GOROOT="$PWD/go" export PATH="$PWD/go/bin:$PATH" ``` @@ -36,7 +36,7 @@ $ cd maddy 3. Select the appropriate version to build: ``` -$ git checkout v0.6.0 # a specific release +$ git checkout v0.7.0 # a specific release $ git checkout master # next bugfix release $ git checkout dev # next feature release ``` diff --git a/go.mod b/go.mod index 2b3f9a16..61d22fbc 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module github.com/foxcpp/maddy -go 1.18 +go 1.19 require ( blitiri.com.ar/go/spf v1.5.1 - github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 + github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 github.com/caddyserver/certmagic v0.17.2 github.com/emersion/go-imap v1.2.2-0.20220928192137-6fac715be9cf github.com/emersion/go-imap-compress v0.0.0-20201103190257-14809af1d1b9 @@ -23,9 +23,9 @@ require ( github.com/foxcpp/go-mockdns v1.0.0 github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8 github.com/go-ldap/ldap/v3 v3.4.4 - github.com/go-sql-driver/mysql v1.7.0 + github.com/go-sql-driver/mysql v1.7.1 github.com/google/uuid v1.3.0 - github.com/hashicorp/go-hclog v1.4.0 + github.com/hashicorp/go-hclog v1.5.0 github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c github.com/lib/pq v1.10.6 github.com/libdns/alidns v1.0.3-0.20220501125541-4a895238a95d @@ -35,110 +35,113 @@ require ( github.com/libdns/googleclouddns v1.1.0 github.com/libdns/hetzner v0.0.1 github.com/libdns/leaseweb v0.3.1 - github.com/libdns/libdns v0.2.2-0.20221006221142-3ef90aee33fd + github.com/libdns/libdns v0.2.2-0.20230227175549-2dc480633939 github.com/libdns/metaname v0.3.0 github.com/libdns/namecheap v0.0.0-20211109042440-fc7440785c8e github.com/libdns/namedotcom v0.3.3 - github.com/libdns/route53 v1.3.0 - github.com/libdns/vultr v0.0.0-20220906182619-5ea9da3d9625 + github.com/libdns/route53 v1.3.3 + github.com/libdns/vultr v1.0.0 github.com/mattn/go-sqlite3 v2.0.3+incompatible - github.com/miekg/dns v1.1.50 - github.com/minio/minio-go/v7 v7.0.47 + github.com/miekg/dns v1.1.54 + github.com/minio/minio-go/v7 v7.0.55 github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6 - github.com/prometheus/client_golang v1.14.0 - github.com/urfave/cli/v2 v2.24.3 + github.com/prometheus/client_golang v1.15.1 + github.com/urfave/cli/v2 v2.25.5 go.uber.org/zap v1.24.0 - golang.org/x/crypto v0.5.0 - golang.org/x/net v0.7.0 - golang.org/x/sync v0.1.0 - golang.org/x/text v0.7.0 + golang.org/x/crypto v0.9.0 + golang.org/x/net v0.10.0 + golang.org/x/sync v0.2.0 + golang.org/x/text v0.9.0 ) require ( - cloud.google.com/go/compute v1.18.0 // indirect + cloud.google.com/go/compute v1.19.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/aws/aws-sdk-go v1.44.40 // indirect - github.com/aws/aws-sdk-go-v2 v1.17.4 // indirect - github.com/aws/aws-sdk-go-v2/config v1.18.12 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.12 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 // indirect - github.com/aws/aws-sdk-go-v2/service/route53 v1.27.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.12.1 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.18.3 // indirect + github.com/aws/aws-sdk-go-v2 v1.18.0 // indirect + github.com/aws/aws-sdk-go-v2/config v1.18.25 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.13.24 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 // indirect + github.com/aws/aws-sdk-go-v2/service/route53 v1.28.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 // indirect github.com/aws/smithy-go v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/digitalocean/godo v1.96.0 // indirect + github.com/digitalocean/godo v1.99.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect - github.com/fatih/color v1.14.1 // indirect + github.com/fatih/color v1.15.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.1 // indirect - github.com/googleapis/gax-go/v2 v2.7.0 // indirect + github.com/google/s2a-go v0.1.4 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect + github.com/googleapis/gax-go/v2 v2.9.1 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.2 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.15.15 // indirect - github.com/klauspost/cpuid/v2 v2.2.3 // indirect + github.com/klauspost/compress v1.16.5 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mholt/acmez v1.0.4 // indirect + github.com/mholt/acmez v1.1.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect - github.com/minio/sha256-simd v1.0.0 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd // indirect - github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.39.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect - github.com/rs/xid v1.4.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/rs/xid v1.5.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 // indirect - github.com/sirupsen/logrus v1.9.0 // indirect - github.com/spf13/afero v1.9.3 // indirect - github.com/spf13/cast v1.5.0 // indirect + github.com/sirupsen/logrus v1.9.2 // indirect + github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.15.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/vultr/govultr/v2 v2.17.2 // indirect + github.com/vultr/govultr/v3 v3.0.2 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.opencensus.io v0.24.0 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.9.0 // indirect - golang.org/x/mod v0.7.0 // indirect - golang.org/x/oauth2 v0.4.0 // indirect - golang.org/x/sys v0.5.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/mod v0.10.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/sys v0.8.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.5.0 // indirect - google.golang.org/api v0.109.0 // indirect + golang.org/x/tools v0.9.1 // indirect + google.golang.org/api v0.124.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 // indirect - google.golang.org/grpc v1.52.3 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/genproto v0.0.0-20230525234025-438c736192d0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e // indirect + google.golang.org/grpc v1.55.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools v2.2.0+incompatible // indirect diff --git a/go.sum b/go.sum index ddf6797a..dbe85a5f 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,7 @@ cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+ cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww= +cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= @@ -73,6 +74,8 @@ cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQH cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY= cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds= +cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= @@ -116,6 +119,7 @@ cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQn cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= +cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= @@ -191,6 +195,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw= github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo= +github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 h1:IEjq88XO4PuBDcvmjQJcQGg+w+UaafSy8G5Kcb5tBhI= +github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5/go.mod h1:exZ0C/1emQJAw5tHOaUDyY1ycttqBAPcxuzf7QbY6ec= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/aws/aws-sdk-go v1.17.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= @@ -199,36 +205,72 @@ github.com/aws/aws-sdk-go v1.44.40/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4 github.com/aws/aws-sdk-go-v2 v1.10.0/go.mod h1:U/EyyVvKtzmFeQQcca7eBotKdlpcP2zzU6bXBYcf7CE= github.com/aws/aws-sdk-go-v2 v1.17.4 h1:wyC6p9Yfq6V2y98wfDsj6OnNQa4w2BLGCLIxzNhwOGY= github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2 v1.17.8/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= +github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2/config v1.9.0/go.mod h1:qhK5NNSgo9/nOSMu3HyE60WHXZTWTHTgd5qtIF44vOQ= github.com/aws/aws-sdk-go-v2/config v1.18.12 h1:fKs/I4wccmfrNRO9rdrbMO1NgLxct6H9rNMiPdBxHWw= github.com/aws/aws-sdk-go-v2/config v1.18.12/go.mod h1:J36fOhj1LQBr+O4hJCiT8FwVvieeoSGOtPuvhKlsNu8= +github.com/aws/aws-sdk-go-v2/config v1.18.21/go.mod h1:+jPQiVPz1diRnjj6VGqWcLK6EzNmQ42l7J3OqGTLsSY= +github.com/aws/aws-sdk-go-v2/config v1.18.25 h1:JuYyZcnMPBiFqn87L2cRppo+rNwgah6YwD3VuyvaW6Q= +github.com/aws/aws-sdk-go-v2/config v1.18.25/go.mod h1:dZnYpD5wTW/dQF0rRNLVypB396zWCcPiBIvdvSWHEg4= github.com/aws/aws-sdk-go-v2/credentials v1.5.0/go.mod h1:kvqTkpzQmzri9PbsiTY+LvwFzM0gY19emlAWwBOJMb0= github.com/aws/aws-sdk-go-v2/credentials v1.13.12 h1:Cb+HhuEnV19zHRaYYVglwvdHGMJWbdsyP4oHhw04xws= github.com/aws/aws-sdk-go-v2/credentials v1.13.12/go.mod h1:37HG2MBroXK3jXfxVGtbM2J48ra2+Ltu+tmwr/jO0KA= +github.com/aws/aws-sdk-go-v2/credentials v1.13.20/go.mod h1:xtZnXErtbZ8YGXC3+8WfajpMBn5Ga/3ojZdxHq6iI8o= +github.com/aws/aws-sdk-go-v2/credentials v1.13.24 h1:PjiYyls3QdCrzqUN35jMWtUK1vqVZ+zLfdOa/UPFDp0= +github.com/aws/aws-sdk-go-v2/credentials v1.13.24/go.mod h1:jYPYi99wUOPIFi0rhiOvXeSEReVOzBqFNOX5bXYoG2o= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.7.0/go.mod h1:KqEkRkxm/+1Pd/rENRNbQpfblDBYeg5HDSqjB6ks8hA= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 h1:3aMfcTmoXtTZnaT86QlVaYh+BRMbvrrmZwIQ5jWqCZQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22/go.mod h1:YGSIJyQ6D6FjKMQh16hVFSIUD54L4F7zTGePqYMYYJU= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.2/go.mod h1:cDh1p6XkSGSwSRIArWRc6+UqAQ7x4alQ0QfpVR6f+co= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 h1:jJPgroehGvjrde3XufFIJUZVK5A2L9a3KwSFgKy9n8w= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 h1:r+XwaCLpIvCKjBIYy/HVZujQS9tsz5ohHG3ZIe0wKoE= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.32/go.mod h1:RudqOgadTWdcS3t/erPQo24pcVEoYyqj/kKW5Vya21I= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 h1:7AwGYXDdqRQYsluvKFmWoqpcOQJ4bH634SkYf3FNj/A= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.26/go.mod h1:vq86l7956VgFr0/FWQ2BWnK07QC3WYsepKzy33qqY5U= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= github.com/aws/aws-sdk-go-v2/internal/ini v1.2.5/go.mod h1:6ZBTuDmvpCOD4Sf1i2/I3PgftlEcDGgvi8ocq64oQEg= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 h1:J4xhFd6zHhdF9jPP0FQJ6WknzBboGMBNjKOv4iTuw4A= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29/go.mod h1:TwuqRBGzxjQJIwH16/fOZodwXt2Zxa9/cwJC5ke4j7s= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.33/go.mod h1:zG2FcwjQarWaqXSCGpgcr3RSjZ6dHGguZSppUL0XR7Q= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 h1:gGLG7yKaXG02/jBlg210R7VgQIotiQntNhsCFejawx8= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.4.0/go.mod h1:X5/JuOxPLU/ogICgDTtnpfaQzdQJO0yKDcpoxWLLJ8Y= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 h1:LjFQf8hFuMO22HkV5VWGLBvmCLBCLPivUAmpdpnp4Vs= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22/go.mod h1:xt0Au8yPIwYXf/GYPy/vl4K3CgwhfQMYbrH7DlUUIws= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.26/go.mod h1:Bd4C/4PkVGubtNe5iMXu5BNnaBi/9t/UsFspPt4ram8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAcCa2qVtRs7Ot5hItA2MsufrphbRFlz1Owxo= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw= github.com/aws/aws-sdk-go-v2/service/route53 v1.12.0/go.mod h1:LbPVLMeOEGLIW54yuMayW70DcTtsb+17ekL5j48deF4= github.com/aws/aws-sdk-go-v2/service/route53 v1.27.1 h1:F0SHIrL3PMxZFhxRfzr0MS1TyLuSZ5U/mLwFU8QZPI8= github.com/aws/aws-sdk-go-v2/service/route53 v1.27.1/go.mod h1:Dc2/L5MZOZaLaBHJmykEltTj15t7WMTQnGZlD0Ju/kg= +github.com/aws/aws-sdk-go-v2/service/route53 v1.27.7/go.mod h1:Jhu94omkrksnqX6Xs4Qo10eA1Fx+2NYKjZMU4GvZLp0= +github.com/aws/aws-sdk-go-v2/service/route53 v1.28.1 h1:8e1fgdyer5IqBPtiWNsVLY/XFucmNTtYMqADyCFXTgQ= +github.com/aws/aws-sdk-go-v2/service/route53 v1.28.1/go.mod h1:9SEpwqaALzp34eCT6w5PTh4SDDT84wxfMRx9VJSJPsk= github.com/aws/aws-sdk-go-v2/service/sso v1.5.0/go.mod h1:GsqaJOJeOfeYD88/2vHWKXegvDRofDqWwC5i48A2kgs= github.com/aws/aws-sdk-go-v2/service/sso v1.12.1 h1:lQKN/LNa3qqu2cDOQZybP7oL4nMGGiFqob0jZJaR8/4= github.com/aws/aws-sdk-go-v2/service/sso v1.12.1/go.mod h1:IgV8l3sj22nQDd5qcAGY0WenwCzCphqdbFOpfktZPrI= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.8/go.mod h1:GNIveDnP+aE3jujyUSH5aZ/rktsTM5EvtKnCqBZawdw= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 h1:UBQjaMTCKwyUYwiVnUt6toEJwGXsLBI6al083tpjJzY= +github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1 h1:0bLhH6DRAqox+g0LatcjGKjjhU6Eudyys6HB6DJVPj8= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1/go.mod h1:O1YSOg3aekZibh2SngvCRRG+cRHKKlYgxf/JBF/Kr/k= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.8/go.mod h1:44qFP1g7pfd+U+sQHLPalAPKnyfTZjJsYR4xIwsJy5o= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 h1:PkHIIJs8qvq0e5QybnZoG1K/9QTrLr9OsqCIo59jOBA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= github.com/aws/aws-sdk-go-v2/service/sts v1.8.0/go.mod h1:dOlm91B439le5y1vtPCk5yJtbx3RdT3hRGYRY8TYKvQ= github.com/aws/aws-sdk-go-v2/service/sts v1.18.3 h1:s49mSnsBZEXjfGBkRfmK+nPqzT7Lt3+t2SmAKNyHblw= github.com/aws/aws-sdk-go-v2/service/sts v1.18.3/go.mod h1:b+psTJn33Q4qGoDaM7ZiOVVG8uVjGI6HaZ8WBHdgDgU= +github.com/aws/aws-sdk-go-v2/service/sts v1.18.9/go.mod h1:yyW88BEPXA2fGFyI2KCcZC3dNpiT0CZAHaF+i656/tQ= +github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 h1:2DQLAKDteoEDI8zpCzqBMaZlJuoE9iTYD0gFmXVax9E= +github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= github.com/aws/smithy-go v1.8.1/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= @@ -239,6 +281,7 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE= github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= @@ -264,6 +307,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/digitalocean/godo v1.41.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= github.com/digitalocean/godo v1.96.0 h1:w46AC3z9upSEjxRa4jhjwYlp3XCTHpKdTFLtPWA4rXE= github.com/digitalocean/godo v1.96.0/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA= +github.com/digitalocean/godo v1.99.0 h1:gUHO7n9bDaZFWvbzOum4bXE0/09ZuYA9yA8idQHX57E= +github.com/digitalocean/godo v1.99.0/go.mod h1:SsS2oXo2rznfM/nORlZ/6JaUJZFhmKTib1YhopUc8NA= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a/go.mod h1:ikgISoP7pRAolqsVP64yMteJa2FIpS6ju88eBT6K1yQ= @@ -304,6 +349,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/foxcpp/go-dovecot-sasl v0.0.0-20200522223722-c4699d7a24bf h1:rmBPY5fryjp9zLQYsUmQqqgsYq7qeVfrjtr96Tf9vD8= github.com/foxcpp/go-dovecot-sasl v0.0.0-20200522223722-c4699d7a24bf/go.mod h1:5yZUmwr851vgjyAfN7OEfnrmKOh/qLA5dbGelXYsu1E= github.com/foxcpp/go-imap v1.0.0-beta.1.0.20220623182312-df940c324887 h1:qUoaaHyrRpQw85ru6VQcC6JowdhrWl7lSbI1zRX1FTM= @@ -328,6 +375,7 @@ github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8 h1:k8w0iy6GP9oeSZ github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8/go.mod h1:HO1YOCbBM8KjpgThMMFejHx6K/UsnEv2Oh9YGtBIlOU= github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -341,6 +389,8 @@ github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXg github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -374,6 +424,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -420,6 +472,8 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= +github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -428,6 +482,8 @@ github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg= github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -439,6 +495,8 @@ github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqE github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.9.1 h1:DpTpJqzZ3NvX9zqjhIuI1oVzYZMvboZe+3LoeEIJjHM= +github.com/googleapis/gax-go/v2 v2.9.1/go.mod h1:4FG3gMrVZlyMp5itSYKMU9z/lBE7+SbnUOvzH2HqbEY= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= @@ -448,6 +506,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/S github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0= github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= @@ -474,19 +534,26 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= +github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= +github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/lib/pq v1.4.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libdns/alidns v1.0.3-0.20220501125541-4a895238a95d h1:UiGXId+q/C65kEY3MJhdmK3d4QiS4yrWljeDjc8tZ0E= github.com/libdns/alidns v1.0.3-0.20220501125541-4a895238a95d/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE= github.com/libdns/cloudflare v0.1.1-0.20221006221909-9d3ab3c3cddd h1:c5hc0b5/pFqFeyQaOTVmYJbyr+QwZZFcMnjgtZGIk6k= @@ -506,6 +573,8 @@ github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSO github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= github.com/libdns/libdns v0.2.2-0.20221006221142-3ef90aee33fd h1:SyZBFgMczGjPf5VIKgj3OqpvWPd4qsx6VTX5Bpe3GkU= github.com/libdns/libdns v0.2.2-0.20221006221142-3ef90aee33fd/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= +github.com/libdns/libdns v0.2.2-0.20230227175549-2dc480633939 h1:EvTiXkv78P20yfk4CUPmAkH3Cmumt3s/48WWiC2babY= +github.com/libdns/libdns v0.2.2-0.20230227175549-2dc480633939/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/libdns/metaname v0.3.0 h1:HJudLYthdv52TupOPczojip/nEQHW7xqk5+whGReva4= github.com/libdns/metaname v0.3.0/go.mod h1:a3hqEgj59tjWaWlF4WxQGhvMVtjz1E4Ngs1GfVS+VhQ= github.com/libdns/namecheap v0.0.0-20211109042440-fc7440785c8e h1:WCcKyxiiK/sJnST1ulVBKNg4J8luCYDdgUrp2ySMO2s= @@ -514,8 +583,12 @@ github.com/libdns/namedotcom v0.3.3 h1:R10C7+IqQGVeC4opHHMiFNBxdNBg1bi65ZwqLESl+ github.com/libdns/namedotcom v0.3.3/go.mod h1:GbYzsAF2yRUpI0WgIK5fs5UX+kDVUPaYCFLpTnKQm0s= github.com/libdns/route53 v1.3.0 h1:f41D9uUK7Gib8Zbg3LtAXfxGRFlqfR4gep+FsthDFg0= github.com/libdns/route53 v1.3.0/go.mod h1:Vu827KwORxYR2I6iGsu8IKh4MESliECL7VA4pAsn95o= +github.com/libdns/route53 v1.3.3 h1:16sTxbbRGm0zODz0p0aVHHIyTqtHzEn3j0s4dGzQvNI= +github.com/libdns/route53 v1.3.3/go.mod h1:n1Xy55lpfdxMIx4CVWAM16GQac+/OZcnm1xBjMyhZAo= github.com/libdns/vultr v0.0.0-20220906182619-5ea9da3d9625 h1:ZOC61eCF7y6Hjj3D0aMtef7zMbQAUGGLXydvOmpa75Y= github.com/libdns/vultr v0.0.0-20220906182619-5ea9da3d9625/go.mod h1:s+M03kLf7Z2ZR6Ut5cl16fycy9MjI3ETdF1LENh+8E8= +github.com/libdns/vultr v1.0.0 h1:W8B4+k2bm9ro3bZLSZV9hMOQI+uO6Svu+GmD+Olz7ZI= +github.com/libdns/vultr v1.0.0/go.mod h1:8K1HJExcbeHS4YPkFHRZpqpXZzZ+DZAA0m0VikJgEqk= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= @@ -531,22 +604,32 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80= github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY= +github.com/mholt/acmez v1.1.1 h1:sYeeYd/EHVm9cSmLdWey5oW/fXFVAq5pNLjSczN2ZUg= +github.com/mholt/acmez v1.1.1/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI= +github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/minio-go/v7 v7.0.47 h1:sLiuCKGSIcn/MI6lREmTzX91DX/oRau4ia0j6e6eOSs= github.com/minio/minio-go/v7 v7.0.47/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw= +github.com/minio/minio-go/v7 v7.0.55 h1:ZXqUO/8cgfHzI+08h/zGuTTFpISSA32BZmBE3FCLJas= +github.com/minio/minio-go/v7 v7.0.55/go.mod h1:NUDy4A4oXPq1l2yK6LTSvCEzAMeIcoz9lcj5dbzSrRE= github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -560,6 +643,8 @@ github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd h1:4yVpQ/+li28lQ/ github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd/go.mod h1:wpK5wqysOJU1w2OxgG65du8M7UqBkxzsNaJdjwiRqAs= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -571,18 +656,29 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= +github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= +github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= +github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8= @@ -591,12 +687,18 @@ github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 h1:J6qvD6rbm github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= +github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -617,13 +719,19 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli/v2 v2.24.3 h1:7Q1w8VN8yE0MJEHP06bv89PjYsN4IHWED2s1v/Zlfm0= github.com/urfave/cli/v2 v2.24.3/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= +github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc= +github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= +github.com/vultr/govultr/v3 v3.0.2 h1:rrYiuF9adB3rjnhp0ev+mkJXKEzuYa/AGfezYPr3EMs= +github.com/vultr/govultr/v3 v3.0.2/go.mod h1:Pd3D6VKmQKyKWsdV1xLx4VKclEV23adMs3YoI7rh7gA= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -646,11 +754,15 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= @@ -664,10 +776,14 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -706,6 +822,8 @@ 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 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -763,6 +881,8 @@ golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfS golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= 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/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -790,6 +910,8 @@ golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -806,6 +928,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -881,6 +1005,9 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.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/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -893,9 +1020,12 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -959,6 +1089,8 @@ golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyj golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4= golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= 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= @@ -1017,6 +1149,8 @@ google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= google.golang.org/api v0.109.0 h1:sW9hgHyX497PP5//NUM7nqfV8D0iDfBApqq7sOh1XR8= google.golang.org/api v0.109.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.124.0 h1:dP6Ef1VgOGqQ8eiv4GiY8RhmeyqzovcXBYPDUYG8Syo= +google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1130,6 +1264,10 @@ google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55/go.mod h1:45EK0dUbEZ2NHjCeAd2LXmyjAgGUGrpGROgjhC3ADck= google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 h1:vArvWooPH749rNHpBGgVl+U9B9dATjiEhJzcWGlovNs= google.golang.org/genproto v0.0.0-20230202175211-008b39050e57/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230525234025-438c736192d0 h1:x1vNwUhVOcsYoKyEGCZBH694SBmmBjA2EfauFVEI2+M= +google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e h1:NumxXLPfHSndr3wBBdeKiVHjGVFzi9RX2HwwQke94iY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1167,6 +1305,8 @@ google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCD google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.52.3 h1:pf7sOysg4LdgBqduXveGKrcEwbStiK2rtfghdzlUYDQ= google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= +google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= +google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -1184,6 +1324,8 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 5d6d1a3a536813f1ac3adfbbf2d12b731ade4a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: 2023年6月29日 16:12:11 +0300 Subject: [PATCH 008/171] Improve Markdown formatting * use

and
elements to separate configuration directives * use elements to mark constants, fs paths, module names etc. * fix unneeded "\_" escaping * fix list formatting --- docs/reference/auth/dovecot_sasl.md | 8 +- docs/reference/auth/external.md | 23 ++-- docs/reference/auth/ldap.md | 65 ++++++---- docs/reference/auth/netauth.md | 15 +-- docs/reference/auth/pam.md | 24 ++-- docs/reference/auth/pass_table.md | 12 +- docs/reference/auth/plain_separate.md | 11 +- docs/reference/auth/shadow.md | 16 ++- docs/reference/blob/fs.md | 5 +- docs/reference/blob/s3.md | 56 +++++---- docs/reference/checks/actions.md | 6 +- docs/reference/checks/authorize_sender.md | 62 +++++---- docs/reference/checks/command.md | 113 ++++++----------- docs/reference/checks/dkim.md | 28 +++-- docs/reference/checks/dnsbl.md | 65 ++++++---- docs/reference/checks/milter.md | 12 +- docs/reference/checks/misc.md | 33 ++--- docs/reference/checks/rspamd.md | 58 ++++++--- docs/reference/checks/spf.md | 50 +++++--- docs/reference/config-syntax.md | 16 +-- docs/reference/endpoints/imap.md | 61 ++++++--- docs/reference/endpoints/openmetrics.md | 1 + docs/reference/endpoints/smtp.md | 140 ++++++++++++--------- docs/reference/global-config.md | 88 +++++++------ docs/reference/modifiers/dkim.md | 122 +++++++++++------- docs/reference/modifiers/envelope.md | 5 +- docs/reference/smtp-pipeline.md | 140 +++++++++++++-------- docs/reference/storage/imapsql.md | 131 +++++++++++-------- docs/reference/table/chain.md | 7 +- docs/reference/table/email_localpart.md | 7 +- docs/reference/table/email_with_domains.md | 6 +- docs/reference/table/regexp.md | 25 ++-- docs/reference/table/sql_query.md | 46 ++++--- docs/reference/table/static.md | 2 +- docs/reference/targets/queue.md | 45 ++++--- docs/reference/targets/remote.md | 118 +++++++++++------ docs/reference/targets/smtp.md | 87 ++++++------- docs/reference/tls-acme.md | 68 ++++++---- docs/reference/tls.md | 132 +++++++++---------- 39 files changed, 1111 insertions(+), 798 deletions(-) diff --git a/docs/reference/auth/dovecot_sasl.md b/docs/reference/auth/dovecot_sasl.md index b00f9c77..919d42b8 100644 --- a/docs/reference/auth/dovecot_sasl.md +++ b/docs/reference/auth/dovecot_sasl.md @@ -1,6 +1,6 @@ # Dovecot SASL -The 'auth.dovecot\_sasl' module implements the client side of the Dovecot +The 'auth.dovecot_sasl' module implements the client side of the Dovecot authentication protocol, allowing maddy to use it as a credentials source. Currently SASL mechanisms support is limited to mechanisms supported by maddy @@ -16,11 +16,11 @@ dovecot_sasl unix://socket_path ## Configuration directives -**Syntax**: endpoint _schema://address_
-**Default**: not set +### endpoint _schema://address_ +Default: not set Set the address to use to contact Dovecot SASL server in the standard endpoint format. -tcp://10.0.0.1:2222 for TCP, unix:///var/lib/dovecot/auth.sock for Unix +`tcp://10.0.0.1:2222` for TCP, `unix:///var/lib/dovecot/auth.sock` for Unix domain sockets. diff --git a/docs/reference/auth/external.md b/docs/reference/auth/external.md index 6c28d743..9b9659ef 100644 --- a/docs/reference/auth/external.md +++ b/docs/reference/auth/external.md @@ -1,12 +1,12 @@ # System command auth.external module for authentication using external helper binary. It looks for binary -named maddy-auth-helper in $PATH and libexecdir and uses it for authentication +named `maddy-auth-helper` in $PATH and libexecdir and uses it for authentication using username/password pair. The protocol is very simple: Program is launched for each authentication. Username and password are written -to stdin, adding \\n to the end. If binary exits with 0 status code - +to stdin, adding \n to the end. If binary exits with 0 status code - authentication is considered successful. If the status code is 1 - authentication is failed. If the status code is 2 - another unrelated error has happened. Additional information should be written to stderr. @@ -21,19 +21,24 @@ auth.external { ## Configuration directives -**Syntax**: helper _file\_path\_ +### helper _file_path_ -Location of the helper binary. **Required.** +**Required.**
+Location of the helper binary. -**Syntax**: perdomain _boolean_
-**Default**: no +--- + +### perdomain _boolean_ +Default: `no` Don't remove domain part of username when authenticating and require it to be present. Can be used if you want user@domain1 and user@domain2 to be different accounts. -**Syntax**: domains _domains..._
-**Default**: not specified +--- + +### domains _domains..._ +Default: not specified Domains that should be allowed in username during authentication. @@ -43,5 +48,5 @@ name in addition to just username. If used without 'perdomain', domain part will be removed from login before check with underlying auth. mechanism. If 'perdomain' is set, then -domains must be also set and domain part WILL NOT be removed before check. +domains must be also set and domain part **will not** be removed before check. diff --git a/docs/reference/auth/ldap.md b/docs/reference/auth/ldap.md index c2c3ce6b..a4ced551 100644 --- a/docs/reference/auth/ldap.md +++ b/docs/reference/auth/ldap.md @@ -8,7 +8,7 @@ directory search or template . Note that storage backends conventionally use email addresses, if you use non-email identifiers as usernames then you should map them onto -emails on delivery by using auth\_map (see documentation page for used storage backend). +emails on delivery by using `auth_map` (see documentation page for used storage backend). auth.ldap also can be a used as a table module. This way you can check whether the account exists. It works only if DN template is not used. @@ -42,72 +42,89 @@ auth.ldap ldap://maddy.test.389 { ## Configuration directives -**Syntax:** urls _servers...\_ +### urls _servers..._ -REQUIRED. +**Required.** URLs of the directory servers to use. First available server is used - no load-balancing is done. -URLs should use 'ldap://', 'ldaps://', 'ldapi://' schemes. +URLs should use `ldap://`, `ldaps://`, `ldapi://` schemes. -**Syntax:** bind off
-bind unauth
-bind external
-bind plain _username_ _password_
-**Default:** off +--- + +### bind `off` | `unauth` | `external` | `plain` _username_ _password_ + +Default: `off` Credentials to use for initial binding. Required if DN lookup is used. -'unauth' performs unauthenticated bind. 'external' performs external binding -which is useful for Unix socket connections (ldapi://) or TLS client certificate -authentication (cert. is set using tls\_client directive). 'plain' performs a +`unauth` performs unauthenticated bind. `external` performs external binding +which is useful for Unix socket connections (`ldapi://`) or TLS client certificate +authentication (cert. is set using tls_client directive). `plain` performs a simple bind using provided credentials. -**Syntax:** dn\_template _template\_ +--- + +### dn_template _template_ -DN template to use for binding. '{username}' is replaced with the +DN template to use for binding. `{username}` is replaced with the username specified by the user. -**Syntax:** base\_dn _dn\_ +--- + +### base_dn _dn_ Base DN to use for lookup. -**Syntax:** filter _str\_ +--- + +### filter _str_ -DN lookup filter. '{username}' is replaced with the username specified +DN lookup filter. `{username}` is replaced with the username specified by the user. Example: + ``` (&(objectClass=posixAccount)(uid={username})) ``` Example (using ActiveDirectory): + ``` (&(objectCategory=Person)(memberOf=CN=user-group,OU=example,DC=example,DC=org)(sAMAccountName={username})(!(UserAccountControl:1.2.840.113556.1.4.803:=2))) ``` Example: + ``` (&(objectClass=Person)(mail={username})) ``` -**Syntax:** starttls _bool_
-**Default:** off +--- + +### starttls _bool_ +Default: `off` Whether to upgrade connection to TLS using STARTTLS. -**Syntax:** tls\_client { ... } +--- + +### tls_client { ... } Advanced TLS client configuration. See [TLS configuration / Client](/reference/tls/#client) for details. -**Syntax:** connect\_timeout _duration_
-**Default:** 1m +--- + +### connect_timeout _duration_ +Default: `1m` Timeout for initial connection to the directory server. -**Syntax:** request\_timeout _duration_
-**Default:** 1m +--- + +### request_timeout _duration_ +Default: `1m` Timeout for each request (binding, lookup). diff --git a/docs/reference/auth/netauth.md b/docs/reference/auth/netauth.md index 074b74c8..2664d41f 100644 --- a/docs/reference/auth/netauth.md +++ b/docs/reference/auth/netauth.md @@ -9,7 +9,7 @@ mail address. Note that storage backends conventionally use email addresses. Since NetAuth recommends *nix compatible usernames, you will need to map the -email identifiers to NetAuth Entity IDs using auth\_map (see +email identifiers to NetAuth Entity IDs using `auth_map` (see documentation page for used storage backend). auth.netauth also can be used as a table module. This way you can @@ -33,15 +33,16 @@ auth.netauth {} ## Configuration directives -**Syntax:** require\_group _group_ +### require_group _group_ -OPTIONAL. +Optional. Group that entities must possess to be able to use maddy services. This can be used to provide email to just a subset of the entities present in NetAuth. -**Syntax** debug off
-debug on
-debug off
-**Default:** off +--- + +### debug `on` | `off` + +Default: `off` diff --git a/docs/reference/auth/pam.md b/docs/reference/auth/pam.md index 79331fce..89f0f3e3 100644 --- a/docs/reference/auth/pam.md +++ b/docs/reference/auth/pam.md @@ -4,7 +4,8 @@ auth.pam module implements authentication using libpam. Alternatively it can be use helper binary like auth.external module does. maddy should be built with libpam build tag to use this module without -'use\_helper' directive. +'use_helper' directive. + ``` go get -tags 'libpam' ... ``` @@ -18,25 +19,28 @@ auth.pam { ## Configuration directives -**Syntax**: debug _boolean_
-**Default**: no +### debug _boolean_ +Default: `no` Enable verbose logging for all modules. You don't need that unless you are reporting a bug. -**Syntax**: use\_helper _boolean_
-**Default**: no +--- + +### use_helper _boolean_ +Default: `no` -Use LibexecDirectory/maddy-pam-helper instead of directly calling libpam. +Use `LibexecDirectory/maddy-pam-helper` instead of directly calling libpam. You need to use that if: -1. maddy is not compiled with libpam, but maddy-pam-helper is built separately. -2. maddy is running as an unprivileged user and used PAM configuration requires additional - privileges (e.g. when using system accounts). -For 2, you need to make maddy-pam-helper binary setuid, see +1. maddy is not compiled with libpam, but `maddy-pam-helper` is built separately. +2. maddy is running as an unprivileged user and used PAM configuration requires additional privileges (e.g. when using system accounts). + +For 2, you need to make `maddy-pam-helper` binary setuid, see README.md in source tree for details. TL;DR (assuming you have the maddy group): + ``` chown root:maddy /usr/lib/maddy/maddy-pam-helper chmod u+xs,g+x,o-x /usr/lib/maddy/maddy-pam-helper diff --git a/docs/reference/auth/pass_table.md b/docs/reference/auth/pass_table.md index 6b1e2146..39fea6fc 100644 --- a/docs/reference/auth/pass_table.md +++ b/docs/reference/auth/pass_table.md @@ -3,7 +3,7 @@ auth.pass_table module implements username:password authentication by looking up the password hash using a table module (maddy-tables(5)). It can be used to load user credentials from text file (via table.file module) or SQL query -(via table.sql\_table module). +(via table.sql_table module). Definition: @@ -30,15 +30,15 @@ smtp tcp://0.0.0.0:587 { ## Password hashes -pass\_table expects the used table to contain certain structured values with +pass_table expects the used table to contain certain structured values with hash algorithm name, salt and other necessary parameters. -You should use 'maddy hash' command to generate suitable values. -See 'maddy hash --help' for details. +You should use `maddy hash` command to generate suitable values. +See `maddy hash --help` for details. ## maddy creds If the underlying table is a "mutable" table (see maddy-tables(5)) then -the 'maddy creds' command can be used to modify the underlying tables -via pass\_table module. It will act on a "local credentials store" and will write +the `maddy creds` command can be used to modify the underlying tables +via pass_table module. It will act on a "local credentials store" and will write appropriate hash values to the table. diff --git a/docs/reference/auth/plain_separate.md b/docs/reference/auth/plain_separate.md index 0e1cb09b..f5b57667 100644 --- a/docs/reference/auth/plain_separate.md +++ b/docs/reference/auth/plain_separate.md @@ -1,6 +1,6 @@ # Separate username and password lookup -auth.plain\_separate module implements authentication using username:password pairs but can +auth.plain_separate module implements authentication using username:password pairs but can use zero or more "table modules" (maddy-tables(5)) and one or more authentication providers to verify credentials. @@ -24,19 +24,22 @@ How it works: ## Configuration directives -***Syntax:*** user _table module\_ +### user _table-module_ Configuration block for any module from maddy-tables(5) can be used here. Example: + ``` user file /etc/maddy/allowed_users ``` -***Syntax:*** pass _auth provider\_ +--- + +### pass _auth-provider_ Configuration block for any auth. provider module can be used here, even -'plain\_split' itself. +'plain_split' itself. The used auth. provider must provide username:password pair-based authentication. diff --git a/docs/reference/auth/shadow.md b/docs/reference/auth/shadow.md index e3c41c8c..0fc3e89b 100644 --- a/docs/reference/auth/shadow.md +++ b/docs/reference/auth/shadow.md @@ -12,23 +12,27 @@ auth.shadow { ## Configuration directives -**Syntax**: debug _boolean_
-**Default**: no +### debug _boolean_ + +Default: `no` Enable verbose logging for all modules. You don't need that unless you are reporting a bug. -**Syntax**: use\_helper _boolean_
-**Default**: no +--- + +### use_helper _boolean_ +Default: `no` -Use LibexecDirectory/maddy-shadow-helper instead of directly reading /etc/shadow. +Use `LibexecDirectory/maddy-shadow-helper` instead of directly reading `/etc/shadow`. You need to use that if maddy is running as an unprivileged user privileges (e.g. when using system accounts). -You need to make maddy-shadow-helper binary setuid, see +You need to make `maddy-shadow-helper` binary setuid, see cmd/maddy-shadow-helper/README.md in source tree for details. TL;DR (assuming you have maddy group): + ``` chown root:maddy /usr/lib/maddy/maddy-shadow-helper chmod u+xs,g+x,o-x /usr/lib/maddy/maddy-shadow-helper diff --git a/docs/reference/blob/fs.md b/docs/reference/blob/fs.md index 4bc1c897..ef94b54b 100644 --- a/docs/reference/blob/fs.md +++ b/docs/reference/blob/fs.md @@ -7,14 +7,15 @@ storage.blob.fs { root } ``` + ``` storage.blob.fs ``` ## Configuration directives -**Syntax:** root _path_
-**Default:** not set +### root _path_ +Default: not set Path to the FS directory. Must be readable and writable by the server process. If it does not exist - it will be created (parent directory should be writable diff --git a/docs/reference/blob/s3.md b/docs/reference/blob/s3.md index ac02ed48..54b6a4e2 100644 --- a/docs/reference/blob/s3.md +++ b/docs/reference/blob/s3.md @@ -18,6 +18,7 @@ storage.blob.s3 { ``` Example: + ``` storage.imapsql local_mailboxes { ... @@ -34,53 +35,64 @@ storage.imapsql local_mailboxes { ## Configuration directives -**Syntax:** endpoint _address:port\_ +### endpoint _address:port_ + +**Required**. -REQUIRED. +Root S3 endpoint. e.g. `s3.amazonaws.com` -Root S3 endpoint. e.g. s3.amazonaws.com +--- -**Syntax:** secure _boolean_
-**Default:** yes +### secure _boolean_ +Default: `yes` Whether TLS should be used. -**Syntax:** access\_key _string_
-**Syntax:** secret\_key _string\_ +--- -REQUIRED. +### access_key _string_
secret_key _string_ + +**Required**. Static S3 credentials. -**Syntax:** bucket _name\_ +--- + +### bucket _name_ -REQUIRED. +**Required**. S3 bucket name. The bucket must exist and be read-writable. -**Syntax:** region _string_
-**Default:** not set +--- -S3 bucket location. May be called "endpoint" -in some manuals. +### region _string_ +Default: not set -**Syntax:** object\_prefix _string_
-**Default:** empty string +S3 bucket location. May be called "endpoint" in some manuals. + +--- + +### object_prefix _string_ +Default: empty string String to add to all keys stored by maddy. Can be useful when S3 is used as a file system. -**Syntax:** creds _string_
-**Default:** access_key +--- + +### creds `access_key` | `file_minio` | `file_aws` | `iam` +Default: `access_key` Credentials to use for accessing the S3 Bucket. Credential Types: - - access_key: use AWS access key and secret access key - - file_minio: use credentials for Minio present at ~/.mc/config.json - - file_aws: use credentials for AWS S3 present at ~/.aws/credentials - - iam: use AWS IAM instance profile for credentials. + + - `access_key`: use AWS access key and secret access key + - `file_minio`: use credentials for Minio present at ~/.mc/config.json + - `file_aws`: use credentials for AWS S3 present at ~/.aws/credentials + - `iam`: use AWS IAM instance profile for credentials. By default, access_key is used with the access key and secret access key present in the config. diff --git a/docs/reference/checks/actions.md b/docs/reference/checks/actions.md index 7ab88288..d9e9f9c9 100644 --- a/docs/reference/checks/actions.md +++ b/docs/reference/checks/actions.md @@ -4,16 +4,16 @@ When a certain check module thinks the message is "bad", it takes some actions depending on its configuration. Most checks follow the same configuration structure and allow following actions to be taken on check failure: -- Do nothing ('action ignore') +- Do nothing (`action ignore`) Useful for testing deployment of new checks. Check failures are still logged but they have no effect on message delivery. -- Reject the message ('action reject') +- Reject the message (`action reject`) Reject the message at connection time. No bounce is generated locally. -- Quarantine the message ('action quarantine') +- Quarantine the message (`action quarantine`) Mark message as 'quarantined'. If message is then delivered to the local storage, the storage backend can place the message in the 'Junk' mailbox. diff --git a/docs/reference/checks/authorize_sender.md b/docs/reference/checks/authorize_sender.md index b65ea2d0..0ceacff8 100644 --- a/docs/reference/checks/authorize_sender.md +++ b/docs/reference/checks/authorize_sender.md @@ -3,7 +3,7 @@ Module check.authorize_sender verifies that envelope and header sender addresses belong to the authenticated user. Address ownership is established via table that maps each user account to a email address it is allowed to use. -There are some special cases, see user\_to\_email description below. +There are some special cases, see `user_to_email` description below. ``` check.authorize_sender { @@ -28,16 +28,16 @@ check { ## Configuration directives -**Syntax:** user\_to\_email _table_
-**Default:** identity +### user_to_email _table_ +Default: `identity` Table that maps authorization username to the list of sender emails the user is allowed to use. In additional to email addresses, the table can contain domain names or -special string "\*" as a value. If the value is a domain - user +special string "*" as a value. If the value is a domain - user will be allowed to use any mailbox within it as a sender address. -If it is "\*" - user will be allowed to use any address. +If it is "*" - user will be allowed to use any address. By default, table.identity is used, meaning that username should be equal to the sender email. @@ -45,8 +45,10 @@ be equal to the sender email. Before username is looked up via the table, normalization algorithm defined by auth_normalize is applied to it. -**Syntax:** prepare\_email _table_
-**Default:** identity +--- + +### prepare_email _table_ +Default: `identity` Table that is used to translate email addresses before they are matched against user_to_email values. @@ -59,36 +61,48 @@ done in default configuration. If table does not contain any mapping for the used sender address, it will be used as is. -**Syntax:** check\_header _boolean_
-**Default:** yes +--- + +### check_header _boolean_ +Default: `yes` Whether to verify header sender in addition to envelope. Either Sender or From field value should match the authorization identity. -**Syntax:** unauth\_action _action_
-**Default:** reject +--- + +### unauth_action _action_ +Default: `reject` What to do if the user is not authenticated at all. -**Syntax:** no\_match\_action _action_
-**Default:** reject +--- + +### no_match_action _action_ +Default: `reject` What to do if user is not allowed to use the sender address specified. -**Syntax:** malformed\_action _action_
-**Default:** reject +--- + +### malformed_action _action_ +Default: `reject` What to do if From or Sender header fields contain malformed values. -**Syntax:** err\_action _action_
-**Default:** reject +--- + +### err_action _action_ +Default: `reject` -What to do if error happens during prepare\_email or user\_to\_email lookup. +What to do if error happens during prepare_email or user_to_email lookup. -**Syntax:** auth\_normalize _action_
-**Default:** auto +--- + +### auth_normalize _action_ +Default: `auto` Normalization function to apply to authorization username before further processing. @@ -107,10 +121,12 @@ PRECIS profiles are defined by RFC 8265. In short, they make sure that Unicode strings that look the same will be compared as if they were the same. CaseMapped profiles also convert strings to lower case. -**Syntax:** from\_normalize _action_
-**Default:** auto +--- + +### from_normalize _action_ +Default: `auto` Normalization function to apply to email addresses before further processing. -Available options are same as for auth\_normalize. +Available options are same as for `auth_normalize`. diff --git a/docs/reference/checks/command.md b/docs/reference/checks/command.md index 3cbb4892..6475efcf 100644 --- a/docs/reference/checks/command.md +++ b/docs/reference/checks/command.md @@ -23,47 +23,23 @@ system shell. There is a set of special strings that are replaced with the corresponding message-specific values: -- {source\_ip} - - IPv4/IPv6 address of the sending MTA. - -- {source\_host} - - Hostname of the sending MTA, from the HELO/EHLO command. - -- {source\_rdns} - - PTR record of the sending MTA IP address. - -- {msg\_id} - - Internal message identifier. Unique for each delivery. - -- {auth\_user} - - Client username, if authenticated using SASL PLAIN - -- {sender} - - Message sender address, as specified in the MAIL FROM SMTP command. - -- {rcpts} - - List of accepted recipient addresses, including the currently handled +- `{source_ip}` – IPv4/IPv6 address of the sending MTA. +- `{source_host}` – Hostname of the sending MTA, from the HELO/EHLO command. +- `{source_rdns}` – PTR record of the sending MTA IP address. +- `{msg_id}` – Internal message identifier. Unique for each delivery. +- `{auth_user}` – Client username, if authenticated using SASL PLAIN +- `{sender}` – Message sender address, as specified in the MAIL FROM SMTP command. +- `{rcpts}` – List of accepted recipient addresses, including the currently handled one. +- `{address}` – Currently handled address. This is a recipient address if the command + is called during RCPT TO command handling (`run_on rcpt`) or a sender + address if the command is called during MAIL FROM command handling (`run_on + sender`). -- {address} - - Currently handled address. This is a recipient address if the command - is called during RCPT TO command handling ('run\_on rcpt') or a sender - address if the command is called during MAIL FROM command handling ('run\_on - sender'). - - -If value is undefined (e.g. {source\_ip} for a message accepted over a Unix +If value is undefined (e.g. `{source_ip}` for a message accepted over a Unix socket) or unavailable (the command is executed too early), the placeholder is replaced with an empty string. Note that it can not remove the argument. -E.g. -i {source\_ip} will not become just -i, it will be -i "" +E.g. `-i {source_ip}` will not become just `-i`, it will be `-i ""` Undefined placeholders are not replaced. @@ -77,55 +53,44 @@ The header from stdout will be **prepended** to the message header. ## Configuration directives -**Syntax**: run\_on conn|sender|rcpt|body
-**Default**: body +### run_on `conn` | `sender` | `rcpt` | `body` +Default: `body` When to run the command. This directive also affects the information visible for the message. -- conn - - Run before the sender address (MAIL FROM) is handled. - - **Stdin**: Empty
- **Available placeholders**: {source\_ip}, {source\_host}, {msg\_id}, {auth\_user}. - -- sender - - Run during sender address (MAIL FROM) handling. - - **Stdin**: Empty
- **Available placeholders**: conn placeholders + {sender}, {address}. - - The {address} placeholder contains the MAIL FROM address. - -- rcpt - - Run during recipient address (RCPT TO) handling. The command is executed - once for each RCPT TO command, even if the same recipient is specified - multiple times. - - **Stdin**: Empty
- **Available placeholders**: sender placeholders + {rcpts}. +- `conn`
+ Run before the sender address (MAIL FROM) is handled.
+ **Stdin**: Empty
+ **Available placeholders**: {source_ip}, {source_host}, {msg_id}, {auth_user}. - The {address} placeholder contains the recipient address. +- `sender`
+ Run during sender address (MAIL FROM) handling.
+ **Stdin**: Empty
+ **Available placeholders**: conn placeholders + {sender}, {address}. + The {address} placeholder contains the MAIL FROM address. -- body +- `rcpt`
+ Run during recipient address (RCPT TO) handling. The command is executed + once for each RCPT TO command, even if the same recipient is specified + multiple times.
+ **Stdin**: Empty
+ **Available placeholders**: sender placeholders + {rcpts}. + The {address} placeholder contains the recipient address. - Run during message body handling. +- `body`
+ Run during message body handling.
+ **Stdin**: The message header + body
+ **Available placeholders**: all except for {address}. - **Stdin**: The message header + body
- **Available placeholders**: all except for {address}. +--- -**Syntax**:
-code _integer_ ignore
-code _integer_ quarantine
-code _integer_ reject [SMTP code] [SMTP enhanced code] [SMTP message] +### code _integer_ ignore
code _integer_ quarantine
code _integer_ reject _smtp-code_ _smtp-enhanced-code_ _smtp-message_ -This directives specified the mapping from the command exit code _integer_ to +This directive specifies the mapping from the command exit code _integer_ to the message pipeline action. Two codes are defined implicitly, exit code 1 causes the message to be rejected with a permanent error, exit code 2 causes the message to be quarantined. Both -action can be overridden using the 'code' directive. +actions can be overridden using the 'code' directive. diff --git a/docs/reference/checks/dkim.md b/docs/reference/checks/dkim.md index 5aa2dc34..7ab14a6d 100644 --- a/docs/reference/checks/dkim.md +++ b/docs/reference/checks/dkim.md @@ -16,14 +16,16 @@ check.dkim { } ``` -**Syntax**: debug _boolean_
-**Default**: global directive value +### debug _boolean_ +Default: global directive value Log both successful and unsuccessful check executions instead of just unsuccessful. -**Syntax**: required\_fields _string..._
-**Default**: From Subject +--- + +### required_fields _string..._ +Default: `From Subject` Header fields that should be included in each signature. If signature lacks any field listed in that directive, it will be considered invalid. @@ -31,24 +33,30 @@ lacks any field listed in that directive, it will be considered invalid. Note that From is always required to be signed, even if it is not included in this directive. -**Syntax**: no\_sig\_action _action_
-**Default**: ignore (recommended by RFC 6376) +--- + +### no_sig_action _action_ +Default: `ignore` (recommended by RFC 6376) Action to take when message without any signature is received. Note that DMARC policy of the sender domain can request more strict handling of missing DKIM signatures. -**Syntax**: broken\_sig\_action _action_
-**Default**: ignore (recommended by RFC 6376) +--- + +### broken_sig_action _action_ +Default: `ignore` (recommended by RFC 6376) Action to take when there are not valid signatures in a message. Note that DMARC policy of the sender domain can request more strict handling of broken DKIM signatures. -**Syntax**: fail\_open _boolean_
-**Default**: no +--- + +### fail_open _boolean_ +Default: `no` Whether to accept the message if a temporary error occurs during DKIM verification. Rejecting the message with a 4xx code will require the sender diff --git a/docs/reference/checks/dnsbl.md b/docs/reference/checks/dnsbl.md index bb3615b9..a2d27362 100644 --- a/docs/reference/checks/dnsbl.md +++ b/docs/reference/checks/dnsbl.md @@ -60,13 +60,15 @@ check { ## Configuration directives -**Syntax**: debug _boolean_
-**Default**: global directive value +### debug _boolean_ +Default: global directive value Enable verbose logging. -**Syntax**: check\_early _boolean_
-**Default**: no +--- + +### check_early _boolean_ +Default: `no` Check BLs before mail delivery starts and silently reject blacklisted clients. @@ -74,22 +76,27 @@ For this to work correctly, check should not be used in source/destination pipeline block. In particular, this means: + - No logging is done for rejected messages. -- No action is taken if quarantine\_threshold is hit, only reject\_threshold +- No action is taken if `quarantine_threshold` is hit, only `reject_threshold` applies. -- defer\_sender\_reject from SMTP configuration takes no effect. +- `defer_sender_reject` from SMTP configuration takes no effect. - MAIL FROM is not checked, even if specified. If you often get hit by spam attacks, it is recommended to enable this setting to save server resources. -**Syntax**: quarantine\_threshold _integer_
-**Default**: 1 +--- + +### quarantine_threshold _integer_ +Default: `1` DNSBL score needed (equals-or-higher) to quarantine the message. -**Syntax**: reject\_threshold _integer_
-**Default**: 9999 +--- + +### reject_threshold _integer_ +Default: `9999` DNSBL score needed (equals-or-higher) to reject the message. @@ -110,46 +117,56 @@ Directive name and arguments specify the actual DNS zone to query when checking the list. Using multiple arguments is equivalent to specifying the same configuration separately for each list. -**Syntax**: client\_ipv4 _boolean_
-**Default**: yes +### client_ipv4 _boolean_ +Default: `yes` Whether to check address of the IPv4 clients against the list. -**Syntax**: client\_ipv6 _boolean_
-**Default**: yes +--- + +### client_ipv6 _boolean_ +Default: `yes` Whether to check address of the IPv6 clients against the list. -**Syntax**: ehlo _boolean_
-**Default**: no +--- + +### ehlo _boolean_ +Default: `no` Whether to check hostname specified n the HELO/EHLO command against the list. This works correctly only with domain-based DNSBLs. -**Syntax**: mailfrom _boolean_
-**Default**: no +--- + +### mailfrom _boolean_ +Default: `no` Whether to check domain part of the MAIL FROM address against the list. This works correctly only with domain-based DNSBLs. -**Syntax**: responses _cidr|ip..._
-**Default**: 127.0.0.1/24 +--- + +### responses _cidr_ | _ip..._ +Default: `127.0.0.1/24` IP networks (in CIDR notation) or addresses to permit in list lookup results. Addresses not matching any entry in this directives will be ignored. -**Syntax**: score _integer_
-**Default**: 1 +--- + +### score _integer_ +Default: `1` Score value to add for the message if it is listed. -If sum of list scores is equals or higher than quarantine\_threshold, the +If sum of list scores is equals or higher than `quarantine_threshold`, the message will be quarantined. -If sum of list scores is equals or higher than rejected\_threshold, the message +If sum of list scores is equals or higher than `rejected_threshold`, the message will be rejected. It is possible to specify a negative value to make list act like a whitelist diff --git a/docs/reference/checks/milter.md b/docs/reference/checks/milter.md index 4f597636..8286a79b 100644 --- a/docs/reference/checks/milter.md +++ b/docs/reference/checks/milter.md @@ -32,15 +32,17 @@ via. See below. ## Configuration directives -***Syntax:*** endpoint _scheme://path_
-***Default:*** not set +### endpoint _scheme://path_ +Default: not set Specifies milter protocol endpoint to use. The endpoit is specified in standard URL-like format: -'tcp://127.0.0.1:6669' or 'unix:///var/lib/milter/filter.sock' +`tcp://127.0.0.1:6669` or `unix:///var/lib/milter/filter.sock` -***Syntax:*** fail\_open _boolean_
-***Default:*** false +--- + +### fail_open _boolean_ +Default: `false` Toggles behavior on milter I/O errors. If false ("fail closed") - message is rejected with temporary error code. If true ("fail open") - check is skipped. diff --git a/docs/reference/checks/misc.md b/docs/reference/checks/misc.md index 25e1ff63..19c71ad0 100644 --- a/docs/reference/checks/misc.md +++ b/docs/reference/checks/misc.md @@ -4,40 +4,45 @@ Following directives are defined for all modules listed below. -**Syntax**:
-fail\_action ignore
-fail\_action reject
-fail\_action quarantine
-**Default**: quarantine +### fail_action `ignore` | `reject` | `quarantine` +Default: `quarantine` -Action to take when check fails. See Check actions for details. +Action to take when check fails. See [Check actions](../actions/) for details. -**Syntax**: debug _boolean_
-**Default**: global directive value +--- + +### debug _boolean_ +Default: global directive value Log both successful and unsuccessful check executions instead of just unsuccessful. -## require\_mx\_record +--- + +### require_mx_record Check that domain in MAIL FROM command does have a MX record and none of them are "null" (contain a single dot as the host). By default, quarantines messages coming from servers missing MX records, -use 'fail\_action' directive to change that. +use `fail_action` directive to change that. -## require\_matching\_rdns +--- + +### require_matching_rdns Check that source server IP does have a PTR record point to the domain specified in EHLO/HELO command. By default, quarantines messages coming from servers with mismatched or missing -PTR record, use 'fail\_action' directive to change that. +PTR record, use `fail_action` directive to change that. + +--- -## require\_tls +### require_tls Check that the source server is connected via TLS; either directly, or by using the STARTTLS command. By default, rejects messages coming from unencrypted servers. Use the -'fail\_action' directive to change that. \ No newline at end of file +`fail_action` directive to change that. \ No newline at end of file diff --git a/docs/reference/checks/rspamd.md b/docs/reference/checks/rspamd.md index cf30d512..90063ae9 100644 --- a/docs/reference/checks/rspamd.md +++ b/docs/reference/checks/rspamd.md @@ -22,58 +22,76 @@ rspamd http://127.0.0.1:11333 ## Configuration directives -**Syntax:** tls\_client { ... }
-**Default:** not set +### tls_client { ... } +Default: not set Configure TLS client if HTTPS is used. See [TLS configuration / Client](/reference/tls/#client) for details. -**Syntax:** api\_path _url_
-**Default:** http://127.0.0.1:11333 +--- + +### api_path _url_ +Default: `http://127.0.0.1:11333` URL of HTTP API endpoint. Supports both HTTP and HTTPS and can include path element. -**Syntax:** settings\_id _string_
-**Default:** not set +--- + +### settings_id _string_ +Default: not set Settings ID to pass to the server. -**Syntax:** tag _string_
-**Default:** maddy +--- + +### tag _string_ +Default: `maddy` Value to send in MTA-Tag header field. -**Syntax:** hostname _string_
-**Default:** value of global directive +--- + +### hostname _string_
+Default: value of global directive Value to send in MTA-Name header field. -**Syntax:** io\_error\_action _action_
-**Default:** ignore +--- + +### io_error_action _action_ +Default: `ignore` Action to take in case of inability to contact the rspamd server. -**Syntax:** error\_resp\_action _action_
-**Default:** ignore +--- + +### error_resp_action _action_ +Default: `ignore` Action to take in case of 5xx or 4xx response received from the rspamd server. -**Syntax:** add\_header\_action _action_
-**Default:** quarantine +--- + +### add_header_action _action_ +Default: `quarantine` Action to take when rspamd requests to "add header". X-Spam-Flag and X-Spam-Score are added to the header irregardless of value. -**Syntax:** rewrite\_subj\_action _action_
-**Default:** quarantine +--- + +### rewrite_subj_action _action_ +Default: `quarantine` Action to take when rspamd requests to "rewrite subject". X-Spam-Flag and X-Spam-Score are added to the header irregardless of value. -**Syntax:** flags _string list..._
-**Default:** pass\_all +--- + +### flags _string-list..._ +Default: `pass_all` Flags to pass to the rspamd server. See [https://rspamd.com/doc/architecture/protocol.html](https://rspamd.com/doc/architecture/protocol.html) for details. diff --git a/docs/reference/checks/spf.md b/docs/reference/checks/spf.md index 83bc81b0..f0afb347 100644 --- a/docs/reference/checks/spf.md +++ b/docs/reference/checks/spf.md @@ -14,12 +14,12 @@ Authentication-Results field is generated irregardless of status. It is recommended by the DMARC standard to don't fail delivery based solely on SPF policy and always check DMARC policy and take action based on it. -If enforce\_early is no, check.spf module will not take any action on SPF +If `enforce_early` is `no`, check.spf module will not take any action on SPF policy failure if sender domain does have a DMARC record with 'quarantine' or 'reject' policy. Instead it will rely on DMARC support to take necesary actions using SPF results as an input. -Disabling enforce\_early without enabling DMARC support will make SPF policies +Disabling `enforce_early` without enabling DMARC support will make SPF policies no-op and is considered insecure. ## Configuration directives @@ -35,49 +35,63 @@ check.spf { } ``` -**Syntax**: debug _boolean_
-**Default**: global directive value +### debug _boolean_ +Default: global directive value Enable verbose logging for check.spf. -**Syntax**: enforce\_early _boolean_
-**Default**: no +--- + +### enforce_early _boolean_ +Default: `no` Make policy decision on MAIL FROM stage (before the message body is received). This makes it impossible to apply DMARC override (see above). -**Syntax**: none\_action reject|quarantine|ignore
-**Default**: ignore +--- + +### none_action `reject` | `quarantine` | `ignore` +Default: `ignore` Action to take when SPF policy evaluates to a 'none' result. See [https://tools.ietf.org/html/rfc7208#section-2.6](https://tools.ietf.org/html/rfc7208#section-2.6) for meaning of SPF results. -**Syntax**: neutral\_action reject|quarantine|ignore
-**Default**: ignore +--- + +### neutral_action `reject` | `quarantine` | `ignore` +Default: `ignore` Action to take when SPF policy evaluates to a 'neutral' result. See [https://tools.ietf.org/html/rfc7208#section-2.6](https://tools.ietf.org/html/rfc7208#section-2.6) for meaning of SPF results. -**Syntax**: fail\_action reject|quarantine|ignore
-**Default**: quarantine +--- + +### fail_action `reject` | `quarantine` | `ignore` +Default: `quarantine` Action to take when SPF policy evaluates to a 'fail' result. -**Syntax**: softfail\_action reject|quarantine|ignore
-**Default**: ignore +--- + +### softfail_action `reject` | `quarantine` | `ignore` +Default: `ignore` Action to take when SPF policy evaluates to a 'softfail' result. -**Syntax**: permerr\_action reject|quarantine|ignore
-**Default**: reject +--- + +### permerr_action `reject` | `quarantine` | `ignore` +Default: `reject` Action to take when SPF policy evaluates to a 'permerror' result. -**Syntax**: temperr\_action reject|quarantine|ignore
-**Default**: reject +--- + +### temperr_action `reject` | `quarantine` | `ignore` +Default: `reject` Action to take when SPF policy evaluates to a 'temperror' result. diff --git a/docs/reference/config-syntax.md b/docs/reference/config-syntax.md index 506ff023..72e18d41 100644 --- a/docs/reference/config-syntax.md +++ b/docs/reference/config-syntax.md @@ -182,21 +182,15 @@ Also note that the following is not valid, unlike Duration values syntax: 32M5K ``` -# ADDRESS DEFINITIONS +## Address Definitions Maddy configuration uses URL-like syntax to specify network addresses. -- unix://file\_path - Unix domain socket. Relative paths are relative to runtime directory - (/run/maddy). +- `unix://file_path` – Unix domain socket. Relative paths are relative to runtime directory (`/run/maddy`). +- `tcp://ADDRESS:PORT` – TCP/IP socket. +- `tls://ADDRESS:PORT` – TCP/IP socket using TLS. -- tcp://ADDRESS:PORT - TCP/IP socket. - -- tls://ADDRESS:PORT - TCP/IP socket using TLS. - -# DUMMY MODULE +## Dummy Module No-op module. It doesn't need to be configured explicitly and can be referenced using "dummy" name. It can act as a delivery target or auth. diff --git a/docs/reference/endpoints/imap.md b/docs/reference/endpoints/imap.md index 41e4f2f3..86904941 100644 --- a/docs/reference/endpoints/imap.md +++ b/docs/reference/endpoints/imap.md @@ -27,11 +27,12 @@ imap tcp://0.0.0.0:143 tls://0.0.0.0:993 { } ``` -**Syntax**: tls _certificate\_path_ _key\_path_ { ... }
-**Default**: global directive value +### tls _certificate-path_ _key-path_ { ... } +Default: global directive value TLS certificate & key to use. Fine-tuning of other TLS properties is possible by specifying a configuration block and options inside it: + ``` tls cert.crt key.key { protocols tls1.2 tls1.3 @@ -40,36 +41,50 @@ tls cert.crt key.key { See [TLS configuration / Server](/reference/tls/#server-side) for details. -**Syntax**: io\_debug _boolean_
-**Default**: no +--- + +### io_debug _boolean_ +Default: `no` Write all commands and responses to stderr. -**Syntax**: io\_errors _boolean_
-**Default**: no +--- + +### io_errors _boolean_ +Default: `no` Log I/O errors. -**Syntax**: debug _boolean_
-**Default**: global directive value +--- + +### debug _boolean_ +Default: global directive value Enable verbose logging. -**Syntax**: insecure\_auth _boolean_
-**Default**: no (yes if TLS is disabled) +--- -**Syntax**: auth _module\_reference\_ +### insecure_auth _boolean_ +Default: `no` (`yes` if TLS is disabled) -Use the specified module for authentication. +--- + +### auth _module-reference_ **Required.** -**Syntax**: storage _module\_reference\_ +Use the specified module for authentication. -Use the specified module for message storage. +--- + +### storage _module-reference_ **Required.** -**Syntax**: storage\_map _module\_reference_
-**Default**: identity +Use the specified module for message storage. + +--- + +### storage_map _module-reference_ +Default: `identity` Use the specified table to map SASL usernames to storage account names. @@ -78,6 +93,7 @@ Before username is looked up, it is normalized using function defined by This directive is useful if you want users user@example.org and user@example.com to share the same storage account named "user". In this case, use + ``` storage_map email_localpart ``` @@ -88,6 +104,7 @@ authentication provider. It also does not affect how message delivery is handled, you should specify `delivery_map` in storage module to define how to map email addresses to storage accounts. E.g. + ``` storage.imapsql local_mailboxes { ... @@ -95,13 +112,17 @@ to storage accounts. E.g. } ``` -**Syntax**: storage\_map_normalize _function_
-**Default**: auto +--- + +### storage_map_normalize _function_ +Default: `auto` Same as `auth_map_normalize` but for `storage_map`. -**Syntax**: auth\_map_normalize _function_
-**Default**: auto +--- + +### auth_map_normalize _function_ +Default: `auto` Overrides global `auth_map_normalize` value for this endpoint. diff --git a/docs/reference/endpoints/openmetrics.md b/docs/reference/endpoints/openmetrics.md index df77665c..f455f716 100644 --- a/docs/reference/endpoints/openmetrics.md +++ b/docs/reference/endpoints/openmetrics.md @@ -4,6 +4,7 @@ Various server statistics are provided in OpenMetrics format by the "openmetrics" module. To enable it, add the following line to the server config: + ``` openmetrics tcp://127.0.0.1:9749 { } ``` diff --git a/docs/reference/endpoints/smtp.md b/docs/reference/endpoints/smtp.md index 6ddbba1f..6b412868 100644 --- a/docs/reference/endpoints/smtp.md +++ b/docs/reference/endpoints/smtp.md @@ -36,8 +36,8 @@ smtp tcp://0.0.0.0:25 { ## Configuration directives -**Syntax**: hostname _string_
-**Default**: global directive value +### hostname _string_ +Default: global directive value Server name to use in SMTP banner. @@ -45,11 +45,14 @@ Server name to use in SMTP banner. 220 example.org ESMTP Service Ready ``` -**Syntax**: tls _certificate\_path_ _key\_path_ { ... }
-**Default**: global directive value +--- + +### tls _certificate-path_ _key-path_ { ... } +Default: global directive value TLS certificate & key to use. Fine-tuning of other TLS properties is possible by specifying a configuration block and options inside it: + ``` tls cert.crt key.key { protocols tls1.2 tls1.3 @@ -58,94 +61,107 @@ tls cert.crt key.key { See [TLS configuration / Server](/reference/tls/#server-side) for details. +--- -**Syntax**: io\_debug _boolean_
-**Default**: no +### io_debug _boolean_ +Default: `no` Write all commands and responses to stderr. -**Syntax**: debug _boolean_
-**Default**: global directive value +--- + +### debug _boolean_ +Default: global directive value Enable verbose logging. -**Syntax**: insecure\_auth _boolean_
-**Default**: no (yes if TLS is disabled) +--- + +### insecure_auth _boolean_ +Default: `no` (`yes` if TLS is disabled) Allow plain-text authentication over unencrypted connections. Not recommended! -**Syntax**: read\_timeout _duration_
-**Default**: 10m +--- + +### read_timeout _duration_ +Default: `10m` I/O read timeout. -**Syntax**: write\_timeout _duration_
-**Default**: 1m +--- + +### write_timeout _duration_ +Default: `1m` I/O write timeout. -**Syntax**: max\_message\_size _size_
-**Default**: 32M +--- + +### max_message_size _size_ +Default: `32M` Limit the size of incoming messages to 'size'. -**Syntax**: max\_header\_size _size_
-**Default**: 1M +--- + +### max_header_size _size_ +Default: `1M` Limit the size of incoming message headers to 'size'. -**Syntax**: auth _module\_reference_
-**Default**: not specified +--- + +### auth _module-reference_ +Default: not specified Use the specified module for authentication. -**Syntax**: defer\_sender\_reject _boolean_
-**Default**: yes +--- + +### defer_sender_reject _boolean_ +Default: `yes` Apply sender-based checks and routing logic when first RCPT TO command is received. This allows maddy to log recipient address of the rejected message and also improves interoperability with (improperly implemented) clients that don't expect an error early in session. -**Syntax**: max\_logged\_rcpt\_errors _integer_
-**Default**: 5 +--- + +### max_logged_rcpt_errors _integer_ +Default: `5` Amount of RCPT-time errors that should be logged. Further errors will be handled silently. This is to prevent log flooding during email dictionary attacks (address probing). -**Syntax**: max\_received _integer_
-**Default**: 50 +--- + +### max_received _integer_ +Default: `50` Max. amount of Received header fields in the message header. If the incoming message has more fields than this number, it will be rejected with the permanent error 5.4.6 ("Routing loop detected"). -**Syntax**:
-buffer ram
-buffer fs _[path]_
-buffer auto _max\_size_ _[path]_
-**Default**: auto 1M StateDirectory/buffer +--- -Temporary storage to use for the body of accepted messages. +### buffer `ram`
buffer `fs` _path_
buffer `auto` _max-size_ _path_ +Default: `auto 1M StateDirectory/buffer` -- ram - -Store the body in RAM. - -- fs +Temporary storage to use for the body of accepted messages. -Write out the message to the FS and read it back as needed. +- `ram` – Store the body in RAM. +- `fs` – Write out the message to the FS and read it back as needed. _path_ can be omitted and defaults to StateDirectory/buffer. +- `auto` – Store message bodies smaller than `_max_size_` entirely in RAM, +otherwise write them out to the FS. _path_ can be omitted and defaults to `StateDirectory/buffer`. -- auto +--- -Store message bodies smaller than _max\_size_ entirely in RAM, otherwise write -them out to the FS. -_path_ can be omitted and defaults to StateDirectory/buffer. - -**Syntax**: smtp\_max\_line\_length _integer_
-**Default**: 4000 +### smtp_max_line_length _integer_ +Default: `4000` The maximum line length allowed in the SMTP input stream. If client sends a longer line - connection will be closed and message (if any) will be rejected @@ -157,26 +173,31 @@ to handle longer lines correctly but some senders may produce them. Unless BDAT extension is used by the sender, this limitation also applies to the message body. -**Syntax**: dmarc _boolean_
-**Default**: yes +--- + +### dmarc _boolean_ +Default: `yes` Enforce sender's DMARC policy. Due to implementation limitations, it is not a check module. -**NOTE**: Report generation is not implemented now. +**Note**: Report generation is not implemented now. -**NOTE**: DMARC needs SPF and DKIM checks to function correctly. +**Note**: DMARC needs SPF and DKIM checks to function correctly. Without these, DMARC check will not run. +--- + ## Rate & concurrency limiting -**Syntax**: limits _config block_
-**Default**: no limits +### limits { ... } +Default: no limits This allows configuring a set of message flow restrictions including max. concurrency and rate per-endpoint, per-source, per-destination. Limits are specified as directives inside the block: + ``` limits { all rate 20 @@ -186,16 +207,14 @@ limits { Supported limits: -- Rate limit - -**Syntax**: _scope_ rate _burst_ _[period]_
-Restrict the amount of messages processed in _period_ to _burst_ messages. -If period is not specified, 1 second is used. +### _scope_ rate _burst_ _period_ -- Concurrency limit +Rate limit. Restrict the amount of messages processed in _period_ to +_burst_ messages. If period is not specified, 1 second is used. -**Syntax**: _scope_ concurrency _max_
-Restrict the amount of messages processed in parallel to _max\_. +### _scope_ concurrency _max_ +Concurrency limit. Restrict the amount of messages processed in parallel +to _max_. For each supported limitation, _scope_ determines whether it should be applied for all messages ("all"), per-sender IP ("ip"), per-sender domain ("source") or @@ -212,6 +231,7 @@ on outbound messages, do so using 'limits' directive for the 'table.remote' modu It is possible to share limit counters between multiple endpoints (or any other modules). To do so define a top-level configuration block for module "limits" and reference it where needed using standard & syntax. E.g. + ``` limits inbound_limits { all rate 20 @@ -227,6 +247,7 @@ submission tls://0.0.0.0:465 { ... } ``` + Using an "all rate" restriction in such way means that no more than 20 messages can enter the server through both endpoints in one second. @@ -259,7 +280,6 @@ lmtp unix://lmtp.sock { ## Limitations of LMTP implementation - Can't be used with TCP. - - Delivery to 'sql' module storage is always atomic, either all recipients will succeed or none of them will. diff --git a/docs/reference/global-config.md b/docs/reference/global-config.md index db477bf5..db0ec1a3 100644 --- a/docs/reference/global-config.md +++ b/docs/reference/global-config.md @@ -5,26 +5,32 @@ configuration blocks and they are applied to all modules. Some directives can be overridden on per-module basis (e.g. hostname). -**Syntax**: state\_dir _path_
-**Default**: /var/lib/maddy +### state_dir _path_ +Default: `/var/lib/maddy` The path to the state directory. This directory will be used to store all persistent data and should be writable. -**Syntax**: runtime\_dir _path_
-**Default**: /run/maddy +--- + +### runtime_dir _path_ +Default: `/run/maddy` The path to the runtime directory. Used for Unix sockets and other temporary objects. Should be writable. -**Syntax**: hostname _domain_
-**Default**: not specified +--- + +### hostname _domain_ +Default: not specified Internet hostname of this mail server. Typicall FQDN is used. It is recommended to make sure domain specified here resolved to the public IP of the server. -**Syntax**: auth\_map _module\_reference_
-**Default**: identity +--- + +### auth_map _module-reference_ +Default: `identity` Use the specified table to translate SASL usernames before passing it to the authentication provider. @@ -38,9 +44,11 @@ should also use `storage_map` in IMAP config block to handle this. This directive is useful if used authentication provider does not support using emails as usernames but you still want users to have separate mailboxes on separate domains. In this case, use it with `email_localpart` table: + ``` auth_map email_localpart ``` + With this configuration, `user@example.org` and `user@example.com` will use `user` credentials when authenticating, but will access `user@example.org` and `user@example.com` mailboxes correspondingly. If you want to also accept @@ -49,17 +57,22 @@ With this configuration, `user@example.org` and `user@example.com` will use If you want `user@example.org` and `user@example.com` to have the same mailbox, also set `storage_map` in IMAP config block to use `email_localpart` (or `email_localpart_optional` if you want to also accept just "user"): + ``` storage_map email_localpart ``` + In this case you will need to create storage accounts without domain part in the name: + ``` maddy imap-acct create user # instead of user@example.org ``` -**Syntax**: auth\_map_normalize _function_
-**Default**: auto +--- + +### auth_map_normalize _function_ +Default: `auto` Normalization function to apply to SASL usernames before mapping them to storage accounts. @@ -74,17 +87,18 @@ Available options: - `casefold` Convert to lower case - `noop` Nothing -**Syntax**: autogenerated\_msg\_domain _domain_
-**Default**: not specified +--- + +### autogenerated_msg_domain _domain_ +Default: not specified Domain that is used in From field for auto-generated messages (such as Delivery Status Notifications). -**Syntax**:
-tls file _cert\_file_ _pkey\_file_
-tls _module reference_
-tls off
-**Default**: not specified +--- + +### tls `file` _cert-file_ _pkey-file_ | _module-reference_ | `off` +Default: not specified Default TLS certificate to use for all endpoints. @@ -96,40 +110,32 @@ version. See maddy-tls(5) for details. maddy uses reasonable cipher suites and TLS versions by default so you generally don't have to worry about it. -**Syntax**: tls\_client { ... }
-**Default**: not specified +--- + +### tls_client { ... } +Default: not specified This is optional block that specifies various TLS-related options to use when making outbound connections. See TLS client configuration for details on directives that can be used in it. maddy uses reasonable cipher suites and TLS versions by default so you generally don't have to worry about it. -**Syntax**:
-log _targets..._
-log off
-**Default**: stderr +--- + +### log _targets..._ | `off` +Default: `stderr` Write log to one of more "targets". The target can be one or the following: -- stderr - - Write logs to stderr. - -- stderr\_ts - - Write logs to stderr with timestamps. - -- syslog - - Send logs to the local syslog daemon. - -- _file path_ - - Write (append) logs to file. +- `stderr` – Write logs to stderr. +- `stderr_ts` – Write logs to stderr with timestamps. +- `syslog` – Send logs to the local syslog daemon. +- _file path_ – Write (append) logs to file. Example: + ``` log syslog /var/log/maddy.log ``` @@ -137,8 +143,10 @@ log syslog /var/log/maddy.log **Note:** Maddy does not perform log files rotation, this is the job of the logrotate daemon. Send SIGUSR1 to maddy process to make it reopen log files. -**Syntax**: debug _boolean_
-**Default**: no +--- + +### debug _boolean_ +Default: `no` Enable verbose logging for all modules. You don't need that unless you are reporting a bug. diff --git a/docs/reference/modifiers/dkim.md b/docs/reference/modifiers/dkim.md index 44e212ee..5672a7d4 100644 --- a/docs/reference/modifiers/dkim.md +++ b/docs/reference/modifiers/dkim.md @@ -13,10 +13,10 @@ key for the first domain will be used. If domain in envelope sender does not match any of loaded keys, message will not be signed. Additionally, for each messages From header is checked to match MAIL FROM and authorization identity (username sender is logged in as). -This can be controlled using require\_sender\_match directive. +This can be controlled using require_sender_match directive. Generated private keys are stored in unencrypted PKCS#8 format -in state_directory/dkim_keys (/var/lib/maddy/dkim_keys). +in state_directory/dkim_keys (`/var/lib/maddy/dkim_keys`). In the same directory .dns files are generated that contain public key for each domain formatted in the form of a DNS record. @@ -24,6 +24,7 @@ public key for each domain formatted in the form of a DNS record. domains and selector can be specified in arguments, so actual modify.dkim use can be shortened to the following: + ``` modify { dkim example.org selector @@ -48,34 +49,39 @@ modify.dkim { } ``` -**Syntax**: debug _boolean_
-**Default**: global directive value +### debug _boolean_ +Default: global directive value Enable verbose logging. -**Syntax**: domains _string list_
-**Default**: not specified +--- + +### domains _string-list_ +**Required**.
+Default: not specified -**REQUIRED.** ADministrative Management Domains (ADMDs) taking responsibility for messages. Should be specified either as a directive or as an argument. -**Syntax**: selector _string_
-**Default**: not specified +--- -**REQUIRED.** +### selector _string_ +**Required**.
+Default: not specified Identifier of used key within the ADMD. Should be specified either as a directive or as an argument. -**Syntax**: key\_path _string_
-**Default**: dkim\_keys/{domain}\\_{selector}.key +--- + +### key_path _string_ +Default: `dkim_keys/{domain}_{selector}.key` Path to private key. It should be in PKCS#8 format wrapped in PAM encoding. If key does not exist, it will be generated using algorithm specified -in newkey\_algo. +in newkey_algo. Placeholders '{domain}' and '{selector}' will be replaced with corresponding values from domain and selector directives. @@ -84,16 +90,19 @@ Additionally, keys in PKCS#1 ("RSA PRIVATE KEY") and RFC 5915 ("EC PRIVATE KEY") can be read by modify.dkim. Note, however that newly generated keys are always in PKCS#8. -**Syntax**: oversign\_fields _list..._
-**Default**: see below +--- + +### oversign_fields _list..._ +Default: see below Header fields that should be signed n+1 times where n is times they are present in the message. This makes it impossible to replace field value by prepending another field with the same name to the message. -Fields specified here don't have to be also specified in sign\_fields. +Fields specified here don't have to be also specified in `sign_fields`. Default set of oversigned fields: + - Subject - To - From @@ -107,14 +116,17 @@ Default set of oversigned fields: - Autocrypt - Openpgp -**Syntax**: sign\_fields _list..._
-**Default**: see below +--- + +### sign_fields _list..._ +Default: see below Header fields that should be signed n+1 times where n is times they are present in the message. For these fields, additional values can be prepended by intermediate relays, but existing values can't be changed. Default set of signed fields: + - List-Id - List-Help - List-Unsubscribe @@ -128,72 +140,86 @@ Default set of signed fields: - Resent-From - Resent-Cc -**Syntax**: header\_canon relaxed|simple
-**Default**: relaxed +--- + +### header_canon `relaxed` | `simple` +Default: `relaxed` -Canonicalization algorithm to use for header fields. With 'relaxed', whitespace within -fields can be modified without breaking the signature, with 'simple' no +Canonicalization algorithm to use for header fields. With `relaxed`, whitespace within +fields can be modified without breaking the signature, with `simple` no modifications are allowed. -**Syntax**: body\_canon relaxed|simple
-**Default**: relaxed +--- -Canonicalization algorithm to use for message body. With 'relaxed', whitespace within -can be modified without breaking the signature, with 'simple' no +### body_canon `relaxed` | `simple` +Default: `relaxed` + +Canonicalization algorithm to use for message body. With `relaxed`, whitespace within +can be modified without breaking the signature, with `simple` no modifications are allowed. -**Syntax**: sig\_expiry _duration_
-**Default**: 120h +--- + +### sig_expiry _duration_ +Default: `120h` Time for which signature should be considered valid. Mainly used to prevent unauthorized resending of old messages. -**Syntax**: hash _hash_
-**Default**: sha256 +--- + +### hash _hash_ +Default: `sha256` Hash algorithm to use when computing body hash. sha256 is the only supported algorithm now. -**Syntax**: newkey\_algo rsa4096|rsa2048|ed25519
-**Default**: rsa2048 +--- + +### newkey_algo `rsa4096` | `rsa2048` | `ed25519` +Default: `rsa2048` Algorithm to use when generating a new key. -Currently ed25519 is NOT supported by most platforms. +Currently ed25519 is **not** supported by most platforms. -**Syntax**: require\_sender\_match _ids..._
-**Default**: envelope auth +--- + +### require_sender_match _ids..._ +Default: `envelope auth` Require specified identifiers to match From header field and key domain, otherwise - don't sign the message. If From field contains multiple addresses, message will not be -signed unless allow\_multiple\_from is also specified. In that +signed unless `allow_multiple_from` is also specified. In that case only first address will be compared. Matching is done in a case-insensitive way. Valid values: -- off - Disable check, always sign. -- envelope - Require MAIL FROM address to match From header. -- auth - If authorization identity contains @ - then require it to + +- `off` – Disable check, always sign. +- `envelope` – Require MAIL FROM address to match From header. +- `auth` – If authorization identity contains @ - then require it to fully match From header. Otherwise, check only local-part (username). -**Syntax**: allow\_multiple\_from _boolean_
-**Default**: no +--- + +### allow_multiple_from _boolean_ +Default: `no` Allow multiple addresses in From header field for purposes of -require\_sender\_match checks. Only first address will be checked, however. +`require_sender_match` checks. Only first address will be checked, however. + +--- -**Syntax**: sign\_subdomains _boolean_
-**Default**: no +### sign_subdomains _boolean_ +Default: `no` Sign emails from subdomains using a top domain key. -Allows only one domain to be specified (can be worked around by using modify.dkim +Allows only one domain to be specified (can be worked around by using `modify.dkim` multiple times). diff --git a/docs/reference/modifiers/envelope.md b/docs/reference/modifiers/envelope.md index 6615a71b..0e101cf6 100644 --- a/docs/reference/modifiers/envelope.md +++ b/docs/reference/modifiers/envelope.md @@ -1,6 +1,6 @@ # Envelope sender / recipient rewriting -'replace\_sender' and 'replace\_rcpt' modules replace SMTP envelope addresses +`replace_sender` and `replace_rcpt` modules replace SMTP envelope addresses based on the mapping defined by the table module (maddy-tables(5)). It is possible to specify 1:N mappings. This allows, for example, implementing mailing lists. @@ -17,6 +17,7 @@ multiple times to a single recipient. However, used delivery target can apply such deduplication (imapsql storage does it). Definition: + ``` replace_rcpt [table arguments] { [extended table config] @@ -27,6 +28,7 @@ replace_sender
[table arguments] { ``` Use examples: + ``` modify { replace_rcpt file /etc/maddy/aliases @@ -40,6 +42,7 @@ modify { ``` Possible contents of /etc/maddy/aliases in the example above: + ``` # Replace 'cat' with any domain to 'dog'. # E.g. cat@example.net -> dog@example.net diff --git a/docs/reference/smtp-pipeline.md b/docs/reference/smtp-pipeline.md index 728bab1e..a41deb80 100644 --- a/docs/reference/smtp-pipeline.md +++ b/docs/reference/smtp-pipeline.md @@ -16,40 +16,41 @@ The pipeline is responsible for message. Message handling flow is as follows: -- Execute checks referenced in top-level 'check' blocks (if any) +- Execute checks referenced in top-level `check` blocks (if any) -- Execute modifiers referenced in top-level 'modify' blocks (if any) +- Execute modifiers referenced in top-level `modify` blocks (if any) -- If there are 'source' blocks - select one that matches message sender (as - specified in MAIL FROM). If there are no 'source' blocks - entire - configuration is assumed to be the 'default\_source' block. +- If there are `source` blocks - select one that matches message sender (as + specified in MAIL FROM). If there are no `source` blocks - entire + configuration is assumed to be the `default_source` block. - Execute checks referenced in 'check' blocks inside selected 'source' block (if any). -- Execute modifiers referenced in 'modify' blocks inside selected 'source' +- Execute modifiers referenced in `modify` blocks inside selected `source` block (if any). Then, for each recipient: -- Select 'destination' block that matches it. If there are - no 'destination' blocks - entire used 'source' block is interpreted as if it - was a 'default\_destination' block. +- Select `destination` block that matches it. If there are + no `destination` blocks - entire used `source` block is interpreted as if it + was a `default_destination` block. -- Execute checks referenced in 'check' block inside selected 'destination' block +- Execute checks referenced in `check` block inside selected `destination` block (if any). -- Execute modifiers referenced in 'modify' block inside selected 'destination' +- Execute modifiers referenced in `modify` block inside selected `destination` block (if any). -- If used block contains 'reject' directive - reject the recipient with +- If used block contains `reject` directive - reject the recipient with specified SMTP status code. -- If used block contains 'deliver\_to' directive - pass the message to the +- If used block contains `deliver_to` directive - pass the message to the specified target module. Only recipients that are handled by used block are visible to the target. -Each recipient is handled only by a single 'destination' block, in case of -overlapping 'destination' - first one takes priority. +Each recipient is handled only by a single `destination` block, in case of +overlapping `destination` - first one takes priority. + ``` destination example.org { deliver_to targetA @@ -58,30 +59,34 @@ destination example.org { # ambiguous and thus not allowed deliver_to targetB } ``` -Same goes for 'source' blocks, each message is handled only by a single block. -Each recipient block should contain at least one 'deliver\_to' directive or -'reject' directive. If 'destination' blocks are used, then -'default\_destination' block should also be used to specify behavior for -unmatched recipients. Same goes for source blocks, 'default\_source' should be -used if 'source' is used. +Same goes for `source` blocks, each message is handled only by a single block. + +Each recipient block should contain at least one `deliver_to` directive or +`reject` directive. If `destination` blocks are used, then +`default_destination` block should also be used to specify behavior for +unmatched recipients. Same goes for source blocks, `default_source` should be +used if `source` is used. That is, pipeline configuration should explicitly specify behavior for each possible sender/recipient combination. -Additionally, directives that specify final handling decision ('deliver\_to', -'reject') can't be used at the same level as source/destination rules. +Additionally, directives that specify final handling decision (`deliver_to`, +`reject`) can't be used at the same level as source/destination rules. Consider example: + ``` destination example.org { deliver_to local_mboxes } reject ``` -It is not obvious whether 'reject' applies to all recipients or + +It is not obvious whether `reject` applies to all recipients or just for non-example.org ones, hence this is not allowed. Complete configuration example using all of the mentioned directives: + ``` check { # Run a check to make sure source SMTP server identification @@ -114,8 +119,9 @@ default_source { ## Directives -**Syntax**: check _block name_ { ... }
-**Context**: pipeline configuration, source block, destination block + +### check _block name_ { ... } +Context: pipeline configuration, source block, destination block List of the module references for checks that should be executed on messages handled by block where 'check' is placed in. @@ -126,6 +132,7 @@ be rejected for all recipients which is not what you usually want when using such configurations. Example: + ``` check { # Reference implicitly defined default configuration for check. @@ -141,6 +148,7 @@ check { It is also possible to define the block of checks at the top level as "checks" module and reference it using & syntax. Example: + ``` checks inbound_checks { spf @@ -154,9 +162,11 @@ checks inbound_checks { } ``` -**Syntax**: modify { ... }
-**Default**: not specified
-**Context**: pipeline configuration, source block, destination block +--- + +### modify { ... } +Default: not specified
+Context: pipeline configuration, source block, destination block List of the module references for modifiers that should be executed on messages handled by block where 'modify' is placed in. @@ -177,6 +187,7 @@ affect the message header will affect it for all recipients. It is also possible to define the block of modifiers at the top level as "modiifers" module and reference it using & syntax. Example: + ``` modifiers local_modifiers { replace_rcpt file /etc/maddy/aliases @@ -189,12 +200,10 @@ modifiers local_modifiers { } ``` -**Syntax**:
-reject _smtp\_code_ _smtp\_enhanced\_code_ _error\_description_
-reject _smtp\_code_ _smtp\_enhanced\_code_
-reject _smtp\_code_
-reject
-**Context**: destination block +--- + +### reject _smtp-code_ _smtp-enhanced-code_ _error-description_
reject _smtp-code_ _smtp-enhanced-code_
reject _smtp-code_
reject +Context: destination block Messages handled by the configuration block with this directive will be rejected with the specified SMTP error. @@ -203,30 +212,36 @@ If you aren't sure which codes to use, use 541 and 5.4.0 with your message or just leave all arguments out, the error description will say "message is rejected due to policy reasons" which is usually what you want to mean. -'reject' can't be used in the same block with 'deliver\_to' or -'destination/source' directives. +`reject` can't be used in the same block with `deliver_to` or +`destination`/`source` directives. Example: + ``` reject 541 5.4.0 "We don't like example.org, go away" ``` -**Syntax**: deliver\_to _target-config-block_
-**Context**: pipeline configuration, source block, destination block +--- + +### deliver_to _target-config-block_ +Context: pipeline configuration, source block, destination block Deliver the message to the referenced delivery target. What happens next is -defined solely by used target. If deliver\_to is used inside 'destination' +defined solely by used target. If `deliver_to` is used inside `destination` block, only matching recipients will be passed to the target. -**Syntax**: source\_in _table reference_ { ... }
-**Context**: pipeline configuration +--- + +### source_in _table-reference_ { ... } +Context: pipeline configuration Handle messages with envelope senders present in the specified table in accordance with the specified configuration block. -Takes precedence over all 'sender' directives. +Takes precedence over all `sender` directives. Example: + ``` source_in file /etc/maddy/banned_addrs { reject 550 5.7.0 "You are not welcome here" @@ -237,10 +252,12 @@ source example.org { ... ``` -See 'destination\_in' documentation for note about table configuration. +See `destination_in` documentation for note about table configuration. -**Syntax**: source _rules..._ { ... }
-**Context**: pipeline configuration +--- + +### source _rules..._ { ... } +Context: pipeline configuration Handle messages with MAIL FROM value (sender address) matching any of the rules in accordance with the specified configuration block. @@ -249,6 +266,7 @@ in accordance with the specified configuration block. 'rules', first one takes priority. Matching is case-insensitive. Example: + ``` # All messages coming from example.org domain will be delivered # to local_mailboxes. @@ -261,8 +279,10 @@ default_source { } ``` -**Syntax**: reroute { ... }
-**Context**: pipeline configuration, source block, destination block +--- + +### reroute { ... } +Context: pipeline configuration, source block, destination block This directive allows to make message routing decisions based on the result of modifiers. The block can contain all pipeline directives and they @@ -271,6 +291,7 @@ will use the final recipient and sender values (e.g. after all modifiers are applied). Here is the concrete example how it can be useful: + ``` destination example.org { modify { @@ -288,15 +309,17 @@ destination example.org { ``` This configuration allows to specify alias local addresses to remote ones -without being an open relay, since remote\_queue can be used only if remote +without being an open relay, since remote_queue can be used only if remote address was introduced as a result of rewrite of local address. -**WARNING**: If you have DMARC enabled (default), results generated by SPF +**Warning**: If you have DMARC enabled (default), results generated by SPF and DKIM checks inside a reroute block **will not** be considered in DMARC evaluation. -**Syntax**: destination\_in _table reference_ { ... }
-**Context**: pipeline configuration, source block +--- + +### destination_in _table-reference_ { ... } +Context: pipeline configuration, source block Handle messages with envelope recipients present in the specified table in accordance with the specified configuration block. @@ -304,6 +327,7 @@ accordance with the specified configuration block. Takes precedence over all 'destination' directives. Example: + ``` destination_in file /etc/maddy/remote_addrs { deliver_to smtp tcp://10.0.0.7:25 @@ -316,6 +340,7 @@ destination example.com { Note that due to the syntax restrictions, it is not possible to specify extended configuration for table module. E.g. this is not valid: + ``` destination_in sql_table { dsn ... @@ -327,6 +352,7 @@ destination_in sql_table { In this case, configuration should be specified separately and be referneced using '&' syntax: + ``` table.sql_table remote_addrs { dsn ... @@ -340,8 +366,10 @@ whatever { } ``` -**Syntax**: destination _rule..._ { ... }
-**Context**: pipeline configuration, source block +--- + +### destination _rule..._ { ... } +Context: pipeline configuration, source block Handle messages with RCPT TO value (recipient address) matching any of the rules in accordance with the specified configuration block. @@ -354,6 +382,7 @@ they have recipients matched by multiple blocks. Each block will see the message only with recipients matched by its rules. Example: + ``` # Messages with recipients at example.com domain will be # delivered to local_mailboxes target. @@ -370,9 +399,10 @@ default_destination { ## Reusable pipeline snippets (msgpipeline module) The message pipeline can be used independently of the SMTP module in other -contexts that require a delivery target via "msgpipeline" module. +contexts that require a delivery target via `msgpipeline` module. Example: + ``` msgpipeline local_routing { destination whatever.com { diff --git a/docs/reference/storage/imapsql.md b/docs/reference/storage/imapsql.md index 871be614..f1abbb37 100644 --- a/docs/reference/storage/imapsql.md +++ b/docs/reference/storage/imapsql.md @@ -3,7 +3,7 @@ The imapsql module implements database for IMAP index and message metadata using SQL-based relational database. -Message contents are stored in an "blob store" defined by msg\_store +Message contents are stored in an "blob store" defined by msg_store directive. By default this is a file system directory under /var/lib/maddy. Supported RDBMS: @@ -25,7 +25,7 @@ storage.imapsql { imapsql module also can be used as a lookup table. It returns empty string values for existing usernames. This might be useful -with destination\_in directive e.g. to implement catch-all +with `destination_in` directive e.g. to implement catch-all addresses (this is a bad idea to do so, this is just an example): ``` destination_in &local_mailboxes { @@ -46,20 +46,20 @@ Specify the driver and DSN. ## Configuration directives -**Syntax**: driver _string_
-**Default**: not specified - -REQUIRED. +### driver _string_ +**Required.**
+Default: not specified Use a specified driver to communicate with the database. Supported values: sqlite3, postgres. Should be specified either via an argument or via this directive. -**Syntax**: dsn _string_
-**Default**: not specified +--- -REQUIRED. +### dsn _string_ +**Required.**
+Default: not specified Data Source Name, the driver-specific value that specifies the database to use. @@ -68,118 +68,141 @@ For PostgreSQL: [https://godoc.org/github.com/lib/pq#hdr-Connection\_String\_Par Should be specified either via an argument or via this directive. -**Syntax**: msg\_store _store_
-**Default**: fs messages/ +--- + +### msg_store _store_ +Default: `fs messages/` Module to use for message bodies storage. See "Blob storage" section for what you can use here. -**Syntax**:
-compression off
-compression _algorithm_
-compression _algorithm_ _level_
-**Default**: off +--- + +### compression `off`
compression _algorithm_
compression _algorithm_ _level_ +Default: `off` Apply compression to message contents. -Supported algorithms: lz4, zstd. +Supported algorithms: `lz4`, `zstd`. + +--- -**Syntax**: appendlimit _size_
-**Default**: 32M +### appendlimit _size_ +Default: `32M` Don't allow users to add new messages larger than 'size'. This does not affect messages added when using module as a delivery target. -Use 'max\_message\_size' directive in SMTP endpoint module to restrict it too. +Use `max_message_size` directive in SMTP endpoint module to restrict it too. -**Syntax**: debug _boolean_
-**Default**: global directive value +--- + +### debug _boolean_ +Default: global directive value Enable verbose logging. -**Syntax**: junk\_mailbox _name_
-**Default**: Junk +--- + +### junk_mailbox _name_ +Default: `Junk` The folder to put quarantined messages in. Thishis setting is not used if user does have a folder with "Junk" special-use attribute. -**Syntax**: disable\_recent _boolean_
-*Default: true +--- + +### disable_recent _boolean_ +Default: `true` Disable RFC 3501-conforming handling of \Recent flag. This significantly improves storage performance when SQLite3 or CockroackDB is used at the cost of confusing clients that use this flag. -**Syntax**: sqlite\_cache\_size _integer_
-**Default**: defined by SQLite +--- + +### sqlite_cache_size _integer_ +Default: defined by SQLite SQLite page cache size. If positive - specifies amount of pages (1 page - 4 KiB) to keep in cache. If negative - specifies approximate upper bound of cache size in KiB. -**Syntax**: sqlite\_busy\_timeout _integer_
-**Default**: 5000000 +--- + +### sqlite_busy_timeout _integer_ +Default: `5000000` SQLite-specific performance tuning option. Amount of milliseconds to wait before giving up on DB lock. -**Syntax**: imap\_filter { ... }
-**Default**: not set +--- + +### imap_filter { ... } +Default: not set Specifies IMAP filters to apply for messages delivered from SMTP pipeline. Ex. + ``` imap_filter { command /etc/maddy/sieve.sh {account_name} } ``` -**Syntax:** delivery\_map **table**
-**Default:** identity +--- + +### delivery_map _table_ +Default: `identity` Use specified table module to map recipient addresses from incoming messages to mailbox names. -Normalization algorithm specified in delivery\_normalize is appied before -delivery\_map. +Normalization algorithm specified in `delivery_normalize` is appied before +`delivery_map`. + +--- -**Syntax:** delivery\_normalize _name_
-**Default:** precis\_casefold\_email +### delivery_normalize _name_ +Default: `precis_casefold_email` Normalization function to apply to email addresses before mapping them to mailboxes. -See auth\_normalize. +See `auth_normalize`. -**Syntax**: auth\_map **table**
-**Default**: identity +--- -**DEPRECATED:** Use `storage_map` in imap config instead. +### auth_map _table_ +**Deprecated:** Use `storage_map` in imap config instead.
+Default: `identity` Use specified table module to map authentication usernames to mailbox names. -Normalization algorithm specified in auth\_normalize is applied before -auth\_map. +Normalization algorithm specified in auth_normalize is applied before +auth_map. -**Syntax**: auth\_normalize _name_
-**Default**: precis\_casefold\_email +--- -**DEPRECATED:** Use `storage_map_normalize` in imap config instead. +### auth_normalize _name_ +**Deprecated:** Use `storage_map_normalize` in imap config instead.
+**Default**: `precis_casefold_email` Normalization function to apply to authentication usernames before mapping them to mailboxes. Available options: -- precis\_casefold\_email PRECIS UsernameCaseMapped profile + U-labels form for domain -- precis\_casefold PRECIS UsernameCaseMapped profile for the entire string -- precis\_email PRECIS UsernameCasePreserved profile + U-labels form for domain -- precis PRECIS UsernameCasePreserved profile for the entire string -- casefold Convert to lower case -- noop Nothing + +- `precis_casefold_email` PRECIS UsernameCaseMapped profile + U-labels form for domain +- `precis_casefold` PRECIS UsernameCaseMapped profile for the entire string +- `precis_email` PRECIS UsernameCasePreserved profile + U-labels form for domain +- `precis` PRECIS UsernameCasePreserved profile for the entire string +- `casefold` Convert to lower case +- `noop` Nothing Note: On message delivery, recipient address is unconditionally normalized -using precis\_casefold\_email function. +using `precis_casefold_email` function. diff --git a/docs/reference/table/chain.md b/docs/reference/table/chain.md index 12ef3be0..1cbc24c1 100644 --- a/docs/reference/table/chain.md +++ b/docs/reference/table/chain.md @@ -16,12 +16,14 @@ in /etc/maddy/emails list. ## Configuration directives -**Syntax**: step _table\_ +### step _table_ Adds a table module to the chain. If input value is not in the table (e.g. file) - return "not exists" error. -**Syntax**: optional\_step _table\_ +--- + +### optional_step _table_ Same as step but if input value is not in the table - it is passed to the next step without changes. @@ -29,6 +31,7 @@ next step without changes. Example: Something like this can be used to map emails to usernames after translating them via aliases map: + ``` table.chain { optional_step file /etc/maddy/aliases diff --git a/docs/reference/table/email_localpart.md b/docs/reference/table/email_localpart.md index a5fc81f2..19b90f19 100644 --- a/docs/reference/table/email_localpart.md +++ b/docs/reference/table/email_localpart.md @@ -1,11 +1,12 @@ # Email local part -The module 'table.email\_localpart' extracts and unescapes local ("username") part +The module `table.email_localpart` extracts and unescapes local ("username") part of the email address. E.g. -test@example.org => test -"test @ a"@example.org => test @ a + +* `test@example.org` => `test` +* `"test @ a"@example.org` => `test @ a` Mappings for invalid emails are not defined (will be treated as non-existing values). diff --git a/docs/reference/table/email_with_domains.md b/docs/reference/table/email_with_domains.md index 175e4511..c9a56b68 100644 --- a/docs/reference/table/email_with_domains.md +++ b/docs/reference/table/email_with_domains.md @@ -1,6 +1,6 @@ # Email with domain -The table module 'table.email\_with\_domain' appends one or more +The table module `table.email_with_domain` appends one or more domains (allowing 1:N expansion) to the specified value. ``` @@ -9,6 +9,7 @@ table.email_with_domains DOMAIN DOMAIN... { } It can be used to implement domain-level expansion for aliases if used together with `table.chain`. Example: + ``` modify { replace_rcpt chain { @@ -17,17 +18,20 @@ modify { } } ``` + This configuration will alias `anything@anydomain` to `anything@example.org` and `anything@example.com`. It is also useful with `authorize_sender` to authorize sending using multiple addresses under different domains if non-email usernames are used for authentication: + ``` check.authorize_sender { ... user_to_email email_with_domain example.org example.com } ``` + This way, user authenticated as `user` will be allowed to use `user@example.org` or `user@example.com` as a sender address. diff --git a/docs/reference/table/regexp.md b/docs/reference/table/regexp.md index 9a39b6fd..39e873db 100644 --- a/docs/reference/table/regexp.md +++ b/docs/reference/table/regexp.md @@ -18,8 +18,9 @@ table.regexp [replacement] { Note that [replacement] is optional. If it is not included - table.regexp will return the original string, therefore acting as a regexp match check. -This can be useful in combination in destination\_in for +This can be useful in combination in `destination_in` for advanced matching: + ``` destination_in regexp ".*-bounce+.*@example.com" { ... @@ -28,27 +29,31 @@ destination_in regexp ".*-bounce+.*@example.com" { ## Configuration directives -***Syntax***: full\_match _boolean_
-***Default***: yes +### full_match _boolean_ +Default: `yes` Whether to implicitly add start/end anchors to the regular expression. -That is, if 'full\_match' is yes, then the provided regular expression should -match the whole string. With no - partial match is enough. +That is, if `full_match` is `yes`, then the provided regular expression should +match the whole string. With `no` - partial match is enough. + +--- -***Syntax***: case\_insensitive _boolean_
-***Default***: yes +### case_insensitive _boolean_ +Default: `yes` Whether to make matching case-insensitive. -***Syntax***: expand\_placeholders _boolean_
-***Default***: yes +--- + +### expand_placeholders _boolean_ +Default: `yes` Replace '$name' and '${name}' in the replacement string with contents of corresponding capture groups from the match. To insert a literal $ in the output, use $$ in the template. -# Identity table (table.identity) +## Identity table (table.identity) The module 'identity' is a table module that just returns the key looked up. diff --git a/docs/reference/table/sql_query.md b/docs/reference/table/sql_query.md index c6eb3548..9b3b9ebf 100644 --- a/docs/reference/table/sql_query.md +++ b/docs/reference/table/sql_query.md @@ -1,8 +1,9 @@ # SQL query mapping -The table.sql\_query module implements table interface using SQL queries. +The table.sql_query module implements table interface using SQL queries. Definition: + ``` table.sql_query { driver @@ -19,6 +20,7 @@ table.sql_query { ``` Usage example: + ``` # Resolve SMTP address aliases using PostgreSQL DB. modify { @@ -32,22 +34,26 @@ modify { ## Configuration directives -***Syntax***: driver _driver name_
-***REQUIRED*** +### driver _driver name_ +**Required.** Driver to use to access the database. -Supported drivers: postgres, sqlite3 (if compiled with C support) +Supported drivers: `postgres`, `sqlite3` (if compiled with C support) -***Syntax***: dsn _data source name_
-***REQUIRED*** +--- + +### dsn _data source name_ +**Required.** Data Source Name to pass to the driver. For SQLite3 this is just a path to DB file. For Postgres, see [https://pkg.go.dev/github.com/lib/pq?tab=doc#hdr-Connection\_String\_Parameters](https://pkg.go.dev/github.com/lib/pq?tab=doc#hdr-Connection\_String\_Parameters) -***Syntax***: lookup _query_
-***REQUIRED*** +--- + +### lookup _query_ +**Required.** SQL query to use to obtain the lookup result. @@ -58,12 +64,15 @@ rows, they will be ignored. If there are more columns, lookup will fail. If there are no rows, lookup returns "no results". If there are any error - lookup will fail. -***Syntax***: init _queries..._
-***Default***: empty +--- + +### init _queries..._ +Default: empty List of queries to execute on initialization. Can be used to configure RDBMS. Example, to improve SQLite3 performance: + ``` table.sql_query { driver sqlite3 @@ -74,8 +83,10 @@ table.sql_query { } ``` -**Syntax:** named\_args _boolean_
-**Default:** yes +--- + +### named_args _boolean_ +Default: `yes` Whether to use named parameters binding when executing SQL queries or not. @@ -84,11 +95,10 @@ Note that maddy's PostgreSQL driver does not support named parameters and SQLite3 driver has issues handling numbered parameters: [https://github.com/mattn/go-sqlite3/issues/472](https://github.com/mattn/go-sqlite3/issues/472) -***Syntax:*** add _query_
-***Syntax:*** list _query_
-***Syntax:*** set _query_
-***Syntax:*** del _query_
-***Default:*** none +--- + +### add _query_
list _query_
set _query_
del _query_ +Default: none If queries are set to implement corresponding table operations - table becomes "mutable" and can be used in contexts that require writable key-value store. @@ -105,6 +115,6 @@ entry in the database. 'del' query gets :key argument - key and should remove it from the database. -If named\_args is set to "no" - key is passed as the first numbered parameter +If `named_args` is set to `no` - key is passed as the first numbered parameter (1ドル), value is passed as the second numbered parameter (2ドル). diff --git a/docs/reference/table/static.md b/docs/reference/table/static.md index ccee1d2e..e71b448f 100644 --- a/docs/reference/table/static.md +++ b/docs/reference/table/static.md @@ -13,7 +13,7 @@ table.static { ## Configuration directives -***Syntax***: entry _key_ _value\_ +### entry _key_ _value_ Add an entry to the table. diff --git a/docs/reference/targets/queue.md b/docs/reference/targets/queue.md index cc25db1a..373ff1a4 100644 --- a/docs/reference/targets/queue.md +++ b/docs/reference/targets/queue.md @@ -33,38 +33,45 @@ target.queue { } ``` -**Syntax**: target _block\_name_
-**Default**: not specified - -REQUIRED. +### target _block_name_ +**Required.**
+Default: not specified Delivery target to use for final delivery. -**Syntax**: location _directory_
-**Default**: StateDirectory/configuration\_block\_name +--- + +### location _directory_ +Default: `StateDirectory/configuration_block_name` File system directory to use to store queued messages. Relative paths are relative to the StateDirectory. -**Syntax**: max\_parallelism _integer_
-**Default**: 16 +--- + +### max_parallelism _integer_ +Default: `16` Start up to _integer_ goroutines for message processing. Basically, this option limits amount of messages tried to be delivered concurrently. -**Syntax**: max\_tries _integer_
-**Default**: 20 +--- + +### max_tries _integer_ +Default: `20` Attempt delivery up to _integer_ times. Note that no more attempts will be done is permanent error occurred during previous attempt. Delay before the next attempt will be increased exponentially using the -following formula: 15mins \* 1.2 ^ (n - 1) where n is the attempt number. +following formula: 15mins * 1.2 ^ (n - 1) where n is the attempt number. This gives you approximately the following sequence of delays: 18mins, 21mins, 25mins, 31mins, 37mins, 44mins, 53mins, 64mins, ... -**Syntax**: bounce { ... }
-**Default**: not specified +--- + +### bounce { ... } +Default: not specified This configuration contains pipeline configuration to be used for generated DSN (Delivery Status Notification) messages. @@ -72,13 +79,17 @@ This configuration contains pipeline configuration to be used for generated DSN If this is block is not present in configuration, DSNs will not be generated. Note, however, this is not what you want most of the time. -**Syntax**: autogenerated\_msg\_domain _domain_
-**Default**: global directive value +--- + +### autogenerated_msg_domain _domain_ +Default: global directive value Domain to use in sender address for DSNs. Should be specified too if 'bounce' block is specified. -**Syntax**: debug _boolean_
-**Default**: no +--- + +### debug _boolean_ +Default: `no` Enable verbose logging. \ No newline at end of file diff --git a/docs/reference/targets/remote.md b/docs/reference/targets/remote.md index f69a6de2..ea58d153 100644 --- a/docs/reference/targets/remote.md +++ b/docs/reference/targets/remote.md @@ -15,27 +15,33 @@ target.remote { } ``` -**Syntax**: hostname _domain_
-**Default**: global directive value +### hostname _domain_ +Default: global directive value Hostname to use client greeting (EHLO/HELO command). Some servers require it to be FQDN, SPF-capable servers check whether it corresponds to the server IP address, so it is better to set it to a domain that resolves to the server IP. -**Syntax**: limits _config block_
-**Default**: no limits +--- + +### limits { ... } +Default: no limits See ['limits' directive for SMTP endpoint](/reference/endpoints/smtp/#rate-concurrency-limiting). It works the same except for address domains used for per-source/per-destination are as observed when message exits the server. -**Syntax**: local\_ip _IP address_
-**Default**: empty +--- + +### local_ip _ip-address_ +Default: empty Choose the local IP to bind for outbound SMTP connections. -**Syntax**: force\_ipv4 _boolean_
-**Default**: false +--- + +### force_ipv4 _boolean_ +Default: `false` Force resolving outbound SMTP domains to IPv4 addresses. Some server providers do not offer a way to properly set reverse PTR domains for IPv6 addresses; this @@ -45,8 +51,10 @@ its IPv4 address. Warning: this may break sending outgoing mail to IPv6-only SMTP servers. -**Syntax**: connect\_timeout _duration_
-**Default**: 5m +--- + +### connect_timeout _duration_ +Default: `5m` Timeout for TCP connection establishment. @@ -56,8 +64,10 @@ lookup + TCP handshake) and another for "initial greeting". This directive configures the former. The latter is not configurable and is hardcoded to be 5 minutes. -**Syntax**: command\_timeout _duration_
-**Default**: 5m +--- + +### command_timeout _duration_ +Default: `5m` Timeout for any SMTP command (EHLO, MAIL, RCPT, DATA, etc). @@ -66,28 +76,36 @@ If STARTTLS is used this timeout also applies to TLS handshake. RFC 5321 recommends 5 minutes for MAIL/RCPT and 3 minutes for DATA. -**Syntax**: submission\_timeout _duration_
-**Default**: 12m +--- + +### submission_timeout _duration_ +Default: `12m` Time to wait after the entire message is sent (after "final dot"). RFC 5321 recommends 10 minutes. -**Syntax**: debug _boolean_
-**Default**: global directive value +--- + +### debug _boolean_ +Default: global directive value Enable verbose logging. -**Syntax**: requiretls\_override _boolean_
-**Default**: true +--- + +### requiretls_override _boolean_ +Default: `true` Allow local security policy to be disabled using 'TLS-Required' header field in sent messages. Note that the field has no effect if transparent forwarding is used, message body should be processed before outbound delivery starts for it to take effect (e.g. message should be queued using 'queue' module). -**Syntax**: relaxed\_requiretls _boolean_
-**Default**: true +--- + +### relaxed_requiretls _boolean_ +Default: `true` This option disables strict conformance with REQUIRETLS specification and allows forwarding of messages 'tagged' with REQUIRETLS to MXes that are not @@ -96,54 +114,66 @@ need to have support from all servers. It is based on the assumption that server referenced by MX record is likely the final destination and therefore there is only need to secure communication towards it and not beyond. -**Syntax**: conn\_reuse\_limit _integer_
-**Default**: 10 +--- + +### conn_reuse_limit _integer_ +Default: `10` Amount of times the same SMTP connection can be used. Connections are never reused if the previous DATA command failed. -**Syntax**: conn\_max\_idle\_count _integer_
-**Default**: 10 +--- + +### conn_max_idle_count _integer_ +Default: `10` Max. amount of idle connections per recipient domains to keep in cache. -**Syntax**: conn\_max\_idle\_time _integer_
-**Default**: 150 (2.5 min) +--- + +### conn_max_idle_time _integer_ +Default: `150` (2.5 min) Amount of time the idle connection is still considered potentially usable. +--- + ## Security policies -**Syntax**: mx\_auth _config block_
-**Default**: no policies +### mx_auth { ... } +Default: no policies 'remote' module implements a number of of schemes and protocols necessary to ensure security of message delivery. Most of these schemes are concerned with authentication of recipient server and TLS enforcement. -To enable mechanism, specify its name in the mx\_auth directive block: +To enable mechanism, specify its name in the `mx_auth` directive block: + ``` mx_auth { dane mtasts } ``` + Additional configuration is possible if supported by the mechanism by specifying additional options as a block for the corresponding mechanism. E.g. + ``` mtasts { cache ram } ``` -If the mx\_auth directive is not specified, no mechanisms are enabled. Note +If the `mx_auth` directive is not specified, no mechanisms are enabled. Note that, however, this makes outbound SMTP vulnerable to a numerous downgrade attacks and hence not recommended. It is possible to share the same set of policies for multiple 'remote' module -instances by defining it at the top-level using 'mx\_auth' module and then +instances by defining it at the top-level using `mx_auth` module and then referencing it using standard & syntax: + ``` mx_auth outbound_policy { dane @@ -166,6 +196,8 @@ deliver_to remote { } ``` +--- + ### MTA-STS Checks MTA-STS policy of the recipient domain. Provides proper authentication @@ -182,8 +214,8 @@ mtasts { } ``` -**Syntax**: cache fs|ram
-**Default**: fs +### cache `fs` | `ram` +Default: `fs` Storage to use for MTA-STS cache. 'fs' is to use a filesystem directory, 'ram' to store the cache in memory. @@ -192,11 +224,13 @@ It is recommended to use 'fs' since that will not discard the cache (and thus cause MTA-STS security to disappear) on server restart. However, using the RAM cache can make sense for high-load configurations with good uptime. -**Syntax**: fs\_dir _directory_
-**Default**: StateDirectory/mtasts\_cache +### fs_dir _directory_ +Default: `StateDirectory/mtasts_cache` Filesystem directory to use for policies caching if 'cache' is set to 'fs'. +--- + ### DNSSEC Checks whether MX records are signed. Sets MX level to "dnssec" is they are. @@ -213,6 +247,8 @@ have the /etc/resolv.conf file in the standard format. dnssec { } ``` +--- + ### DANE Checks TLSA records for the recipient MX. Provides downgrade-resistant TLS @@ -227,6 +263,8 @@ See above for notes on DNSSEC. DNSSEC support is required for DANE to work. dane { } ``` +--- + ### Local policy Checks effective TLS and MX levels (as set by other policies) against local @@ -239,17 +277,17 @@ local_policy { } ``` -Using 'local\_policy off' is equivalent to setting both directives to 'none'. +Using `local_policy off` is equivalent to setting both directives to `none`. -**Syntax**: min\_tls\_level none|encrypted|authenticated
-**Default**: none +### min_tls_level `none` | `encrypted` | `authenticated` +Default: `none` Set the minimal TLS security level required for all outbound messages. See [Security levels](../../seclevels) page for details. -**Syntax**: min\_mx\_level: none|mtasts|dnssec
-**Default**: none +### min_mx_level `none` | `mtasts` | `dnssec` +Default: `none` Set the minimal MX security level required for all outbound messages. diff --git a/docs/reference/targets/smtp.md b/docs/reference/targets/smtp.md index a7dec171..7ee56abd 100644 --- a/docs/reference/targets/smtp.md +++ b/docs/reference/targets/smtp.md @@ -3,6 +3,7 @@ Module that implements transparent forwarding of messages over SMTP. Use in pipeline configuration: + ``` deliver_to smtp tcp://127.0.0.1:5353 # or @@ -34,81 +35,81 @@ target.smtp { } ``` -**Syntax**: debug _boolean_
-**Default**: global directive value +### debug _boolean_ +Default: global directive value Enable verbose logging. -**Syntax**: tls\_client { ... }
-**Default**: not specified +--- + +### tls_client { ... } +Default: not specified Advanced TLS client configuration options. See [TLS configuration / Client](/reference/tls/#client) for details. -**Syntax**: attempt\_starttls _boolean_
-**Default**: yes (no for target.lmtp) +--- + +### attempt_starttls _boolean_ +Default: `yes` (`no` for `target.lmtp`) Attempt to use STARTTLS if it is supported by the remote server. If TLS handshake fails, connection will be retried without STARTTLS -unless 'require\_tls' is also specified. +unless `require_tls` is also specified. + +--- -**Syntax**: require\_tls _boolean_
-**Default**: no +### require_tls _boolean_ +Default: `no` Refuse to pass messages over plain-text connections. -**Syntax**:
-auth off
-plain _username_ _password_
-forward
-external
-**Default**: off +--- + +### auth `off` | `plain` _username_ _password_ | `forward` | `external` +Default: `off` Specify the way to authenticate to the remote server. Valid values: -- off - - No authentication. - -- plain - - Authenticate using specified username-password pair. - **Don't use** this without enforced TLS ('require\_tls'). - -- forward - - Forward credentials specified by the client. - **Don't use** this without enforced TLS ('require\_tls'). - -- external - - Request "external" SASL authentication. This is usually used for +- `off` – No authentication. +- `plain` – Authenticate using specified username-password pair. + **Don't use** this without enforced TLS (`require_tls`). +- `forward` – Forward credentials specified by the client. + **Don't use** this without enforced TLS (`require_tls`). +- `external` – Request "external" SASL authentication. This is usually used for authentication using TLS client certificates. See [TLS configuration / Client](/reference/tls/#client) for details. -**Syntax**: targets _endpoints..._
-**Default:** not specified +--- -REQUIRED. +### targets _endpoints..._ +**Required.**
+Default: not specified List of remote server addresses to use. See [Address definitions](/reference/config-syntax/#address-definitions) -for syntax to use. Basically, it is 'tcp://ADDRESS:PORT' -for plain SMTP and 'tls://ADDRESS:PORT' for SMTPS (aka SMTP with Implicit +for syntax to use. Basically, it is `tcp://ADDRESS:PORT` +for plain SMTP and `tls://ADDRESS:PORT` for SMTPS (aka SMTP with Implicit TLS). Multiple addresses can be specified, they will be tried in order until connection to one succeeds (including TLS handshake if TLS is required). -**Syntax**: connect\_timeout _duration_
-**Default**: 5m +--- + +### connect_timeout _duration_ +Default: `5m` Same as for target.remote. -**Syntax**: command\_timeout _duration_
-**Default**: 5m +--- + +### command_timeout _duration_ +Default: `5m` Same as for target.remote. -**Syntax**: submission\_timeout _duration_
-**Default**: 12m +--- + +### submission_timeout _duration_ +Default: `12m` Same as for target.remote. \ No newline at end of file diff --git a/docs/reference/tls-acme.md b/docs/reference/tls-acme.md index 3dc803a5..930e390c 100644 --- a/docs/reference/tls-acme.md +++ b/docs/reference/tls-acme.md @@ -2,9 +2,10 @@ Maddy supports obtaining certificates using ACME protocol. -To use it, create a configuration name for tls.loader.acme +To use it, create a configuration name for `tls.loader.acme` and reference it from endpoints that should use automatically configured certificates: + ``` tls.loader.acme local_tls { email put-your-email-here@example.org @@ -17,8 +18,10 @@ smtp tcp://127.0.0.1:25 { ... } ``` + You can also use a global `tls` directive to use automatically obtained certificates for all endpoints: + ``` tls { loader acme { @@ -29,8 +32,9 @@ tls { } ``` -Currently the only supported challenge is dns-01 one therefore +Currently the only supported challenge is `dns-01` one therefore you also need to configure the DNS provider: + ``` tls.loader.acme local_tls { email maddy-acme@example.org @@ -41,6 +45,7 @@ tls.loader.acme local_tls { } } ``` + See below for supported providers and necessary configuration for each. @@ -60,41 +65,52 @@ tls.loader.acme { } ``` -**Syntax:** debug _boolean_
-**Default:** global directive value +### debug _boolean_ +Default: global directive value Enable debug logging. -**Syntax:** hostname _str_
-**Default:** global directive value +--- + +### hostname _str_ +**Required.**
+Default: global directive value + +Domain name to issue certificate for. -Domain name to issue certificate for. Required. +--- -**Syntax:** store\_path _path_
-**Default:** state\_dir/acme +### store_path _path_ +Default: `state_dir/acme` Where to store issued certificates and associated metadata. Currently only filesystem-based store is supported. -**Syntax:** ca _url_
-**Default:** Let's Encrypt production CA +--- + +### ca _url_ +Default: Let's Encrypt production CA URL of ACME directory to use. -**Syntax:** test\_ca _url_
-**Default:** Let's Encrypt staging CA +--- + +### test_ca _url_ +Default: Let's Encrypt staging CA URL of ACME directory to use for retries should primary CA fail. maddy will keep attempting to issues certificates -using test\_ca until it succeeds then it will switch +using `test_ca` until it succeeds then it will switch back to the one configured via 'ca' option. This avoids rate limit issues with production CA. -**Syntax:** override\_domain _domain_
-**Default:** not set +--- + +### override_domain _domain_ +Default: not set Override the domain to set the TXT record on for DNS-01 challenge. This is to delegate the challenge to a different domain. @@ -102,18 +118,24 @@ This is to delegate the challenge to a different domain. See https://www.eff.org/deeplinks/2018/02/technical-deep-dive-securing-automation-acme-dns-challenge-validation for explanation why this might be useful. -**Syntax:** email _str_
-**Default:** not set +--- + +### email _str_ +Default: not set Email to pass while registering an ACME account. -**Syntax:** agreed _boolean_
-**Default:** false +--- + +### agreed _boolean_ +Default: false Whether you agreed to ToS of the CA service you are using. -**Syntax:** challenge dns-01
-**Default:** not set +--- + +### challenge `dns-01` +Default: not set Challenge(s) to use while performing domain verification. @@ -121,7 +143,7 @@ Challenge(s) to use while performing domain verification. Support for some providers is not provided by standard builds. To be able to use these, you need to compile maddy -with "libdns\_PROVIDER" build tag. +with "libdns_PROVIDER" build tag. E.g. ``` ./build.sh -tags 'libdns_googleclouddns' diff --git a/docs/reference/tls.md b/docs/reference/tls.md index 7f57820c..954b0e06 100644 --- a/docs/reference/tls.md +++ b/docs/reference/tls.md @@ -26,79 +26,73 @@ tls { ### Available certificate loaders -- file - - Accepts argument pairs specifying certificate and then key. - E.g. 'tls file certA.pem keyA.pem certB.pem keyB.pem' - +- `file` – Accepts argument pairs specifying certificate and then key. + E.g. `tls file certA.pem keyA.pem certB.pem keyB.pem`. If multiple certificates are listed, SNI will be used. - -- acme - - Automatically obtains a certificate using ACME protocol (Let's Encrypt) - -- off - - Not really a loader but a special value for tls directive, explicitly disables TLS for - endpoint(s). +- `acme` – Automatically obtains a certificate using ACME protocol (Let's Encrypt) +- `off` – Not really a loader but a special value for tls directive, + explicitly disables TLS for endpoint(s). ## Advanced TLS configuration -**Note: maddy uses secure defaults and TLS handshake is resistant to active downgrade attacks.** -**There is no need to change anything in most cases.** +**Note: maddy uses secure defaults and TLS handshake is resistant to active downgrade attacks. There is no need to change anything in most cases.** + +--- -**Syntax**:
-protocols _min\_version_ _max\_version_
-protocols _version_
-**Default**: tls1.0 tls1.3 +### protocols _min-version_ _max-version_ | _version_ +Default: `tls1.0 tls1.3` Minimum/maximum accepted TLS version. If only one value is specified, it will be the only one usable version. -Valid values are: tls1.0, tls1.1, tls1.2, tls1.3 +Valid values are: `tls1.0`, `tls1.1`, `tls1.2`, `tls1.3` -**Syntax**: ciphers _ciphers..._
-**Default**: Go version-defined set of 'secure ciphers', ordered by hardware +--- + +### ciphers _ciphers..._ +Default: Go version-defined set of 'secure ciphers', ordered by hardware performance List of supported cipher suites, in preference order. Not used with TLS 1.3. Valid values: -- RSA-WITH-RC4128-SHA -- RSA-WITH-3DES-EDE-CBC-SHA -- RSA-WITH-AES128-CBC-SHA -- RSA-WITH-AES256-CBC-SHA -- RSA-WITH-AES128-CBC-SHA256 -- RSA-WITH-AES128-GCM-SHA256 -- RSA-WITH-AES256-GCM-SHA384 -- ECDHE-ECDSA-WITH-RC4128-SHA -- ECDHE-ECDSA-WITH-AES128-CBC-SHA -- ECDHE-ECDSA-WITH-AES256-CBC-SHA -- ECDHE-RSA-WITH-RC4128-SHA -- ECDHE-RSA-WITH-3DES-EDE-CBC-SHA -- ECDHE-RSA-WITH-AES128-CBC-SHA -- ECDHE-RSA-WITH-AES256-CBC-SHA -- ECDHE-ECDSA-WITH-AES128-CBC-SHA256 -- ECDHE-RSA-WITH-AES128-CBC-SHA256 -- ECDHE-RSA-WITH-AES128-GCM-SHA256 -- ECDHE-ECDSA-WITH-AES128-GCM-SHA256 -- ECDHE-RSA-WITH-AES256-GCM-SHA384 -- ECDHE-ECDSA-WITH-AES256-GCM-SHA384 -- ECDHE-RSA-WITH-CHACHA20-POLY1305 -- ECDHE-ECDSA-WITH-CHACHA20-POLY1305 - -**Syntax**: curves _curves..._
-**Default**: defined by Go version +- `RSA-WITH-RC4128-SHA` +- `RSA-WITH-3DES-EDE-CBC-SHA` +- `RSA-WITH-AES128-CBC-SHA` +- `RSA-WITH-AES256-CBC-SHA` +- `RSA-WITH-AES128-CBC-SHA256` +- `RSA-WITH-AES128-GCM-SHA256` +- `RSA-WITH-AES256-GCM-SHA384` +- `ECDHE-ECDSA-WITH-RC4128-SHA` +- `ECDHE-ECDSA-WITH-AES128-CBC-SHA` +- `ECDHE-ECDSA-WITH-AES256-CBC-SHA` +- `ECDHE-RSA-WITH-RC4128-SHA` +- `ECDHE-RSA-WITH-3DES-EDE-CBC-SHA` +- `ECDHE-RSA-WITH-AES128-CBC-SHA` +- `ECDHE-RSA-WITH-AES256-CBC-SHA` +- `ECDHE-ECDSA-WITH-AES128-CBC-SHA256` +- `ECDHE-RSA-WITH-AES128-CBC-SHA256` +- `ECDHE-RSA-WITH-AES128-GCM-SHA256` +- `ECDHE-ECDSA-WITH-AES128-GCM-SHA256` +- `ECDHE-RSA-WITH-AES256-GCM-SHA384` +- `ECDHE-ECDSA-WITH-AES256-GCM-SHA384` +- `ECDHE-RSA-WITH-CHACHA20-POLY1305` +- `ECDHE-ECDSA-WITH-CHACHA20-POLY1305` + +--- + +### curves _curves..._ +Default: defined by Go version The elliptic curves that will be used in an ECDHE handshake, in preference order. -Valid values: p256, p384, p521, X25519. +Valid values: `p256`, `p384`, `p521`, `X25519`. ## Client -tls\_client directive allows to customize behavior of TLS client implementation, +`tls_client` directive allows to customize behavior of TLS client implementation, notably adjusting minimal and maximal TLS versions and allowed cipher suites, enabling TLS client authentication. @@ -114,42 +108,48 @@ tls_client { } ``` -**Syntax**:
-protocols _min\_version_ _max\_version_
-protocols _version_
-**Default**: tls1.0 tls1.3 +--- + +### protocols _min-version_ _max-version_ | _version_ +Default: `tls1.0 tls1.3` Minimum/maximum accepted TLS version. If only one value is specified, it will be the only one usable version. -Valid values are: tls1.0, tls1.1, tls1.2, tls1.3 +Valid values are: `tls1.0`, `tls1.1`, `tls1.2`, `tls1.3` + +--- -**Syntax**: ciphers _ciphers..._
-**Default**: Go version-defined set of 'secure ciphers', ordered by hardware +### ciphers _ciphers..._ +Default: Go version-defined set of 'secure ciphers', ordered by hardware performance List of supported cipher suites, in preference order. Not used with TLS 1.3. See TLS server configuration for list of supported values. -**Syntax**: curves _curves..._
-**Default**: defined by Go version +--- + +### curves _curves..._ +Default: defined by Go version The elliptic curves that will be used in an ECDHE handshake, in preference order. -Valid values: p256, p384, p521, X25519. +Valid values: `p256`, `p384`, `p521`, `X25519`. + +--- -**Syntax**: root\_ca _paths..._
-**Default**: system CA pool +### root_ca _paths..._ +Default: system CA pool List of files with PEM-encoded CA certificates to use when verifying server certificates. -**Syntax**:
-cert _cert\_path_
-key _key\_path_
-**Default**: not specified +--- + +### cert _cert-path_
key _key-path_ +Default: not specified Present the specified certificate when server requests a client certificate. Files should use PEM format. Both directives should be specified. From 05d2d9dfc9265459714d803ed192f93caadb66a3 Mon Sep 17 00:00:00 2001 From: fluidum <16622232+fluidum@users.noreply.github.com> Date: Tue, 4 Jul 2023 10:29:30 +0300 Subject: [PATCH 009/171] Update setting-up.md fixing docker reference --- docs/tutorials/setting-up.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/setting-up.md b/docs/tutorials/setting-up.md index 9e75d581..b528f3b4 100644 --- a/docs/tutorials/setting-up.md +++ b/docs/tutorials/setting-up.md @@ -47,7 +47,7 @@ Your options are: docker pull foxcpp/maddy:0.6 ``` - See [here](../docker) for Docker-specific instructions. + See [here](../../docker) for Docker-specific instructions. * Building from source From b3980ff715a430dda59fff3b84a40e25b8dfa01b Mon Sep 17 00:00:00 2001 From: fluidum <16622232+fluidum@users.noreply.github.com> Date: Tue, 4 Jul 2023 10:45:29 +0300 Subject: [PATCH 010/171] Update docker.md fixing reference --- docs/docker.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docker.md b/docs/docker.md index 7cbfea4a..2898a07b 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -72,4 +72,4 @@ docker run \ It will fail on first startup. Copy TLS certificate to /data/tls/fullchain.pem and key to /data/tls/privkey.pem. Run the server again. Finish DNS configuration -(DKIM keys, etc) as described in [tutorials/setting-up/](tutorials/setting-up/). +(DKIM keys, etc) as described in [tutorials/setting-up/](../tutorials/setting-up/). From 120067f7e7ccce2948352305ecfa34aa2e52b80a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: Tue, 8 Aug 2023 12:23:29 +0300 Subject: [PATCH 011/171] Replace email_with_domains -> email_with_domain in docs Fixes: #609 --- .mkdocs.yml | 2 +- docs/multiple-domains.md | 8 ++++---- .../table/{email_with_domains.md => email_with_domain.md} | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) rename docs/reference/table/{email_with_domains.md => email_with_domain.md} (89%) diff --git a/.mkdocs.yml b/.mkdocs.yml index 56d5b232..3e90eec1 100644 --- a/.mkdocs.yml +++ b/.mkdocs.yml @@ -60,7 +60,7 @@ nav: - reference/table/sql_query.md - reference/table/chain.md - reference/table/email_localpart.md - - reference/table/email_with_domains.md + - reference/table/email_with_domain.md - reference/table/auth.md - Authentication providers: - reference/auth/pass_table.md diff --git a/docs/multiple-domains.md b/docs/multiple-domains.md index f910213f..46fabf02 100644 --- a/docs/multiple-domains.md +++ b/docs/multiple-domains.md @@ -75,8 +75,8 @@ accept non-email usernames: authorize_sender { ... user_to_email chain { - step email_localpart_optional # remove domain from username if present - step email_with_domains $(local_domains) # expand username with all allowed domains + step email_localpart_optional # remove domain from username if present + step email_with_domain $(local_domains) # expand username with all allowed domains } } ``` @@ -141,8 +141,8 @@ mailboxes.** authorize_sender { ... user_to_email chain { - step email_localpart_optional # remove domain from username if present - step email_with_domains $(local_domains) # expand username with all allowed domains + step email_localpart_optional # remove domain from username if present + step email_with_domain $(local_domains) # expand username with all allowed domains } } } diff --git a/docs/reference/table/email_with_domains.md b/docs/reference/table/email_with_domain.md similarity index 89% rename from docs/reference/table/email_with_domains.md rename to docs/reference/table/email_with_domain.md index c9a56b68..6a719e0e 100644 --- a/docs/reference/table/email_with_domains.md +++ b/docs/reference/table/email_with_domain.md @@ -4,7 +4,7 @@ The table module `table.email_with_domain` appends one or more domains (allowing 1:N expansion) to the specified value. ``` -table.email_with_domains DOMAIN DOMAIN... { } +table.email_with_domain DOMAIN DOMAIN... { } ``` It can be used to implement domain-level expansion for aliases if used together @@ -14,7 +14,7 @@ with `table.chain`. Example: modify { replace_rcpt chain { step email_local_part - step email_with_domains example.org example.com + step email_with_domain example.org example.com } } ``` From 214b90dc9fc1d31ff1be91dbfe7bd59dfd937298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: Tue, 8 Aug 2023 12:36:13 +0300 Subject: [PATCH 012/171] Fix md formating and small grammar issues --- docs/reference/smtp-pipeline.md | 47 ++++++++++++++------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/docs/reference/smtp-pipeline.md b/docs/reference/smtp-pipeline.md index a41deb80..b094343f 100644 --- a/docs/reference/smtp-pipeline.md +++ b/docs/reference/smtp-pipeline.md @@ -2,54 +2,47 @@ # Message pipeline -Message pipeline is a set of module references and associated rules that +A message pipeline is a set of module references and associated rules that describe how to handle messages. The pipeline is responsible for -- Running message filters (called "checks"), (e.g. DKIM signature verification, - DNSBL lookup and so on). +- Running message filters (called "checks"), (e.g. DKIM signature verification, + DNSBL lookup, and so on). - Running message modifiers (e.g. DKIM signature creation). - -- Assocating each message recipient with one or more delivery targets. - Delivery target is a module that does final processing (delivery) of the +- Associating each message recipient with one or more delivery targets. + Delivery target is a module that does the final processing (delivery) of the message. Message handling flow is as follows: -- Execute checks referenced in top-level `check` blocks (if any) +- Execute checks referenced in top-level `check` blocks (if any) - Execute modifiers referenced in top-level `modify` blocks (if any) - -- If there are `source` blocks - select one that matches message sender (as - specified in MAIL FROM). If there are no `source` blocks - entire +- If there are `source` blocks - select one that matches the message sender (as + specified in MAIL FROM). If there are no `source` blocks - the entire configuration is assumed to be the `default_source` block. - -- Execute checks referenced in 'check' blocks inside selected 'source' block +- Execute checks referenced in `check` blocks inside the selected `source` block (if any). - - Execute modifiers referenced in `modify` blocks inside selected `source` block (if any). Then, for each recipient: -- Select `destination` block that matches it. If there are - no `destination` blocks - entire used `source` block is interpreted as if it - was a `default_destination` block. - -- Execute checks referenced in `check` block inside selected `destination` block - (if any). -- Execute modifiers referenced in `modify` block inside selected `destination` +- Select the `destination` block that matches it. If there are + no `destination` blocks - the entire used `source` block is interpreted as if it + was a `default_destination` block. +- Execute checks referenced in the `check` block inside the selected `destination` block (if any). - -- If used block contains `reject` directive - reject the recipient with - specified SMTP status code. - -- If used block contains `deliver_to` directive - pass the message to the +- Execute modifiers referenced in `modify` block inside the selected `destination` + block (if any). +- If the used block contains the `reject` directive - reject the recipient with + the specified SMTP status code. +- If the used block contains the `deliver_to` directive - pass the message to the specified target module. Only recipients that are handled - by used block are visible to the target. + by the used block are visible to the target. Each recipient is handled only by a single `destination` block, in case of -overlapping `destination` - first one takes priority. +overlapping `destination` - the first one takes priority. ``` destination example.org { From bfff2fa8d92aae6b0eae868012651dc36608f8dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: Tue, 8 Aug 2023 21:42:42 +0300 Subject: [PATCH 013/171] Update CI to use Go 1.19 --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index ad4ad081..7c8ab81d 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -24,7 +24,7 @@ jobs: restore-keys: ${{ runner.os }}-go- - uses: actions/setup-go@v2 with: - go-version: 1.18.9 + go-version: 1.19 - name: "Verify build.sh" run: | ./build.sh From 14f45e3f0142b3dd9643cac04e066db33d27626f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: 2023年8月14日 11:01:39 +0300 Subject: [PATCH 014/171] Update systemd services to depend on network-online.target Fixes: #616 --- dist/systemd/maddy.service | 2 +- dist/systemd/maddy@.service | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/systemd/maddy.service b/dist/systemd/maddy.service index 0f5ace27..b1598502 100644 --- a/dist/systemd/maddy.service +++ b/dist/systemd/maddy.service @@ -3,7 +3,7 @@ Description=maddy mail server Documentation=man:maddy(1) Documentation=man:maddy.conf(5) Documentation=https://maddy.email -After=network.target +After=network-online.target [Service] Type=notify diff --git a/dist/systemd/maddy@.service b/dist/systemd/maddy@.service index cc776820..015dcd60 100644 --- a/dist/systemd/maddy@.service +++ b/dist/systemd/maddy@.service @@ -3,7 +3,7 @@ Description=maddy mail server (using %i.conf) Documentation=man:maddy(1) Documentation=man:maddy.conf(5) Documentation=https://maddy.email -After=network.target +After=network-online.target [Service] Type=notify From dbb424f1649f36c8f0121b724ca72a57ea30520d Mon Sep 17 00:00:00 2001 From: Martin Matous Date: 2023年8月22日 17:52:22 +0200 Subject: [PATCH 015/171] fix(milter): remove erroneous path check A remnant of d0e7df023cadb3d7068e5b09509bc562ad63f10b when milter.NewClient() didn't accept path. Signed-off-by: Martin Matous --- internal/check/milter/milter.go | 3 -- internal/check/milter/milter_test.go | 61 ++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 internal/check/milter/milter_test.go diff --git a/internal/check/milter/milter.go b/internal/check/milter/milter.go index c0f3700d..37704d43 100644 --- a/internal/check/milter/milter.go +++ b/internal/check/milter/milter.go @@ -90,9 +90,6 @@ func (c *Check) Init(cfg *config.Map) error { default: return fmt.Errorf("%s: scheme unsupported: %v", modName, endp.Scheme) } - if endp.Path != "" { - return fmt.Errorf("%s: stray path in endpoint: %v", modName, endp) - } c.cl = milter.NewClientWithOptions(endp.Network(), endp.Address(), milter.ClientOptions{ Dialer: &net.Dialer{ diff --git a/internal/check/milter/milter_test.go b/internal/check/milter/milter_test.go new file mode 100644 index 00000000..d2ec19d3 --- /dev/null +++ b/internal/check/milter/milter_test.go @@ -0,0 +1,61 @@ +/* +Maddy Mail Server - Composable all-in-one email server. +Copyright © 2019-2020 Max Mazurov , Maddy Mail Server contributors + +This program 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. + +This program 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 . +*/ + +package milter + +import ( + "testing" + + "github.com/foxcpp/maddy/framework/config" +) + +func TestAcceptValidEndpoints(t *testing.T) { + for _, endpoint := range []string{ + "tcp://0.0.0.0:10025", + "tcp://[::]:10025", + "tcp:127.0.0.1:10025", + "unix://path", + "unix:path", + "unix:/path", + "unix:///path", + "unix://also/path", + "unix:///also/path", + } { + c := &Check{milterUrl: endpoint} + + err := c.Init(&config.Map{}) + if err != nil { + t.Errorf("Unexpected failure for %s: %v", endpoint, err) + return + } + } +} + +func TestRejectInvalidEndpoints(t *testing.T) { + for _, endpoint := range []string{ + "tls://0.0.0.0:10025", + "tls:0.0.0.0:10025", + } { + c := &Check{milterUrl: endpoint} + err := c.Init(&config.Map{}) + if err == nil { + t.Errorf("Accepted invalid endpoint: %s", endpoint) + return + } + } +} \ No newline at end of file From 4b64657ed16f3fcad229db19a25f24241a31a5cc Mon Sep 17 00:00:00 2001 From: Martin Matous Date: 2023年8月22日 18:00:44 +0200 Subject: [PATCH 016/171] fix(f2b): use correct retry directive Signed-off-by: Martin Matous --- dist/fail2ban/jail.d/maddy-dictonary-attack.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist/fail2ban/jail.d/maddy-dictonary-attack.conf b/dist/fail2ban/jail.d/maddy-dictonary-attack.conf index c4f7ff3f..ebeb33fa 100644 --- a/dist/fail2ban/jail.d/maddy-dictonary-attack.conf +++ b/dist/fail2ban/jail.d/maddy-dictonary-attack.conf @@ -2,6 +2,6 @@ port = 993,465,25 filter = maddy-dictonary-attack bantime = 72h -maxtries = 3 +maxretry = 3 findtime = 6h backend = systemd From 17b76d95e3b3b30b518c6cb71f82895655f6af92 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2023年8月23日 15:43:35 +0300 Subject: [PATCH 017/171] target/remote: Fix isVerifyError not working correctly on Go 1.20 On Go 1.20, *tls.CertificateVerificationError is returned that wraps x509 errors. See #612. --- internal/target/remote/connect.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/target/remote/connect.go b/internal/target/remote/connect.go index 8df36424..0824d4aa 100644 --- a/internal/target/remote/connect.go +++ b/internal/target/remote/connect.go @@ -22,6 +22,7 @@ import ( "context" "crypto/tls" "crypto/x509" + "errors" "net" "runtime/trace" "sort" @@ -65,20 +66,19 @@ func (c *mxConn) Close() error { } func isVerifyError(err error) bool { - _, ok := err.(x509.UnknownAuthorityError) - if ok { + if errors.As(err, &x509.UnknownAuthorityError{}) { return true } - _, ok = err.(x509.HostnameError) - if ok { + if errors.As(err, &x509.HostnameError{}) { return true } - _, ok = err.(x509.ConstraintViolationError) - if ok { + if errors.As(err, &x509.ConstraintViolationError{}) { return true } - _, ok = err.(x509.CertificateInvalidError) - return ok + if errors.As(err, &x509.CertificateInvalidError{}) { + return true + } + return false } // connect attempts to connect to the MX, first trying STARTTLS with X.509 From 8834501196cd5b5c89658afb11903b449b124ad4 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2023年8月23日 16:11:46 +0300 Subject: [PATCH 018/171] tests: Fix cover_test.go deadlock on Go 1.20 Test code seems to hang somewhere after maddy.Run if os.Stderr, os.Stdout are not consumed. --- tests/cover_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/cover_test.go b/tests/cover_test.go index ad86c535..d47c8840 100644 --- a/tests/cover_test.go +++ b/tests/cover_test.go @@ -38,6 +38,7 @@ https://github.com/albertito/chasquid/blob/master/coverage_test.go import ( "flag" + "io" "os" "testing" @@ -71,11 +72,14 @@ func TestMain(m *testing.M) { } // Silence output produced by "testing" runtime. - _, w, err := os.Pipe() + r, w, err := os.Pipe() if err == nil { os.Stderr = w os.Stdout = w } + go func() { + _, _ = io.ReadAll(r) + }() // Even though we do not have any tests to run, we need to call out into // "testing" to make it process flags and produce the coverage report. From 466906e8dcaf101062aa28268a25c7d30de7db45 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2023年8月23日 16:20:04 +0300 Subject: [PATCH 019/171] mxauth: Fix a few nits in MX auth code --- framework/module/mxauth.go | 6 ++++-- internal/target/remote/security.go | 12 +++++++----- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/framework/module/mxauth.go b/framework/module/mxauth.go index fb09e4c0..5226fb02 100644 --- a/framework/module/mxauth.go +++ b/framework/module/mxauth.go @@ -39,7 +39,9 @@ const ( TLSNone TLSLevel = iota TLSEncrypted TLSAuthenticated +) +const ( MXNone MXLevel = iota MX_MTASTS MX_DNSSEC @@ -113,11 +115,11 @@ type ( // CheckConn call. PrepareDomain(ctx context.Context, domain string) - // PrepareDomain is called before connection and may asynchronously + // PrepareConn is called before connection and may asynchronously // start additional lookups necessary for policy application in // CheckConn. // - // If there any errors - they should be deferred to the CheckConn + // If there are any errors - they should be deferred to the CheckConn // call. PrepareConn(ctx context.Context, mx string) diff --git a/internal/target/remote/security.go b/internal/target/remote/security.go index 1e21f1ee..a8177fb1 100644 --- a/internal/target/remote/security.go +++ b/internal/target/remote/security.go @@ -180,7 +180,7 @@ func (c *mtastsDelivery) CheckMX(ctx context.Context, mxLevel module.MXLevel, do return module.MXNone, &exterrors.SMTPError{ Code: 550, EnhancedCode: exterrors.EnhancedCode{5, 7, 0}, - Message: "Failed to establish the module.MX record authenticity (MTA-STS)", + Message: "Failed to establish the MX record authenticity (MTA-STS)", } } c.log.Msg("MX does not match published non-enforced MTA-STS policy", "mx", mx, "domain", c.domain) @@ -213,7 +213,7 @@ func (c *mtastsDelivery) CheckConn(ctx context.Context, mxLevel module.MXLevel, return module.TLSNone, &exterrors.SMTPError{ Code: 451, EnhancedCode: exterrors.EnhancedCode{4, 7, 1}, - Message: "Recipient server module.TLS certificate is not trusted but " + + Message: "Recipient server TLS certificate is not trusted but " + "authentication is required by MTA-STS", Misc: map[string]interface{}{ "tls_level": tlsLevel, @@ -608,9 +608,10 @@ func (l localPolicy) CheckMX(ctx context.Context, mxLevel module.MXLevel, domain // a temporary error (we can't know with the current design). Code: 451, EnhancedCode: exterrors.EnhancedCode{4, 7, 0}, - Message: "Failed to establish the module.MX record authenticity", + Message: "Failed to establish the MX record authenticity", Misc: map[string]interface{}{ - "mx_level": mxLevel, + "mx_level": mxLevel, + "required_mx_level": l.minMXLevel, }, } } @@ -624,7 +625,8 @@ func (l localPolicy) CheckConn(ctx context.Context, mxLevel module.MXLevel, tlsL EnhancedCode: exterrors.EnhancedCode{4, 7, 1}, Message: "TLS it not available or unauthenticated but required", Misc: map[string]interface{}{ - "tls_level": tlsLevel, + "tls_level": tlsLevel, + "required_tls_level": l.minTLSLevel, }, } } From b8ff1168a0a11a862c9a0416edddcba739b3a83b Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2023年8月23日 16:20:50 +0300 Subject: [PATCH 020/171] smtpconn/pool: Fix idle connections (almost) never cleaned up See #596. --- internal/smtpconn/pool/pool.go | 48 +++++++++++++++++++++++++-- internal/target/remote/remote.go | 4 +-- internal/target/remote/remote_test.go | 4 +-- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/internal/smtpconn/pool/pool.go b/internal/smtpconn/pool/pool.go index bbe059a8..35ab27ba 100644 --- a/internal/smtpconn/pool/pool.go +++ b/internal/smtpconn/pool/pool.go @@ -47,6 +47,8 @@ type P struct { cfg Config keys map[string]slot keysLock sync.Mutex + + cleanupStop chan struct{} } func New(cfg Config) *P { @@ -56,9 +58,46 @@ func New(cfg Config) *P { } } - return &P{ - cfg: cfg, - keys: make(map[string]slot, cfg.MaxKeys), + p := &P{ + cfg: cfg, + keys: make(map[string]slot, cfg.MaxKeys), + cleanupStop: make(chan struct{}), + } + + go p.cleanUpTick(p.cleanupStop) + + return p +} + +func (p *P) cleanUpTick(stop chan struct{}) { + ctx := context.Background() + tick := time.NewTicker(time.Minute) + defer tick.Stop() + + for { + select { + case <-tick.c: + p.CleanUp(ctx) + case <-stop: + return + } + } +} + +func (p *P) CleanUp(ctx context.Context) { + p.keysLock.Lock() + defer p.keysLock.Unlock() + + for k, v := range p.keys { + if v.lastUse+p.cfg.StaleKeyLifetimeSec> time.Now().Unix() { + continue + } + + close(v.c) + for conn := range v.c { + conn.Close() + } + delete(p.keys, k) } } @@ -95,6 +134,7 @@ func (p *P) Get(ctx context.Context, key string) (Conn, error) { } if !conn.Usable() { + conn.Close() continue } @@ -144,6 +184,8 @@ func (p *P) Return(key string, c Conn) { } func (p *P) Close() { + p.cleanupStop <- struct{}{} + p.keysLock.Lock() defer p.keysLock.Unlock() diff --git a/internal/target/remote/remote.go b/internal/target/remote/remote.go index 03b6f33a..69be9d10 100644 --- a/internal/target/remote/remote.go +++ b/internal/target/remote/remote.go @@ -142,8 +142,8 @@ func (rt *Target) Init(cfg *config.Map) error { cfg.Duration("submission_timeout", false, false, 5*time.Minute, &rt.submissionTimeout) poolCfg := pool.Config{ - MaxKeys: 20000, - MaxConnsPerKey: 10, // basically, max. amount of idle connections in cache + MaxKeys: 5000, + MaxConnsPerKey: 5, // basically, max. amount of idle connections in cache MaxConnLifetimeSec: 150, // 2.5 mins, half of recommended idle time from RFC 5321 StaleKeyLifetimeSec: 60 * 5, // should be bigger than MaxConnLifetimeSec } diff --git a/internal/target/remote/remote_test.go b/internal/target/remote/remote_test.go index ee7e88d3..d8064f67 100644 --- a/internal/target/remote/remote_test.go +++ b/internal/target/remote/remote_test.go @@ -62,8 +62,8 @@ func testTarget(t *testing.T, zones map[string]mockdns.Zone, extResolver *dns.Ex policies: extraPolicies, limits: &limits.Group{}, pool: pool.New(pool.Config{ - MaxKeys: 20000, - MaxConnsPerKey: 10, // basically, max. amount of idle connections in cache + MaxKeys: 5000, + MaxConnsPerKey: 5, // basically, max. amount of idle connections in cache MaxConnLifetimeSec: 150, // 2.5 mins, half of recommended idle time from RFC 5321 StaleKeyLifetimeSec: 60 * 5, // should be bigger than MaxConnLifetimeSec }), From d9920f0763cdaf6e49d015732b0de499299fbace Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2023年8月23日 16:55:04 +0300 Subject: [PATCH 021/171] tls/acme: Return certmagic.New config in GetConfigForCert Might have caused partially broken configurations e.g. #619. --- internal/tls/acme/acme.go | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/internal/tls/acme/acme.go b/internal/tls/acme/acme.go index 70eb05b8..34683c03 100644 --- a/internal/tls/acme/acme.go +++ b/internal/tls/acme/acme.go @@ -83,10 +83,7 @@ func (l *Loader) Init(cfg *config.Map) error { l.cache = certmagic.NewCache(certmagic.CacheOptions{ Logger: cmLog, GetConfigForCert: func(c certmagic.Certificate) (*certmagic.Config, error) { - return &certmagic.Config{ - Storage: l.store, - Logger: cmLog, - }, nil + return l.cfg, nil }, }) @@ -95,7 +92,7 @@ func (l *Loader) Init(cfg *config.Map) error { Logger: cmLog, DefaultServerName: hostname, }) - mngr := certmagic.NewACMEIssuer(l.cfg, certmagic.ACMEIssuer{ + issuer := certmagic.NewACMEIssuer(l.cfg, certmagic.ACMEIssuer{ Logger: cmLog, CA: caPath, Email: email, @@ -104,19 +101,19 @@ func (l *Loader) Init(cfg *config.Map) error { switch challenge { case "dns-01": - mngr.DisableTLSALPNChallenge = true - mngr.DisableHTTPChallenge = true + issuer.DisableTLSALPNChallenge = true + issuer.DisableHTTPChallenge = true if provider == nil { return fmt.Errorf("tls.loader.acme: dns-01 challenge requires a configured DNS provider") } - mngr.DNS01Solver = &certmagic.DNS01Solver{ + issuer.DNS01Solver = &certmagic.DNS01Solver{ DNSProvider: provider, OverrideDomain: overrideDomain, } default: return fmt.Errorf("tls.loader.acme: challenge not supported") } - l.cfg.Issuers = []certmagic.Issuer{mngr} + l.cfg.Issuers = []certmagic.Issuer{issuer} if module.NoRun { return nil From 5d8f09ebfebddf724531dd89de5a8ab9a4de0767 Mon Sep 17 00:00:00 2001 From: CUI Hao Date: Thu, 5 Oct 2023 00:01:02 -0700 Subject: [PATCH 022/171] Fix wrong DNS query type in DANE lookups for IPv6-only hosts --- framework/dns/dnssec.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/dns/dnssec.go b/framework/dns/dnssec.go index 74982bef..b8e9c19d 100644 --- a/framework/dns/dnssec.go +++ b/framework/dns/dnssec.go @@ -229,7 +229,7 @@ func (e ExtResolver) CheckCNAMEAD(ctx context.Context, host string) (ad bool, rn if rname == "" { // IPv6-only host? Try to find out rname using AAAA lookup. msg := new(dns.Msg) - msg.SetQuestion(dns.Fqdn(host), dns.TypeA) + msg.SetQuestion(dns.Fqdn(host), dns.TypeAAAA) msg.SetEdns0(4096, false) msg.AuthenticatedData = true resp, err := e.exchange(ctx, msg) From 6e40b0893262267f532617737d923a9f50a6b8bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20K=C3=A1n=C4=9B?= Date: Sun, 8 Oct 2023 14:17:59 +0200 Subject: [PATCH 023/171] doc: Fix links to seclevels page --- docs/reference/targets/remote.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/targets/remote.md b/docs/reference/targets/remote.md index ea58d153..ea6ee354 100644 --- a/docs/reference/targets/remote.md +++ b/docs/reference/targets/remote.md @@ -284,12 +284,12 @@ Default: `none` Set the minimal TLS security level required for all outbound messages. -See [Security levels](../../seclevels) page for details. +See [Security levels](../../../seclevels) page for details. ### min_mx_level `none` | `mtasts` | `dnssec` Default: `none` Set the minimal MX security level required for all outbound messages. -See [Security levels](../../seclevels) page for details. +See [Security levels](../../../seclevels) page for details. From 67d10ac059523ec53d628641ed14ebaab78098fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: 2023年10月11日 22:35:21 +0000 Subject: [PATCH 024/171] build(deps): bump golang.org/x/net from 0.10.0 to 0.17.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.10.0 to 0.17.0. - [Commits](https://github.com/golang/net/compare/v0.10.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- go.mod | 11 ++-- go.sum | 168 ++++----------------------------------------------------- 2 files changed, 15 insertions(+), 164 deletions(-) diff --git a/go.mod b/go.mod index 61d22fbc..e970ff51 100644 --- a/go.mod +++ b/go.mod @@ -48,10 +48,10 @@ require ( github.com/prometheus/client_golang v1.15.1 github.com/urfave/cli/v2 v2.25.5 go.uber.org/zap v1.24.0 - golang.org/x/crypto v0.9.0 - golang.org/x/net v0.10.0 + golang.org/x/crypto v0.14.0 + golang.org/x/net v0.17.0 golang.org/x/sync v0.2.0 - golang.org/x/text v0.9.0 + golang.org/x/text v0.13.0 ) require ( @@ -125,7 +125,6 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.15.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect - github.com/vultr/govultr/v2 v2.17.2 // indirect github.com/vultr/govultr/v3 v3.0.2 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect go.opencensus.io v0.24.0 // indirect @@ -133,12 +132,12 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/mod v0.10.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sys v0.8.0 // indirect + golang.org/x/sys v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.9.1 // indirect google.golang.org/api v0.124.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230525234025-438c736192d0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e // indirect google.golang.org/grpc v1.55.0 // indirect google.golang.org/protobuf v1.30.0 // indirect diff --git a/go.sum b/go.sum index dbe85a5f..e0b5d3a2 100644 --- a/go.sum +++ b/go.sum @@ -34,7 +34,6 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9 cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.107.0 h1:qkj22L7bgkl6vIeZDlOY2po43Mx/TIa2Wsa7VR+PEww= cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= @@ -72,8 +71,6 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.18.0 h1:FEigFqoDbys2cvFkZ9Fjq4gnHBP55anJ0yQyau2f9oY= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= cloud.google.com/go/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds= cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= @@ -118,8 +115,6 @@ cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs= -cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= @@ -193,8 +188,6 @@ github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+ github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw= -github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo= github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 h1:IEjq88XO4PuBDcvmjQJcQGg+w+UaafSy8G5Kcb5tBhI= github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5/go.mod h1:exZ0C/1emQJAw5tHOaUDyY1ycttqBAPcxuzf7QbY6ec= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -202,86 +195,50 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd github.com/aws/aws-sdk-go v1.17.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.44.40 h1:MR0qefjBJrZuXE0VoeKMQFtjS2tUeVpbQNfb7NzQNgI= github.com/aws/aws-sdk-go v1.44.40/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go-v2 v1.10.0/go.mod h1:U/EyyVvKtzmFeQQcca7eBotKdlpcP2zzU6bXBYcf7CE= -github.com/aws/aws-sdk-go-v2 v1.17.4 h1:wyC6p9Yfq6V2y98wfDsj6OnNQa4w2BLGCLIxzNhwOGY= -github.com/aws/aws-sdk-go-v2 v1.17.4/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.17.8/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2/config v1.9.0/go.mod h1:qhK5NNSgo9/nOSMu3HyE60WHXZTWTHTgd5qtIF44vOQ= -github.com/aws/aws-sdk-go-v2/config v1.18.12 h1:fKs/I4wccmfrNRO9rdrbMO1NgLxct6H9rNMiPdBxHWw= -github.com/aws/aws-sdk-go-v2/config v1.18.12/go.mod h1:J36fOhj1LQBr+O4hJCiT8FwVvieeoSGOtPuvhKlsNu8= github.com/aws/aws-sdk-go-v2/config v1.18.21/go.mod h1:+jPQiVPz1diRnjj6VGqWcLK6EzNmQ42l7J3OqGTLsSY= github.com/aws/aws-sdk-go-v2/config v1.18.25 h1:JuYyZcnMPBiFqn87L2cRppo+rNwgah6YwD3VuyvaW6Q= github.com/aws/aws-sdk-go-v2/config v1.18.25/go.mod h1:dZnYpD5wTW/dQF0rRNLVypB396zWCcPiBIvdvSWHEg4= -github.com/aws/aws-sdk-go-v2/credentials v1.5.0/go.mod h1:kvqTkpzQmzri9PbsiTY+LvwFzM0gY19emlAWwBOJMb0= -github.com/aws/aws-sdk-go-v2/credentials v1.13.12 h1:Cb+HhuEnV19zHRaYYVglwvdHGMJWbdsyP4oHhw04xws= -github.com/aws/aws-sdk-go-v2/credentials v1.13.12/go.mod h1:37HG2MBroXK3jXfxVGtbM2J48ra2+Ltu+tmwr/jO0KA= github.com/aws/aws-sdk-go-v2/credentials v1.13.20/go.mod h1:xtZnXErtbZ8YGXC3+8WfajpMBn5Ga/3ojZdxHq6iI8o= github.com/aws/aws-sdk-go-v2/credentials v1.13.24 h1:PjiYyls3QdCrzqUN35jMWtUK1vqVZ+zLfdOa/UPFDp0= github.com/aws/aws-sdk-go-v2/credentials v1.13.24/go.mod h1:jYPYi99wUOPIFi0rhiOvXeSEReVOzBqFNOX5bXYoG2o= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.7.0/go.mod h1:KqEkRkxm/+1Pd/rENRNbQpfblDBYeg5HDSqjB6ks8hA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22 h1:3aMfcTmoXtTZnaT86QlVaYh+BRMbvrrmZwIQ5jWqCZQ= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22/go.mod h1:YGSIJyQ6D6FjKMQh16hVFSIUD54L4F7zTGePqYMYYJU= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.2/go.mod h1:cDh1p6XkSGSwSRIArWRc6+UqAQ7x4alQ0QfpVR6f+co= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 h1:jJPgroehGvjrde3XufFIJUZVK5A2L9a3KwSFgKy9n8w= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28 h1:r+XwaCLpIvCKjBIYy/HVZujQS9tsz5ohHG3ZIe0wKoE= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.32/go.mod h1:RudqOgadTWdcS3t/erPQo24pcVEoYyqj/kKW5Vya21I= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22 h1:7AwGYXDdqRQYsluvKFmWoqpcOQJ4bH634SkYf3FNj/A= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.22/go.mod h1:EqK7gVrIGAHyZItrD1D8B0ilgwMD1GiWAmbU4u/JHNk= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.26/go.mod h1:vq86l7956VgFr0/FWQ2BWnK07QC3WYsepKzy33qqY5U= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.2.5/go.mod h1:6ZBTuDmvpCOD4Sf1i2/I3PgftlEcDGgvi8ocq64oQEg= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29 h1:J4xhFd6zHhdF9jPP0FQJ6WknzBboGMBNjKOv4iTuw4A= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.29/go.mod h1:TwuqRBGzxjQJIwH16/fOZodwXt2Zxa9/cwJC5ke4j7s= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.33/go.mod h1:zG2FcwjQarWaqXSCGpgcr3RSjZ6dHGguZSppUL0XR7Q= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 h1:gGLG7yKaXG02/jBlg210R7VgQIotiQntNhsCFejawx8= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.4.0/go.mod h1:X5/JuOxPLU/ogICgDTtnpfaQzdQJO0yKDcpoxWLLJ8Y= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22 h1:LjFQf8hFuMO22HkV5VWGLBvmCLBCLPivUAmpdpnp4Vs= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.22/go.mod h1:xt0Au8yPIwYXf/GYPy/vl4K3CgwhfQMYbrH7DlUUIws= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.26/go.mod h1:Bd4C/4PkVGubtNe5iMXu5BNnaBi/9t/UsFspPt4ram8= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAcCa2qVtRs7Ot5hItA2MsufrphbRFlz1Owxo= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw= -github.com/aws/aws-sdk-go-v2/service/route53 v1.12.0/go.mod h1:LbPVLMeOEGLIW54yuMayW70DcTtsb+17ekL5j48deF4= -github.com/aws/aws-sdk-go-v2/service/route53 v1.27.1 h1:F0SHIrL3PMxZFhxRfzr0MS1TyLuSZ5U/mLwFU8QZPI8= -github.com/aws/aws-sdk-go-v2/service/route53 v1.27.1/go.mod h1:Dc2/L5MZOZaLaBHJmykEltTj15t7WMTQnGZlD0Ju/kg= github.com/aws/aws-sdk-go-v2/service/route53 v1.27.7/go.mod h1:Jhu94omkrksnqX6Xs4Qo10eA1Fx+2NYKjZMU4GvZLp0= github.com/aws/aws-sdk-go-v2/service/route53 v1.28.1 h1:8e1fgdyer5IqBPtiWNsVLY/XFucmNTtYMqADyCFXTgQ= github.com/aws/aws-sdk-go-v2/service/route53 v1.28.1/go.mod h1:9SEpwqaALzp34eCT6w5PTh4SDDT84wxfMRx9VJSJPsk= -github.com/aws/aws-sdk-go-v2/service/sso v1.5.0/go.mod h1:GsqaJOJeOfeYD88/2vHWKXegvDRofDqWwC5i48A2kgs= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.1 h1:lQKN/LNa3qqu2cDOQZybP7oL4nMGGiFqob0jZJaR8/4= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.1/go.mod h1:IgV8l3sj22nQDd5qcAGY0WenwCzCphqdbFOpfktZPrI= github.com/aws/aws-sdk-go-v2/service/sso v1.12.8/go.mod h1:GNIveDnP+aE3jujyUSH5aZ/rktsTM5EvtKnCqBZawdw= github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 h1:UBQjaMTCKwyUYwiVnUt6toEJwGXsLBI6al083tpjJzY= github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1 h1:0bLhH6DRAqox+g0LatcjGKjjhU6Eudyys6HB6DJVPj8= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1/go.mod h1:O1YSOg3aekZibh2SngvCRRG+cRHKKlYgxf/JBF/Kr/k= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.8/go.mod h1:44qFP1g7pfd+U+sQHLPalAPKnyfTZjJsYR4xIwsJy5o= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 h1:PkHIIJs8qvq0e5QybnZoG1K/9QTrLr9OsqCIo59jOBA= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= -github.com/aws/aws-sdk-go-v2/service/sts v1.8.0/go.mod h1:dOlm91B439le5y1vtPCk5yJtbx3RdT3hRGYRY8TYKvQ= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.3 h1:s49mSnsBZEXjfGBkRfmK+nPqzT7Lt3+t2SmAKNyHblw= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.3/go.mod h1:b+psTJn33Q4qGoDaM7ZiOVVG8uVjGI6HaZ8WBHdgDgU= github.com/aws/aws-sdk-go-v2/service/sts v1.18.9/go.mod h1:yyW88BEPXA2fGFyI2KCcZC3dNpiT0CZAHaF+i656/tQ= github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 h1:2DQLAKDteoEDI8zpCzqBMaZlJuoE9iTYD0gFmXVax9E= github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= -github.com/aws/smithy-go v1.8.1/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE= github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= @@ -305,8 +262,6 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/digitalocean/godo v1.41.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= -github.com/digitalocean/godo v1.96.0 h1:w46AC3z9upSEjxRa4jhjwYlp3XCTHpKdTFLtPWA4rXE= -github.com/digitalocean/godo v1.96.0/go.mod h1:NRpFznZFvhHjBoqZAaOD3khVzsJ3EibzKqFL4R60dmA= github.com/digitalocean/godo v1.99.0 h1:gUHO7n9bDaZFWvbzOum4bXE0/09ZuYA9yA8idQHX57E= github.com/digitalocean/godo v1.99.0/go.mod h1:SsS2oXo2rznfM/nORlZ/6JaUJZFhmKTib1YhopUc8NA= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -347,8 +302,6 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= -github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/foxcpp/go-dovecot-sasl v0.0.0-20200522223722-c4699d7a24bf h1:rmBPY5fryjp9zLQYsUmQqqgsYq7qeVfrjtr96Tf9vD8= @@ -364,8 +317,6 @@ github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613 h1:fw9OWfPxP1C github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613/go.mod h1:P/O/qz4gaVkefzJ40BUtN/ZzBnaEg0YYe1no/SMp7Aw= github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed h1:1Jo7geyvunrPSjL6F6D9EcXoNApS5v3LQaro7aUNPnE= github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed/go.mod h1:Shows1vmkBWO40ChOClaUe6DUnZrsP1UPAuoWzIUdgQ= -github.com/foxcpp/go-imap-sql v0.5.1-0.20230119225722-2c2868ce7ca7 h1:8TcyoV/oZqvpEjMxv6A7tVeYeJqSOI6WhtNIR4UmEmc= -github.com/foxcpp/go-imap-sql v0.5.1-0.20230119225722-2c2868ce7ca7/go.mod h1:8uUTN2RRWZrETuA9pDvDr4SjV1hCvEYG2WOlXuupj+g= github.com/foxcpp/go-imap-sql v0.5.1-0.20230313080458-c0176dad679c h1:vqLBcLtG5lcXL2hifcsKjiUaljRukD8xHodVM2rZ+L4= github.com/foxcpp/go-imap-sql v0.5.1-0.20230313080458-c0176dad679c/go.mod h1:8uUTN2RRWZrETuA9pDvDr4SjV1hCvEYG2WOlXuupj+g= github.com/foxcpp/go-mockdns v0.0.0-20191216195825-5eabd8dbfe1f/go.mod h1:tPg4cp4nseejPd+UKxtCVQ2hUxNTZ7qQZJa7CLriIeo= @@ -374,7 +325,6 @@ github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtV github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8 h1:k8w0iy6GP9oeSZWUH3p2DqZHaXDKZGNs3NZGZMGfQHc= github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8/go.mod h1:HO1YOCbBM8KjpgThMMFejHx6K/UsnEv2Oh9YGtBIlOU= github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= @@ -387,8 +337,6 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs= github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= -github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -422,7 +370,6 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -480,8 +427,6 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1 h1:RY7tHKZcRlk788d5WSo/e83gOyyy742E8GSs771ySpg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -493,22 +438,16 @@ github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99 github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0 h1:IcsPKeInNvYi7eqSaDjiZqDDKu5rsmunY0Y1YupQSSQ= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= github.com/googleapis/gax-go/v2 v2.9.1 h1:DpTpJqzZ3NvX9zqjhIuI1oVzYZMvboZe+3LoeEIJjHM= github.com/googleapis/gax-go/v2 v2.9.1/go.mod h1:4FG3gMrVZlyMp5itSYKMU9z/lBE7+SbnUOvzH2HqbEY= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I= -github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0= github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -532,19 +471,13 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= -github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU= -github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -552,8 +485,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/lib/pq v1.4.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libdns/alidns v1.0.3-0.20220501125541-4a895238a95d h1:UiGXId+q/C65kEY3MJhdmK3d4QiS4yrWljeDjc8tZ0E= github.com/libdns/alidns v1.0.3-0.20220501125541-4a895238a95d/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE= github.com/libdns/cloudflare v0.1.1-0.20221006221909-9d3ab3c3cddd h1:c5hc0b5/pFqFeyQaOTVmYJbyr+QwZZFcMnjgtZGIk6k= @@ -571,8 +502,6 @@ github.com/libdns/leaseweb v0.3.1/go.mod h1:OeZtd+s2M1RfC3wIJF9SHZDFpD7H5RRiC6OP github.com/libdns/libdns v0.1.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= -github.com/libdns/libdns v0.2.2-0.20221006221142-3ef90aee33fd h1:SyZBFgMczGjPf5VIKgj3OqpvWPd4qsx6VTX5Bpe3GkU= -github.com/libdns/libdns v0.2.2-0.20221006221142-3ef90aee33fd/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/libdns/libdns v0.2.2-0.20230227175549-2dc480633939 h1:EvTiXkv78P20yfk4CUPmAkH3Cmumt3s/48WWiC2babY= github.com/libdns/libdns v0.2.2-0.20230227175549-2dc480633939/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/libdns/metaname v0.3.0 h1:HJudLYthdv52TupOPczojip/nEQHW7xqk5+whGReva4= @@ -581,12 +510,8 @@ github.com/libdns/namecheap v0.0.0-20211109042440-fc7440785c8e h1:WCcKyxiiK/sJnS github.com/libdns/namecheap v0.0.0-20211109042440-fc7440785c8e/go.mod h1:dED6sMLZxIcilF1GjrcpwgVoCglXGMn86irqQzRhqRY= github.com/libdns/namedotcom v0.3.3 h1:R10C7+IqQGVeC4opHHMiFNBxdNBg1bi65ZwqLESl+jE= github.com/libdns/namedotcom v0.3.3/go.mod h1:GbYzsAF2yRUpI0WgIK5fs5UX+kDVUPaYCFLpTnKQm0s= -github.com/libdns/route53 v1.3.0 h1:f41D9uUK7Gib8Zbg3LtAXfxGRFlqfR4gep+FsthDFg0= -github.com/libdns/route53 v1.3.0/go.mod h1:Vu827KwORxYR2I6iGsu8IKh4MESliECL7VA4pAsn95o= github.com/libdns/route53 v1.3.3 h1:16sTxbbRGm0zODz0p0aVHHIyTqtHzEn3j0s4dGzQvNI= github.com/libdns/route53 v1.3.3/go.mod h1:n1Xy55lpfdxMIx4CVWAM16GQac+/OZcnm1xBjMyhZAo= -github.com/libdns/vultr v0.0.0-20220906182619-5ea9da3d9625 h1:ZOC61eCF7y6Hjj3D0aMtef7zMbQAUGGLXydvOmpa75Y= -github.com/libdns/vultr v0.0.0-20220906182619-5ea9da3d9625/go.mod h1:s+M03kLf7Z2ZR6Ut5cl16fycy9MjI3ETdF1LENh+8E8= github.com/libdns/vultr v1.0.0 h1:W8B4+k2bm9ro3bZLSZV9hMOQI+uO6Svu+GmD+Olz7ZI= github.com/libdns/vultr v1.0.0/go.mod h1:8K1HJExcbeHS4YPkFHRZpqpXZzZ+DZAA0m0VikJgEqk= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -602,32 +527,22 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80= -github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY= github.com/mholt/acmez v1.1.1 h1:sYeeYd/EHVm9cSmLdWey5oW/fXFVAq5pNLjSczN2ZUg= github.com/mholt/acmez v1.1.1/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= -github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI= github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.47 h1:sLiuCKGSIcn/MI6lREmTzX91DX/oRau4ia0j6e6eOSs= -github.com/minio/minio-go/v7 v7.0.47/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw= github.com/minio/minio-go/v7 v7.0.55 h1:ZXqUO/8cgfHzI+08h/zGuTTFpISSA32BZmBE3FCLJas= github.com/minio/minio-go/v7 v7.0.55/go.mod h1:NUDy4A4oXPq1l2yK6LTSvCEzAMeIcoz9lcj5dbzSrRE= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -641,8 +556,6 @@ github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6 h1:TsF5Cl0Mj5JMv github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6/go.mod h1:4PEbISVqRCQaXaDAt289w3nK9UhoF8/ZOLy31Hbv7ds= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd h1:4yVpQ/+li28lQ/daYCWeDB08obRmjaoAw2qfFFaCQ40= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd/go.mod h1:wpK5wqysOJU1w2OxgG65du8M7UqBkxzsNaJdjwiRqAs= -github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= -github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -654,29 +567,18 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.39.0 h1:oOyhkDq05hPZKItWVBkJ6g6AtGxi+fy7F4JvUV8uhsI= -github.com/prometheus/common v0.39.0/go.mod h1:6XBZ7lYdLCbkAVhwRsWTZn+IN5AB9F/NXd5w0BbEX0Y= github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -685,18 +587,12 @@ github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 h1:J6qvD6rbmOil46orKqJaRPG+zTpoGlBTUdyv8ki63L0= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= -github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= @@ -717,19 +613,14 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli/v2 v2.24.3 h1:7Q1w8VN8yE0MJEHP06bv89PjYsN4IHWED2s1v/Zlfm0= -github.com/urfave/cli/v2 v2.24.3/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc= github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= -github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= -github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= github.com/vultr/govultr/v3 v3.0.2 h1:rrYiuF9adB3rjnhp0ev+mkJXKEzuYa/AGfezYPr3EMs= github.com/vultr/govultr/v3 v3.0.2/go.mod h1:Pd3D6VKmQKyKWsdV1xLx4VKclEV23adMs3YoI7rh7gA= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= @@ -751,19 +642,11 @@ go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= -go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -775,15 +658,12 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -820,8 +700,6 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/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 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -863,9 +741,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211118161319-6a13c67c3ce4/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -874,15 +750,12 @@ golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -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/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -908,8 +781,6 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.4.0 h1:NF0gk8LVPg1Ml7SSbGyySuoxdsXitj7TvgvuRxIMc/M= -golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -926,8 +797,6 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/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.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1003,11 +872,9 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.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/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1022,10 +889,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1085,10 +950,7 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.5.0 h1:+bSpV5HIeWkuvgaMfI3UmKRThoTA5ODJTUd8T17NO+4= -golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1147,8 +1009,6 @@ google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.109.0 h1:sW9hgHyX497PP5//NUM7nqfV8D0iDfBApqq7sOh1XR8= -google.golang.org/api v0.109.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= google.golang.org/api v0.124.0 h1:dP6Ef1VgOGqQ8eiv4GiY8RhmeyqzovcXBYPDUYG8Syo= google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -1262,10 +1122,8 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55/go.mod h1:45EK0dUbEZ2NHjCeAd2LXmyjAgGUGrpGROgjhC3ADck= -google.golang.org/genproto v0.0.0-20230202175211-008b39050e57 h1:vArvWooPH749rNHpBGgVl+U9B9dATjiEhJzcWGlovNs= -google.golang.org/genproto v0.0.0-20230202175211-008b39050e57/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230525234025-438c736192d0 h1:x1vNwUhVOcsYoKyEGCZBH694SBmmBjA2EfauFVEI2+M= -google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= +google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a h1:HiYVD+FGJkTo+9zj1gqz0anapsa1JxjiSrN+BJKyUmE= +google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e h1:NumxXLPfHSndr3wBBdeKiVHjGVFzi9RX2HwwQke94iY= google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1303,8 +1161,6 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.52.3 h1:pf7sOysg4LdgBqduXveGKrcEwbStiK2rtfghdzlUYDQ= -google.golang.org/grpc v1.52.3/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY= google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= @@ -1322,13 +1178,11 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= @@ -1336,11 +1190,9 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 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= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= From 7e851366ca8ade0344fd02fed94a2a012c9a6804 Mon Sep 17 00:00:00 2001 From: opswill <7550211+opswill@users.noreply.github.com> Date: 2023年10月20日 10:50:34 +0800 Subject: [PATCH 025/171] Correct spf dns record Based on the spf documnet: http://www.open-spf.org/action_browse_id_FAQ/Common_mistakes_revision_26/#helo ``` example.com. IN TXT "v=spf1 mx -all" mailserver.example.com. IN TXT "v=spf1 a -all" ``` the second spf record in doc should set to a record. otherwise the mail-tester will show ```SPF_HELO_SOFTFAIL SPF: HELO does not match SPF record (softfail) ``` softfail --- docs/tutorials/setting-up.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/setting-up.md b/docs/tutorials/setting-up.md index b528f3b4..fd4e4aec 100644 --- a/docs/tutorials/setting-up.md +++ b/docs/tutorials/setting-up.md @@ -168,7 +168,7 @@ mx1.example.org. AAAA 2001:beef::1 ; for this domain, and nobody else. example.org. TXT "v=spf1 mx ~all" ; It is recommended to server SPF record for both domain and MX hostname -mx1.example.org. TXT "v=spf1 mx ~all" +mx1.example.org. TXT "v=spf1 a ~all" ; Opt-in into DMARC with permissive policy and request reports about broken ; messages. From 0453d09f3bf88d5a71970c7ca8a175158add0d91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: 2023年10月25日 22:39:28 +0000 Subject: [PATCH 026/171] build(deps): bump google.golang.org/grpc from 1.55.0 to 1.56.3 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.55.0 to 1.56.3. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.55.0...v1.56.3) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e970ff51..ed8c22c6 100644 --- a/go.mod +++ b/go.mod @@ -139,7 +139,7 @@ require ( google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e // indirect - google.golang.org/grpc v1.55.0 // indirect + google.golang.org/grpc v1.56.3 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index e0b5d3a2..d54a2eac 100644 --- a/go.sum +++ b/go.sum @@ -1161,8 +1161,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= +google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= From d99192cd2a84996a5151358965e4d4472162c57f Mon Sep 17 00:00:00 2001 From: Devin Buhl Date: 2023年10月27日 17:48:23 -0400 Subject: [PATCH 027/171] fix: bump alpine to 3.18.4 I am not sure if you want this to be `alpine:3.18` instead, feel free to change it. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 1fe12901..5ceb6f7f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ RUN mkdir -p /pkg/data && \ cp maddy.conf.docker /pkg/data/maddy.conf && \ ./build.sh --builddir /tmp --destdir /pkg/ --tags docker build install -FROM alpine:3.17.0 +FROM alpine:3.18.4 LABEL maintainer="fox.cpp@disroot.org" LABEL org.opencontainers.image.source=https://github.com/foxcpp/maddy From ea2fed2e77e89bc2a226eb918c4e0e099f0ba385 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: 2023年10月30日 14:54:22 +0100 Subject: [PATCH 028/171] docs/reference/modifiers/dkim: fix number of sigs for sign_fields oversign_fields is signed n+1 times, but sign_fields only n times. --- docs/reference/modifiers/dkim.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/modifiers/dkim.md b/docs/reference/modifiers/dkim.md index 5672a7d4..36fffe27 100644 --- a/docs/reference/modifiers/dkim.md +++ b/docs/reference/modifiers/dkim.md @@ -121,7 +121,7 @@ Default set of oversigned fields: ### sign_fields _list..._ Default: see below -Header fields that should be signed n+1 times where n is times they are +Header fields that should be signed n times where n is times they are present in the message. For these fields, additional values can be prepended by intermediate relays, but existing values can't be changed. From 8431eae5e5e068e0553c67118f0984c136ac0df8 Mon Sep 17 00:00:00 2001 From: guoguangwu Date: 2023年11月16日 15:52:52 +0800 Subject: [PATCH 029/171] chore: remove refs to deprecated io/ioutil --- framework/buffer/memory.go | 3 +-- framework/config/tls/client.go | 4 ++-- framework/log/log.go | 3 +-- internal/modify/dkim/dkim_test.go | 4 ++-- internal/modify/dkim/keys.go | 3 +-- internal/modify/dkim/keys_test.go | 8 ++++---- internal/table/file_test.go | 9 ++++----- internal/target/queue/queue.go | 3 +-- internal/target/queue/queue_test.go | 8 ++++---- internal/testutils/bench_delivery.go | 4 ++-- internal/testutils/buffer.go | 7 +++---- internal/testutils/filesystem.go | 6 +++--- internal/testutils/smtp_server.go | 5 ++--- internal/testutils/target.go | 7 +++---- tests/t.go | 5 ++--- 15 files changed, 35 insertions(+), 44 deletions(-) diff --git a/framework/buffer/memory.go b/framework/buffer/memory.go index 997a2dd1..dafd6779 100644 --- a/framework/buffer/memory.go +++ b/framework/buffer/memory.go @@ -20,7 +20,6 @@ package buffer import ( "io" - "io/ioutil" ) // MemoryBuffer implements Buffer interface using byte slice. @@ -43,7 +42,7 @@ func (mb MemoryBuffer) Remove() error { // BufferInMemory is a convenience function which creates MemoryBuffer with // contents of the passed io.Reader. func BufferInMemory(r io.Reader) (Buffer, error) { - blob, err := ioutil.ReadAll(r) + blob, err := io.ReadAll(r) if err != nil { return nil, err } diff --git a/framework/config/tls/client.go b/framework/config/tls/client.go index b93e41c0..cf21b3cb 100644 --- a/framework/config/tls/client.go +++ b/framework/config/tls/client.go @@ -22,7 +22,7 @@ import ( "crypto/tls" "crypto/x509" "fmt" - "io/ioutil" + "os" "github.com/foxcpp/maddy/framework/config" "github.com/foxcpp/maddy/framework/log" @@ -58,7 +58,7 @@ func TLSClientBlock(_ *config.Map, node config.Node) (interface{}, error) { if len(rootCAPaths) != 0 { pool := x509.NewCertPool() for _, path := range rootCAPaths { - blob, err := ioutil.ReadFile(path) + blob, err := os.ReadFile(path) if err != nil { return nil, err } diff --git a/framework/log/log.go b/framework/log/log.go index c3fa3af8..98fb3a81 100644 --- a/framework/log/log.go +++ b/framework/log/log.go @@ -22,7 +22,6 @@ package log import ( "fmt" "io" - "io/ioutil" "os" "strings" "time" @@ -199,7 +198,7 @@ func (l Logger) Write(s []byte) (int, error) { // Write method of returned object will be no-op. func (l Logger) DebugWriter() io.Writer { if !l.Debug { - return ioutil.Discard + return io.Discard } l.Debug = true return &l diff --git a/internal/modify/dkim/dkim_test.go b/internal/modify/dkim/dkim_test.go index d4648635..d4a9b5ad 100644 --- a/internal/modify/dkim/dkim_test.go +++ b/internal/modify/dkim/dkim_test.go @@ -21,7 +21,7 @@ package dkim import ( "bytes" "context" - "io/ioutil" + "os" "path/filepath" "reflect" "sort" @@ -106,7 +106,7 @@ func verifyTestMsg(t *testing.T, keysPath string, expectedDomains []string, hdr domainsMap := make(map[string]bool) zones := map[string]mockdns.Zone{} for _, domain := range expectedDomains { - dnsRecord, err := ioutil.ReadFile(filepath.Join(keysPath, domain+".dns")) + dnsRecord, err := os.ReadFile(filepath.Join(keysPath, domain+".dns")) if err != nil { t.Fatal(err) } diff --git a/internal/modify/dkim/keys.go b/internal/modify/dkim/keys.go index bbf8a2e9..7c39b76e 100644 --- a/internal/modify/dkim/keys.go +++ b/internal/modify/dkim/keys.go @@ -29,7 +29,6 @@ import ( "encoding/pem" "fmt" "io" - "io/ioutil" "os" "path/filepath" ) @@ -45,7 +44,7 @@ func (m *Modifier) loadOrGenerateKey(keyPath, newKeyAlgo string) (pkey crypto.Si } defer f.Close() - pemBlob, err := ioutil.ReadAll(f) + pemBlob, err := io.ReadAll(f) if err != nil { return nil, false, err } diff --git a/internal/modify/dkim/keys_test.go b/internal/modify/dkim/keys_test.go index c8e5dec2..ebe13ba1 100644 --- a/internal/modify/dkim/keys_test.go +++ b/internal/modify/dkim/keys_test.go @@ -22,7 +22,7 @@ import ( "crypto/ed25519" "crypto/rsa" "encoding/base64" - "io/ioutil" + "os" "path/filepath" "strings" "testing" @@ -44,7 +44,7 @@ func TestKeyLoad_new(t *testing.T) { t.Fatal("newKey=false") } - recordBlob, err := ioutil.ReadFile(filepath.Join(dir, "testkey.dns")) + recordBlob, err := os.ReadFile(filepath.Join(dir, "testkey.dns")) if err != nil { t.Fatal(err) } @@ -82,7 +82,7 @@ func TestKeyLoad_existing_pkcs8(t *testing.T) { dir := t.TempDir() - if err := ioutil.WriteFile(filepath.Join(dir, "testkey.key"), []byte(pkeyEd25519), 0o600); err != nil { + if err := os.WriteFile(filepath.Join(dir, "testkey.key"), []byte(pkeyEd25519), 0o600); err != nil { t.Fatal(err) } @@ -134,7 +134,7 @@ func TestKeyLoad_existing_pkcs1(t *testing.T) { dir := t.TempDir() - if err := ioutil.WriteFile(filepath.Join(dir, "testkey.key"), []byte(pkeyRSA), 0o600); err != nil { + if err := os.WriteFile(filepath.Join(dir, "testkey.key"), []byte(pkeyRSA), 0o600); err != nil { t.Fatal(err) } diff --git a/internal/table/file_test.go b/internal/table/file_test.go index 19407860..c51620d2 100644 --- a/internal/table/file_test.go +++ b/internal/table/file_test.go @@ -19,7 +19,6 @@ along with this program. If not, see . package table import ( - "io/ioutil" "os" "reflect" "testing" @@ -33,7 +32,7 @@ func TestReadFile(t *testing.T) { test := func(file string, expected map[string][]string) { t.Helper() - f, err := ioutil.TempFile("", "maddy-tests-") + f, err := os.CreateTemp("", "maddy-tests-") if err != nil { t.Fatal(err) } @@ -88,7 +87,7 @@ func TestFileReload(t *testing.T) { const file = `cat: dog` - f, err := ioutil.TempFile("", "maddy-tests-") + f, err := os.CreateTemp("", "maddy-tests-") if err != nil { t.Fatal(err) } @@ -139,7 +138,7 @@ func TestFileReload_Broken(t *testing.T) { const file = `cat: dog` - f, err := ioutil.TempFile("", "maddy-tests-") + f, err := os.CreateTemp("", "maddy-tests-") if err != nil { t.Fatal(err) } @@ -185,7 +184,7 @@ func TestFileReload_Removed(t *testing.T) { const file = `cat: dog` - f, err := ioutil.TempFile("", "maddy-tests-") + f, err := os.CreateTemp("", "maddy-tests-") if err != nil { t.Fatal(err) } diff --git a/internal/target/queue/queue.go b/internal/target/queue/queue.go index 436df4b3..b749a850 100644 --- a/internal/target/queue/queue.go +++ b/internal/target/queue/queue.go @@ -63,7 +63,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "math" "os" "path/filepath" @@ -640,7 +639,7 @@ func (q *Queue) removeFromDisk(msgMeta *module.MsgMetadata) { } func (q *Queue) readDiskQueue() error { - dirInfo, err := ioutil.ReadDir(q.location) + dirInfo, err := os.ReadDir(q.location) if err != nil { return err } diff --git a/internal/target/queue/queue_test.go b/internal/target/queue/queue_test.go index 4064c49b..1ffaece1 100644 --- a/internal/target/queue/queue_test.go +++ b/internal/target/queue/queue_test.go @@ -24,7 +24,7 @@ import ( "crypto/sha1" "encoding/hex" "errors" - "io/ioutil" + "io" "os" "path/filepath" "reflect" @@ -122,7 +122,7 @@ func (utd *unreliableTargetDelivery) Body(ctx context.Context, header textproto. } r, _ := body.Open() - utd.msg.Body, _ = ioutil.ReadAll(r) + utd.msg.Body, _ = io.ReadAll(r) if len(utd.ut.bodyFailures)> utd.ut.passedMessages { return utd.ut.bodyFailures[utd.ut.passedMessages] @@ -133,7 +133,7 @@ func (utd *unreliableTargetDelivery) Body(ctx context.Context, header textproto. func (utd *unreliableTargetDeliveryPartial) BodyNonAtomic(ctx context.Context, c module.StatusCollector, header textproto.Header, body buffer.Buffer) { r, _ := body.Open() - utd.msg.Body, _ = ioutil.ReadAll(r) + utd.msg.Body, _ = io.ReadAll(r) if len(utd.ut.bodyFailuresPartial)> utd.ut.passedMessages { for rcpt, err := range utd.ut.bodyFailuresPartial[utd.ut.passedMessages] { @@ -200,7 +200,7 @@ func checkQueueDir(t *testing.T, q *Queue, expectedIDs []string) { expectedMap[id] = false } - dir, err := ioutil.ReadDir(q.location) + dir, err := os.ReadDir(q.location) if err != nil { t.Fatalf("failed to read queue directory: %v", err) } diff --git a/internal/testutils/bench_delivery.go b/internal/testutils/bench_delivery.go index 33d47431..834bcf41 100644 --- a/internal/testutils/bench_delivery.go +++ b/internal/testutils/bench_delivery.go @@ -23,7 +23,7 @@ import ( "context" "crypto/sha1" "encoding/hex" - "io/ioutil" + "io" "strconv" "strings" "testing" @@ -100,7 +100,7 @@ func RandomMsg(b *testing.B) (module.MsgMetadata, textproto.Header, buffer.Buffe for i := 0; i < ExtraMessageHeaderFields; i++ { hdr.Add("AAAAAAAAAAAA-"+strconv.Itoa(i), strings.Repeat("A", ExtraMessageHeaderFieldSize)) } - bodyBlob, _ := ioutil.ReadAll(body) + bodyBlob, _ := io.ReadAll(body) return module.MsgMetadata{ DontTraceSender: true, diff --git a/internal/testutils/buffer.go b/internal/testutils/buffer.go index 9e0bcd53..259eea2d 100644 --- a/internal/testutils/buffer.go +++ b/internal/testutils/buffer.go @@ -22,7 +22,6 @@ import ( "bufio" "bytes" "io" - "io/ioutil" "strings" "testing" @@ -38,7 +37,7 @@ func BodyFromStr(t *testing.T, literal string) (textproto.Header, buffer.MemoryB if err != nil { t.Fatal(err) } - body, err := ioutil.ReadAll(bufr) + body, err := io.ReadAll(bufr) if err != nil { t.Fatal(err) } @@ -67,10 +66,10 @@ type FailingBuffer struct { } func (fb FailingBuffer) Open() (io.ReadCloser, error) { - r := ioutil.NopCloser(bytes.NewReader(fb.Blob)) + r := io.NopCloser(bytes.NewReader(fb.Blob)) if fb.IOError != nil { - return ioutil.NopCloser(&errorReader{r, fb.IOError}), fb.OpenError + return io.NopCloser(&errorReader{r, fb.IOError}), fb.OpenError } return r, fb.OpenError diff --git a/internal/testutils/filesystem.go b/internal/testutils/filesystem.go index e6b6abf2..cdac0174 100644 --- a/internal/testutils/filesystem.go +++ b/internal/testutils/filesystem.go @@ -19,14 +19,14 @@ along with this program. If not, see . package testutils import ( - "io/ioutil" + "os" "testing" ) -// Dir is a wrapper for ioutil.TempDir that +// Dir is a wrapper for os.MkdirTemp that // fails the test on errors. func Dir(t *testing.T) string { - dir, err := ioutil.TempDir("", "maddy-tests-") + dir, err := os.MkdirTemp("", "maddy-tests-") if err != nil { t.Fatalf("can't create test dir: %v", err) } diff --git a/internal/testutils/smtp_server.go b/internal/testutils/smtp_server.go index 4f24da38..7c82b640 100644 --- a/internal/testutils/smtp_server.go +++ b/internal/testutils/smtp_server.go @@ -22,7 +22,6 @@ import ( "crypto/tls" "crypto/x509" "io" - "io/ioutil" "net" "reflect" "sort" @@ -144,7 +143,7 @@ func (s *session) Data(r io.Reader) error { return s.backend.DataErr } - b, err := ioutil.ReadAll(r) + b, err := io.ReadAll(r) if err != nil { return err } @@ -161,7 +160,7 @@ func (s *session) LMTPData(r io.Reader, status smtp.StatusCollector) error { return s.backend.DataErr } - b, err := ioutil.ReadAll(r) + b, err := io.ReadAll(r) if err != nil { return err } diff --git a/internal/testutils/target.go b/internal/testutils/target.go index 3a55a44b..e573bdc4 100644 --- a/internal/testutils/target.go +++ b/internal/testutils/target.go @@ -24,7 +24,6 @@ import ( "encoding/hex" "errors" "io" - "io/ioutil" "reflect" "sort" "testing" @@ -131,7 +130,7 @@ func (dtd *testTargetDeliveryPartial) BodyNonAtomic(ctx context.Context, c modul } defer body.Close() - dtd.msg.Body, err = ioutil.ReadAll(body) + dtd.msg.Body, err = io.ReadAll(body) if err != nil { for rcpt, err := range dtd.tgt.PartialBodyErr { c.SetStatus(rcpt, err) @@ -157,11 +156,11 @@ func (dtd *testTargetDelivery) Body(ctx context.Context, header textproto.Header if dtd.tgt.DiscardMessages { // Don't bother. - _, err = io.Copy(ioutil.Discard, body) + _, err = io.Copy(io.Discard, body) return err } - dtd.msg.Body, err = ioutil.ReadAll(body) + dtd.msg.Body, err = io.ReadAll(body) return err } diff --git a/tests/t.go b/tests/t.go index 4b08a476..2243662a 100644 --- a/tests/t.go +++ b/tests/t.go @@ -27,7 +27,6 @@ import ( "bufio" "flag" "fmt" - "io/ioutil" "math/rand" "net" "os" @@ -150,7 +149,7 @@ func (t *T) Run(waitListeners int) { } // Setup file system, create statedir, runtimedir, write out config. - testDir, err := ioutil.TempDir("", "maddy-tests-") + testDir, err := os.MkdirTemp("", "maddy-tests-") if err != nil { t.Fatal("Test configuration failed:", err) } @@ -182,7 +181,7 @@ func (t *T) Run(waitListeners int) { configPreable := "state_dir " + filepath.Join(t.testDir, "statedir") + "\n" + "runtime_dir " + filepath.Join(t.testDir, "runtime") + "\n\n" - err = ioutil.WriteFile(filepath.Join(t.testDir, "maddy.conf"), []byte(configPreable+t.cfg), os.ModePerm) + err = os.WriteFile(filepath.Join(t.testDir, "maddy.conf"), []byte(configPreable+t.cfg), os.ModePerm) if err != nil { t.Fatal("Test configuration failed:", err) } From 0000f92f16ad6974917aa86d5f7a670ca2ab4211 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: 2023年12月18日 23:38:24 +0000 Subject: [PATCH 030/171] build(deps): bump golang.org/x/crypto from 0.14.0 to 0.17.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.14.0 to 0.17.0. - [Commits](https://github.com/golang/crypto/compare/v0.14.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index ed8c22c6..598e81a4 100644 --- a/go.mod +++ b/go.mod @@ -48,10 +48,10 @@ require ( github.com/prometheus/client_golang v1.15.1 github.com/urfave/cli/v2 v2.25.5 go.uber.org/zap v1.24.0 - golang.org/x/crypto v0.14.0 + golang.org/x/crypto v0.17.0 golang.org/x/net v0.17.0 golang.org/x/sync v0.2.0 - golang.org/x/text v0.13.0 + golang.org/x/text v0.14.0 ) require ( @@ -132,7 +132,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/mod v0.10.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sys v0.13.0 // indirect + golang.org/x/sys v0.15.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.9.1 // indirect google.golang.org/api v0.124.0 // indirect diff --git a/go.sum b/go.sum index d54a2eac..f4994d1f 100644 --- a/go.sum +++ b/go.sum @@ -662,8 +662,8 @@ golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -873,8 +873,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -889,8 +889,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 03184806061a2a18f8be6ae7b8f37eeabdaec3e8 Mon Sep 17 00:00:00 2001 From: Lyra Rebane Date: Tue, 9 Jan 2024 23:30:19 +0200 Subject: [PATCH 031/171] Fix escaping --- docs/reference/checks/authorize_sender.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/reference/checks/authorize_sender.md b/docs/reference/checks/authorize_sender.md index 0ceacff8..4ddd7862 100644 --- a/docs/reference/checks/authorize_sender.md +++ b/docs/reference/checks/authorize_sender.md @@ -35,9 +35,9 @@ Table that maps authorization username to the list of sender emails the user is allowed to use. In additional to email addresses, the table can contain domain names or -special string "*" as a value. If the value is a domain - user +special string "\*" as a value. If the value is a domain - user will be allowed to use any mailbox within it as a sender address. -If it is "*" - user will be allowed to use any address. +If it is "\*" - user will be allowed to use any address. By default, table.identity is used, meaning that username should be equal to the sender email. From 9c4438af2b47562b96207e698b214dd88d434f7c Mon Sep 17 00:00:00 2001 From: fluidum <16622232+fluidum@users.noreply.github.com> Date: 2024年1月18日 14:09:26 +0200 Subject: [PATCH 032/171] Update remote.md 1. fixed security levels href 2. changed default of min_tls_level (https://github.com/foxcpp/maddy/blob/master/maddy.conf) --- docs/reference/targets/remote.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/reference/targets/remote.md b/docs/reference/targets/remote.md index ea6ee354..9a1b6061 100644 --- a/docs/reference/targets/remote.md +++ b/docs/reference/targets/remote.md @@ -280,16 +280,16 @@ local_policy { Using `local_policy off` is equivalent to setting both directives to `none`. ### min_tls_level `none` | `encrypted` | `authenticated` -Default: `none` +Default: `encrypted` Set the minimal TLS security level required for all outbound messages. -See [Security levels](../../../seclevels) page for details. +See [Security levels](/seclevels) page for details. ### min_mx_level `none` | `mtasts` | `dnssec` Default: `none` Set the minimal MX security level required for all outbound messages. -See [Security levels](../../../seclevels) page for details. +See [Security levels](/seclevels) page for details. From 66185b90bb36119e81cf944b7eff84dd2b2332de Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月20日 20:53:05 +0300 Subject: [PATCH 033/171] storage/imapsql: Add support for transpiled SQLite driver May have slightly worse single-user perfomance than go-sqlite3 (CGo) but also may scale better due to Go goroutines being used instead of C threads. Also improves compatibility, making installation easier esp. if users do not have working C toolchain installed. See #666. --- go.mod | 14 +++++++- go.sum | 40 +++++++++++++++++++++ internal/storage/imapsql/imapsql.go | 11 ++++++ internal/storage/imapsql/modernc_sqlite3.go | 26 ++++++++++++++ internal/storage/imapsql/no_sqlite3.go | 24 +++++++++++++ internal/storage/imapsql/sqlite3.go | 2 ++ 6 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 internal/storage/imapsql/modernc_sqlite3.go create mode 100644 internal/storage/imapsql/no_sqlite3.go diff --git a/go.mod b/go.mod index 598e81a4..9391e72b 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/foxcpp/go-imap-i18nlevel v0.0.0-20200208001533-d6ec88553005 github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613 github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed - github.com/foxcpp/go-imap-sql v0.5.1-0.20230313080458-c0176dad679c + github.com/foxcpp/go-imap-sql v0.5.1-0.20240120174134-48f9dc0b4abf github.com/foxcpp/go-mockdns v1.0.0 github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8 github.com/go-ldap/ldap/v3 v3.4.4 @@ -94,6 +94,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/klauspost/compress v1.16.5 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -114,6 +115,7 @@ require ( github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rs/xid v1.5.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect @@ -144,6 +146,16 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools v2.2.0+incompatible // indirect + lukechampine.com/uint128 v1.2.0 // indirect + modernc.org/cc/v3 v3.40.0 // indirect + modernc.org/ccgo/v3 v3.16.13 // indirect + modernc.org/libc v1.29.0 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/opt v0.1.3 // indirect + modernc.org/sqlite v1.28.0 // indirect + modernc.org/strutil v1.1.3 // indirect + modernc.org/token v1.0.1 // indirect ) replace github.com/emersion/go-imap => github.com/foxcpp/go-imap v1.0.0-beta.1.0.20220623182312-df940c324887 diff --git a/go.sum b/go.sum index f4994d1f..590b0698 100644 --- a/go.sum +++ b/go.sum @@ -234,8 +234,10 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jely github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE= github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -256,6 +258,8 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -319,6 +323,8 @@ github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed h1:1Jo7ge github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed/go.mod h1:Shows1vmkBWO40ChOClaUe6DUnZrsP1UPAuoWzIUdgQ= github.com/foxcpp/go-imap-sql v0.5.1-0.20230313080458-c0176dad679c h1:vqLBcLtG5lcXL2hifcsKjiUaljRukD8xHodVM2rZ+L4= github.com/foxcpp/go-imap-sql v0.5.1-0.20230313080458-c0176dad679c/go.mod h1:8uUTN2RRWZrETuA9pDvDr4SjV1hCvEYG2WOlXuupj+g= +github.com/foxcpp/go-imap-sql v0.5.1-0.20240120174134-48f9dc0b4abf h1:tqkJhHCPp1LL0tFqe0GwPjw2BMug8ivMtKJbQ7ZUA/g= +github.com/foxcpp/go-imap-sql v0.5.1-0.20240120174134-48f9dc0b4abf/go.mod h1:8uUTN2RRWZrETuA9pDvDr4SjV1hCvEYG2WOlXuupj+g= github.com/foxcpp/go-mockdns v0.0.0-20191216195825-5eabd8dbfe1f/go.mod h1:tPg4cp4nseejPd+UKxtCVQ2hUxNTZ7qQZJa7CLriIeo= github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= @@ -336,9 +342,11 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs= github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -465,10 +473,14 @@ github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c h1:lx/uPI+m github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c/go.mod h1:LIAXxPvcUXwOcTIj9LSNSUpE9/eMHalTWxsP/kmWxQI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= @@ -545,6 +557,7 @@ github.com/minio/minio-go/v7 v7.0.55 h1:ZXqUO/8cgfHzI+08h/zGuTTFpISSA32BZmBE3FCL github.com/minio/minio-go/v7 v7.0.55/go.mod h1:NUDy4A4oXPq1l2yK6LTSvCEzAMeIcoz9lcj5dbzSrRE= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -552,10 +565,12 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6 h1:TsF5Cl0Mj5JMvPOP2ySVq+CZoiPrTGwvNPbuQotuSAE= github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6/go.mod h1:4PEbISVqRCQaXaDAt289w3nK9UhoF8/ZOLy31Hbv7ds= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd h1:4yVpQ/+li28lQ/daYCWeDB08obRmjaoAw2qfFFaCQ40= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd/go.mod h1:wpK5wqysOJU1w2OxgG65du8M7UqBkxzsNaJdjwiRqAs= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -576,6 +591,8 @@ github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdO github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= @@ -587,6 +604,7 @@ github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 h1:J6qvD6rbmOil46orKqJaRPG+zTpoGlBTUdyv8ki63L0= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -645,6 +663,7 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= @@ -1192,6 +1211,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -1204,6 +1224,26 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019年2月3日/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020年1月3日/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020年1月4日/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= +modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= +modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= +modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= +modernc.org/libc v1.29.0 h1:tTFRFq69YKCF2QyGNuRUQxKBm1uZZLubf6Cjh/pVHXs= +modernc.org/libc v1.29.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= +modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= +modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= +modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/storage/imapsql/imapsql.go b/internal/storage/imapsql/imapsql.go index 0fa3df86..ed27b052 100644 --- a/internal/storage/imapsql/imapsql.go +++ b/internal/storage/imapsql/imapsql.go @@ -168,6 +168,17 @@ func (store *Storage) Init(cfg *config.Map) error { return errors.New("imapsql: driver is required") } + if driver == "sqlite3" { + if sqliteImpl == "modernc" { + store.Log.Println("using transpiled SQLite (modernc.org/sqlite), this is experimental") + driver = "sqlite" + } else if sqliteImpl == "cgo" { + store.Log.Debugln("using cgo SQLite") + } else if sqliteImpl == "missing" { + return errors.New("imapsql: SQLite is not supported, recompile without no_sqlite3 tag set") + } + } + deliveryNormFunc, ok := authz.NormalizeFuncs[deliveryNormalize] if !ok { return errors.New("imapsql: unknown normalization function: " + deliveryNormalize) diff --git a/internal/storage/imapsql/modernc_sqlite3.go b/internal/storage/imapsql/modernc_sqlite3.go new file mode 100644 index 00000000..696b4c0a --- /dev/null +++ b/internal/storage/imapsql/modernc_sqlite3.go @@ -0,0 +1,26 @@ +//go:build !nosqlite3 && !cgo +// +build !nosqlite3,!cgo + +/* +Maddy Mail Server - Composable all-in-one email server. +Copyright © 2019-2020 Max Mazurov , Maddy Mail Server contributors + +This program 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. + +This program 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 . +*/ + +package imapsql + +import _ "modernc.org/sqlite" + +const sqliteImpl = "modernc" diff --git a/internal/storage/imapsql/no_sqlite3.go b/internal/storage/imapsql/no_sqlite3.go new file mode 100644 index 00000000..525f8e41 --- /dev/null +++ b/internal/storage/imapsql/no_sqlite3.go @@ -0,0 +1,24 @@ +//go:build nosqlite3 +// +build nosqlite3 + +/* +Maddy Mail Server - Composable all-in-one email server. +Copyright © 2019-2020 Max Mazurov , Maddy Mail Server contributors + +This program 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. + +This program 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 . +*/ + +package imapsql + +const sqliteImpl = "missing" diff --git a/internal/storage/imapsql/sqlite3.go b/internal/storage/imapsql/sqlite3.go index 3307d590..599f39d7 100644 --- a/internal/storage/imapsql/sqlite3.go +++ b/internal/storage/imapsql/sqlite3.go @@ -22,3 +22,5 @@ along with this program. If not, see . package imapsql import _ "github.com/mattn/go-sqlite3" + +const sqliteImpl = "cgo" From 301c47d81547cb2ca22abec6312df0d7ae69a84c Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月20日 20:57:37 +0300 Subject: [PATCH 034/171] ci: Bump used Ubuntu version --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 7c8ab81d..ddff0c69 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -10,7 +10,7 @@ on: jobs: build-and-test: name: "Build and test" - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: "Install libpam" From db0874c2be97319155953ff224051e046a3af6f7 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月21日 14:41:57 +0300 Subject: [PATCH 035/171] Migrate to latest go-smtp version Fixes #661 among other minor things. --- framework/config/map.go | 4 +-- framework/module/delivery_target.go | 3 +- framework/module/dummy.go | 3 +- go.mod | 4 +-- go.sum | 25 +++++----------- internal/endpoint/smtp/session.go | 11 ++++--- internal/endpoint/smtp/smtp.go | 11 ++++++- internal/endpoint/smtp/smtp_test.go | 16 +++++----- internal/msgpipeline/dmarc_test.go | 3 +- internal/msgpipeline/msgpipeline.go | 5 ++-- internal/msgpipeline/msgpipeline_test.go | 5 ++-- internal/smtpconn/smtpconn.go | 18 +++++------ internal/smtpconn/smtputf8_test.go | 2 +- internal/storage/imapsql/delivery.go | 3 +- internal/storage/imapsql/imapsql.go | 4 +-- internal/target/queue/queue.go | 18 +++++------ internal/target/queue/queue_test.go | 7 +++-- internal/target/remote/remote.go | 5 ++-- internal/target/remote/remote_test.go | 38 ++++++++++++------------ internal/target/smtp/smtp_downstream.go | 4 +-- internal/testutils/bench_delivery.go | 3 +- internal/testutils/smtp_server.go | 27 +++++++++++++---- internal/testutils/target.go | 7 +++-- tests/limits_test.go | 10 +++---- tests/stress_test.go | 16 +++++----- 25 files changed, 139 insertions(+), 113 deletions(-) diff --git a/framework/config/map.go b/framework/config/map.go index 83833aa0..10b17623 100644 --- a/framework/config/map.go +++ b/framework/config/map.go @@ -284,7 +284,7 @@ func ParseDataSize(s string) (int, error) { // data unit and allows multiple arguments (they will be added together). // // See Map.Custom for description of arguments. -func (m *Map) DataSize(name string, inheritGlobal, required bool, defaultVal int, store *int) { +func (m *Map) DataSize(name string, inheritGlobal, required bool, defaultVal int64, store *int64) { m.Custom(name, inheritGlobal, required, func() (interface{}, error) { return defaultVal, nil }, func(_ *Map, node Node) (interface{}, error) { @@ -301,7 +301,7 @@ func (m *Map) DataSize(name string, inheritGlobal, required bool, defaultVal int return nil, NodeErr(node, "%v", err) } - return dur, nil + return int64(dur), nil }, store) } diff --git a/framework/module/delivery_target.go b/framework/module/delivery_target.go index 94757a65..9a7c1e09 100644 --- a/framework/module/delivery_target.go +++ b/framework/module/delivery_target.go @@ -22,6 +22,7 @@ import ( "context" "github.com/emersion/go-message/textproto" + "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/buffer" ) @@ -56,7 +57,7 @@ type Delivery interface { // recipients that can't be used. Note: MsgMetadata object passed to Start // contains BodyLength field. If it is non-zero, it can be used to check // storage quota for the user before Body. - AddRcpt(ctx context.Context, rcptTo string) error + AddRcpt(ctx context.Context, rcptTo string, opts smtp.RcptOptions) error // Body sets the body and header contents for the message. // If this method fails, message is assumed to be undeliverable diff --git a/framework/module/dummy.go b/framework/module/dummy.go index ec73c932..0930d4db 100644 --- a/framework/module/dummy.go +++ b/framework/module/dummy.go @@ -22,6 +22,7 @@ import ( "context" "github.com/emersion/go-message/textproto" + "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" ) @@ -63,7 +64,7 @@ func (d *Dummy) Start(ctx context.Context, msgMeta *MsgMetadata, mailFrom string type dummyDelivery struct{} -func (dd dummyDelivery) AddRcpt(ctx context.Context, to string) error { +func (dd dummyDelivery) AddRcpt(ctx context.Context, rcptTo string, opts smtp.RcptOptions) error { return nil } diff --git a/go.mod b/go.mod index 9391e72b..f6209de2 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/emersion/go-milter v0.3.3 github.com/emersion/go-msgauth v0.6.6 github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead - github.com/emersion/go-smtp v0.16.0 + github.com/emersion/go-smtp v0.20.2-0.20240121112028-434ddca4792e github.com/foxcpp/go-dovecot-sasl v0.0.0-20200522223722-c4699d7a24bf github.com/foxcpp/go-imap-backend-tests v0.0.0-20220105184719-e80aa29a5e16 github.com/foxcpp/go-imap-i18nlevel v0.0.0-20200208001533-d6ec88553005 @@ -52,6 +52,7 @@ require ( golang.org/x/net v0.17.0 golang.org/x/sync v0.2.0 golang.org/x/text v0.14.0 + modernc.org/sqlite v1.28.0 ) require ( @@ -153,7 +154,6 @@ require ( modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.7.2 // indirect modernc.org/opt v0.1.3 // indirect - modernc.org/sqlite v1.28.0 // indirect modernc.org/strutil v1.1.3 // indirect modernc.org/token v1.0.1 // indirect ) diff --git a/go.sum b/go.sum index 590b0698..bdaaeeb7 100644 --- a/go.sum +++ b/go.sum @@ -234,10 +234,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jely github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE= github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -258,8 +256,6 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -290,8 +286,8 @@ github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b/go.mod h1:G/dpzLu github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead h1:fI1Jck0vUrXT8bnphprS1EoVRe2Q5CKCX8iDlpqjQ/Y= github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= -github.com/emersion/go-smtp v0.16.0 h1:eB9CY9527WdEZSs5sWisTmilDX7gG+Q/2IdRcmubpa8= -github.com/emersion/go-smtp v0.16.0/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= +github.com/emersion/go-smtp v0.20.2-0.20240121112028-434ddca4792e h1:WAPhaiA+bDO/mFgCDQJKCQI/RbH/73lCcis4Jb8Y2ec= +github.com/emersion/go-smtp v0.20.2-0.20240121112028-434ddca4792e/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 h1:IbFBtwoTQyw0fIM5xv1HF+Y+3ZijDR839WMulgxCcUY= github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= @@ -321,8 +317,6 @@ github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613 h1:fw9OWfPxP1C github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613/go.mod h1:P/O/qz4gaVkefzJ40BUtN/ZzBnaEg0YYe1no/SMp7Aw= github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed h1:1Jo7geyvunrPSjL6F6D9EcXoNApS5v3LQaro7aUNPnE= github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed/go.mod h1:Shows1vmkBWO40ChOClaUe6DUnZrsP1UPAuoWzIUdgQ= -github.com/foxcpp/go-imap-sql v0.5.1-0.20230313080458-c0176dad679c h1:vqLBcLtG5lcXL2hifcsKjiUaljRukD8xHodVM2rZ+L4= -github.com/foxcpp/go-imap-sql v0.5.1-0.20230313080458-c0176dad679c/go.mod h1:8uUTN2RRWZrETuA9pDvDr4SjV1hCvEYG2WOlXuupj+g= github.com/foxcpp/go-imap-sql v0.5.1-0.20240120174134-48f9dc0b4abf h1:tqkJhHCPp1LL0tFqe0GwPjw2BMug8ivMtKJbQ7ZUA/g= github.com/foxcpp/go-imap-sql v0.5.1-0.20240120174134-48f9dc0b4abf/go.mod h1:8uUTN2RRWZrETuA9pDvDr4SjV1hCvEYG2WOlXuupj+g= github.com/foxcpp/go-mockdns v0.0.0-20191216195825-5eabd8dbfe1f/go.mod h1:tPg4cp4nseejPd+UKxtCVQ2hUxNTZ7qQZJa7CLriIeo= @@ -342,11 +336,9 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs= github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -426,6 +418,7 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= @@ -473,12 +466,10 @@ github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c h1:lx/uPI+m github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c/go.mod h1:LIAXxPvcUXwOcTIj9LSNSUpE9/eMHalTWxsP/kmWxQI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -557,7 +548,6 @@ github.com/minio/minio-go/v7 v7.0.55 h1:ZXqUO/8cgfHzI+08h/zGuTTFpISSA32BZmBE3FCL github.com/minio/minio-go/v7 v7.0.55/go.mod h1:NUDy4A4oXPq1l2yK6LTSvCEzAMeIcoz9lcj5dbzSrRE= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -565,12 +555,10 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6 h1:TsF5Cl0Mj5JMvPOP2ySVq+CZoiPrTGwvNPbuQotuSAE= github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6/go.mod h1:4PEbISVqRCQaXaDAt289w3nK9UhoF8/ZOLy31Hbv7ds= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd h1:4yVpQ/+li28lQ/daYCWeDB08obRmjaoAw2qfFFaCQ40= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd/go.mod h1:wpK5wqysOJU1w2OxgG65du8M7UqBkxzsNaJdjwiRqAs= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -604,7 +592,6 @@ github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 h1:J6qvD6rbmOil46orKqJaRPG+zTpoGlBTUdyv8ki63L0= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -663,7 +650,6 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= @@ -1211,7 +1197,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -1230,6 +1215,8 @@ modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= +modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= +modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/libc v1.29.0 h1:tTFRFq69YKCF2QyGNuRUQxKBm1uZZLubf6Cjh/pVHXs= modernc.org/libc v1.29.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= @@ -1242,8 +1229,10 @@ modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/endpoint/smtp/session.go b/internal/endpoint/smtp/session.go index 52f7bc45..dfa19ea0 100644 --- a/internal/endpoint/smtp/session.go +++ b/internal/endpoint/smtp/session.go @@ -335,7 +335,7 @@ func (s *Session) fetchRDNSName(ctx context.Context) { s.connState.RDNSName.Set(name, nil) } -func (s *Session) Rcpt(to string) error { +func (s *Session) Rcpt(to string, opts *smtp.RcptOptions) error { s.msgLock.Lock() defer s.msgLock.Unlock() @@ -363,7 +363,7 @@ func (s *Session) Rcpt(to string) error { rcptCtx, rcptTask := trace.NewTask(s.msgCtx, "RCPT TO") defer rcptTask.End() - if err := s.rcpt(rcptCtx, to); err != nil { + if err := s.rcpt(rcptCtx, to, opts); err != nil { if s.loggedRcptErrors < s.endp.maxLoggedRcptErrors { s.log.Error("RCPT error", err, "rcpt", to, "msg_id", s.msgMeta.ID) s.loggedRcptErrors++ @@ -377,7 +377,7 @@ func (s *Session) Rcpt(to string) error { return nil } -func (s *Session) rcpt(ctx context.Context, to string) error { +func (s *Session) rcpt(ctx context.Context, to string, opts *smtp.RcptOptions) error { // INTERNATIONALIZATION: Do not permit non-ASCII addresses unless SMTPUTF8 is // used. if !address.IsASCII(to) && !s.opts.UTF8 { @@ -396,7 +396,7 @@ func (s *Session) rcpt(ctx context.Context, to string) error { } } - return s.delivery.AddRcpt(ctx, cleanTo) + return s.delivery.AddRcpt(ctx, cleanTo, *opts) } func (s *Session) Logout() error { @@ -413,6 +413,9 @@ func (s *Session) Logout() error { if s.cancelRDNS != nil { s.cancelRDNS() } + + s.endp.sessionCnt.Add(-1) + return nil } diff --git a/internal/endpoint/smtp/smtp.go b/internal/endpoint/smtp/smtp.go index a322b6d4..15df9f96 100644 --- a/internal/endpoint/smtp/smtp.go +++ b/internal/endpoint/smtp/smtp.go @@ -30,6 +30,7 @@ import ( "path/filepath" "strings" "sync" + "sync/atomic" "time" "github.com/emersion/go-sasl" @@ -67,7 +68,9 @@ type Endpoint struct { deferServerReject bool maxLoggedRcptErrors int maxReceived int - maxHeaderBytes int + maxHeaderBytes int64 + + sessionCnt atomic.Int32 authNormalize authz.NormalizeFunc authMap module.Table @@ -401,6 +404,8 @@ func (endp *Endpoint) NewSession(conn *smtp.Conn) (smtp.Session, error) { return nil, endp.wrapErr("", true, "EHLO", err) } + endp.sessionCnt.Add(1) + return sess, nil } @@ -447,6 +452,10 @@ func (endp *Endpoint) newSession(conn *smtp.Conn) *Session { return s } +func (endp *Endpoint) ConnectionCount() int { + return int(endp.sessionCnt.Load()) +} + func (endp *Endpoint) Close() error { endp.serv.Close() endp.listenersWg.Wait() diff --git a/internal/endpoint/smtp/smtp_test.go b/internal/endpoint/smtp/smtp_test.go index b825d155..c295d7c8 100644 --- a/internal/endpoint/smtp/smtp_test.go +++ b/internal/endpoint/smtp/smtp_test.go @@ -124,7 +124,7 @@ func submitMsgOpts(t *testing.T, cl *smtp.Client, from string, rcpts []string, o return err } for _, rcpt := range rcpts { - if err := cl.Rcpt(rcpt); err != nil { + if err := cl.Rcpt(rcpt, &smtp.RcptOptions{}); err != nil { return err } } @@ -334,9 +334,9 @@ func TestSMTPDeliver_CheckError_Deferred(t *testing.T) { } } - checkErr(cl.Rcpt("test1@example.org")) - checkErr(cl.Rcpt("test1@example.org")) - checkErr(cl.Rcpt("test2@example.org")) + checkErr(cl.Rcpt("test1@example.org", &smtp.RcptOptions{})) + checkErr(cl.Rcpt("test1@example.org", &smtp.RcptOptions{})) + checkErr(cl.Rcpt("test2@example.org", &smtp.RcptOptions{})) } func TestSMTPDelivery_Multi(t *testing.T) { @@ -394,7 +394,7 @@ func TestSMTPDelivery_AbortData(t *testing.T) { if err := cl.Mail("sender@example.org", nil); err != nil { t.Fatal(err) } - if err := cl.Rcpt("test@example.com"); err != nil { + if err := cl.Rcpt("test@example.com", &smtp.RcptOptions{}); err != nil { t.Fatal(err) } data, err := cl.Data() @@ -432,7 +432,7 @@ func TestSMTPDelivery_EmptyMessage(t *testing.T) { if err := cl.Mail("sender@example.org", nil); err != nil { t.Fatal(err) } - if err := cl.Rcpt("test@example.com"); err != nil { + if err := cl.Rcpt("test@example.com", &smtp.RcptOptions{}); err != nil { t.Fatal(err) } data, err := cl.Data() @@ -471,7 +471,7 @@ func TestSMTPDelivery_AbortLogout(t *testing.T) { if err := cl.Mail("sender@example.org", nil); err != nil { t.Fatal(err) } - if err := cl.Rcpt("test@example.com"); err != nil { + if err := cl.Rcpt("test@example.com", &smtp.RcptOptions{}); err != nil { t.Fatal(err) } @@ -499,7 +499,7 @@ func TestSMTPDelivery_Reset(t *testing.T) { if err := cl.Mail("from-garbage@example.org", nil); err != nil { t.Fatal(err) } - if err := cl.Rcpt("to-garbage@example.org"); err != nil { + if err := cl.Rcpt("to-garbage@example.org", &smtp.RcptOptions{}); err != nil { t.Fatal(err) } if err := cl.Reset(); err != nil { diff --git a/internal/msgpipeline/dmarc_test.go b/internal/msgpipeline/dmarc_test.go index 8b7e1222..f942baf8 100644 --- a/internal/msgpipeline/dmarc_test.go +++ b/internal/msgpipeline/dmarc_test.go @@ -30,6 +30,7 @@ import ( "github.com/emersion/go-message/textproto" "github.com/emersion/go-msgauth/authres" + "github.com/emersion/go-smtp" "github.com/foxcpp/go-mockdns" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/exterrors" @@ -59,7 +60,7 @@ func doTestDelivery(t *testing.T, tgt module.DeliveryTarget, from string, to []s return encodedID, err } for _, rcpt := range to { - if err := delivery.AddRcpt(context.Background(), rcpt); err != nil { + if err := delivery.AddRcpt(context.Background(), rcpt, smtp.RcptOptions{}); err != nil { if err := delivery.Abort(context.Background()); err != nil { t.Log("delivery.Abort:", err) } diff --git a/internal/msgpipeline/msgpipeline.go b/internal/msgpipeline/msgpipeline.go index aa63c5b3..388caab1 100644 --- a/internal/msgpipeline/msgpipeline.go +++ b/internal/msgpipeline/msgpipeline.go @@ -22,6 +22,7 @@ import ( "context" "github.com/emersion/go-message/textproto" + "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/address" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" @@ -276,7 +277,7 @@ type msgpipelineDelivery struct { checkRunner *checkRunner } -func (dd *msgpipelineDelivery) AddRcpt(ctx context.Context, to string) error { +func (dd *msgpipelineDelivery) AddRcpt(ctx context.Context, to string, opts smtp.RcptOptions) error { if err := dd.checkRunner.checkRcpt(ctx, dd.d.globalChecks, to); err != nil { return err } @@ -363,7 +364,7 @@ func (dd *msgpipelineDelivery) AddRcpt(ctx context.Context, to string) error { return wrapErr(err) } - if err := delivery.AddRcpt(ctx, to); err != nil { + if err := delivery.AddRcpt(ctx, to, opts); err != nil { return wrapErr(err) } delivery.recipients = append(delivery.recipients, originalTo) diff --git a/internal/msgpipeline/msgpipeline_test.go b/internal/msgpipeline/msgpipeline_test.go index c9a4b1cd..9899eb03 100644 --- a/internal/msgpipeline/msgpipeline_test.go +++ b/internal/msgpipeline/msgpipeline_test.go @@ -24,6 +24,7 @@ import ( "testing" "github.com/emersion/go-message/textproto" + "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/modify" @@ -422,10 +423,10 @@ func TestMsgPipeline_PerRcptReject(t *testing.T) { } }() - if err := delivery.AddRcpt(context.Background(), "rcpt2@example.com"); err == nil { + if err := delivery.AddRcpt(context.Background(), "rcpt2@example.com", smtp.RcptOptions{}); err == nil { t.Fatalf("expected error for delivery.AddRcpt(rcpt2@example.com), got nil") } - if err := delivery.AddRcpt(context.Background(), "rcpt1@example.com"); err != nil { + if err := delivery.AddRcpt(context.Background(), "rcpt1@example.com", smtp.RcptOptions{}); err != nil { t.Fatalf("unexpected AddRcpt err for %s: %v", "rcpt1@example.com", err) } if err := delivery.Body(context.Background(), textproto.Header{}, buffer.MemoryBuffer{Slice: []byte("foobar")}); err != nil { diff --git a/internal/smtpconn/smtpconn.go b/internal/smtpconn/smtpconn.go index e6231f7c..451a8442 100644 --- a/internal/smtpconn/smtpconn.go +++ b/internal/smtpconn/smtpconn.go @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -// The package smtpconn contains the code shared between target.smtp and +// Package smtpconn contains the code shared between target.smtp and // remote modules. // // It implements the wrapper over the SMTP connection (go-smtp.Client) object @@ -222,13 +222,9 @@ func (c *C) attemptConnect(ctx context.Context, lmtp bool, endp config.Endpoint, c.lmtp = lmtp // This uses initial greeting timeout of 5 minutes (hardcoded). if lmtp { - cl, err = smtp.NewClientLMTP(conn, endp.Host) + cl = smtp.NewClientLMTP(conn) } else { - cl, err = smtp.NewClient(conn, endp.Host) - } - if err != nil { - conn.Close() - return false, nil, err + cl = smtp.NewClient(conn) } cl.CommandTimeout = c.CommandTimeout @@ -336,9 +332,13 @@ func (c *C) IsLMTP() bool { // // If the address is non-ASCII and cannot be converted to ASCII and the remote // server does not support SMTPUTF8, error will be returned. -func (c *C) Rcpt(ctx context.Context, to string) error { +func (c *C) Rcpt(ctx context.Context, to string, opts smtp.RcptOptions) error { defer trace.StartRegion(ctx, "smtpconn/RCPT TO").End() + outOpts := &smtp.RcptOptions{ + // TODO: DSN support + } + // If necessary, the extension flag is enabled in Start. if ok, _ := c.cl.Extension("SMTPUTF8"); !address.IsASCII(to) && !ok { var err error @@ -356,7 +356,7 @@ func (c *C) Rcpt(ctx context.Context, to string) error { } } - if err := c.cl.Rcpt(to); err != nil { + if err := c.cl.Rcpt(to, outOpts); err != nil { return c.wrapClientErr(err, c.serverName) } diff --git a/internal/smtpconn/smtputf8_test.go b/internal/smtpconn/smtputf8_test.go index 22acf410..dc580d9c 100644 --- a/internal/smtpconn/smtputf8_test.go +++ b/internal/smtpconn/smtputf8_test.go @@ -37,7 +37,7 @@ func doTestDelivery(t *testing.T, conn *C, from string, to []string, opts smtp.M return err } for _, rcpt := range to { - if err := conn.Rcpt(context.Background(), rcpt); err != nil { + if err := conn.Rcpt(context.Background(), rcpt, smtp.RcptOptions{}); err != nil { return err } } diff --git a/internal/storage/imapsql/delivery.go b/internal/storage/imapsql/delivery.go index 09a39496..60cb2e1f 100644 --- a/internal/storage/imapsql/delivery.go +++ b/internal/storage/imapsql/delivery.go @@ -25,6 +25,7 @@ import ( "github.com/emersion/go-imap" "github.com/emersion/go-imap/backend" "github.com/emersion/go-message/textproto" + "github.com/emersion/go-smtp" imapsql "github.com/foxcpp/go-imap-sql" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/exterrors" @@ -58,7 +59,7 @@ func userDoesNotExist(actual error) error { } } -func (d *delivery) AddRcpt(ctx context.Context, rcptTo string) error { +func (d *delivery) AddRcpt(ctx context.Context, rcptTo string, _ smtp.RcptOptions) error { defer trace.StartRegion(ctx, "sql/AddRcpt").End() accountName, err := d.store.deliveryNormalize(ctx, rcptTo) diff --git a/internal/storage/imapsql/imapsql.go b/internal/storage/imapsql/imapsql.go index ed27b052..711a34e1 100644 --- a/internal/storage/imapsql/imapsql.go +++ b/internal/storage/imapsql/imapsql.go @@ -107,7 +107,7 @@ func (store *Storage) Init(cfg *config.Map) error { var ( driver string dsn []string - appendlimitVal = -1 + appendlimitVal int64 = -1 compression []string authNormalize string deliveryNormalize string @@ -232,7 +232,7 @@ func (store *Storage) Init(cfg *config.Map) error { } else { // int is 32-bit on some platforms, so cut off values we can't actually // use. - if int(uint32(appendlimitVal)) != appendlimitVal { + if int64(uint32(appendlimitVal)) != appendlimitVal { return errors.New("imapsql: appendlimit value is too big") } opts.MaxMsgBytes = new(uint32) diff --git a/internal/target/queue/queue.go b/internal/target/queue/queue.go index b749a850..2c7a0394 100644 --- a/internal/target/queue/queue.go +++ b/internal/target/queue/queue.go @@ -30,12 +30,12 @@ All scheduled deliveries are attempted to the configured DeliveryTarget. All metadata is preserved on disk. Failure status is determined on per-recipient basis: -- Delivery.Start fail handled as a failure for all recipients. -- Delivery.AddRcpt fail handled as a failure for the corresponding recipient. -- Delivery.Body fail handled as a failure for all recipients. -- If Delivery implements PartialDelivery, then - PartialDelivery.BodyNonAtomic is used instead. Failures are determined based - on StatusCollector.SetStatus calls done by target in this case. + - Delivery.Start fail handled as a failure for all recipients. + - Delivery.AddRcpt fail handled as a failure for the corresponding recipient. + - Delivery.Body fail handled as a failure for all recipients. + - If Delivery implements PartialDelivery, then + PartialDelivery.BodyNonAtomic is used instead. Failures are determined based + on StatusCollector.SetStatus calls done by target in this case. For each failure check is done to see if it is a permanent failure or a temporary one. This is done using exterrors.IsTemporaryOrUnspec. @@ -487,7 +487,7 @@ func (q *Queue) deliver(meta *QueueMetadata, header textproto.Header, body buffe var acceptedRcpts []string for _, rcpt := range meta.To { rcptCtx, rcptTask := trace.NewTask(msgCtx, "RCPT TO") - if err := delivery.AddRcpt(rcptCtx, rcpt); err != nil { + if err := delivery.AddRcpt(rcptCtx, rcpt, smtp.RcptOptions{} /* TODO: DSN support */); err != nil { dl.Debugf("delivery.AddRcpt %s failed: %v", rcpt, err) perr.Errs[rcpt] = err } else { @@ -558,7 +558,7 @@ type queueDelivery struct { body buffer.Buffer } -func (qd *queueDelivery) AddRcpt(ctx context.Context, rcptTo string) error { +func (qd *queueDelivery) AddRcpt(ctx context.Context, rcptTo string, _ smtp.RcptOptions) error { qd.meta.To = append(qd.meta.To, rcptTo) return nil } @@ -975,7 +975,7 @@ func (q *Queue) emitDSN(meta *QueueMetadata, header textproto.Header, failedRcpt }() rcptCtx, rcptTask := trace.NewTask(msgCtx, "RCPT TO") - if err = dsnDelivery.AddRcpt(rcptCtx, meta.From); err != nil { + if err = dsnDelivery.AddRcpt(rcptCtx, meta.From, smtp.RcptOptions{}); err != nil { rcptTask.End() return } diff --git a/internal/target/queue/queue_test.go b/internal/target/queue/queue_test.go index 1ffaece1..ff9a4f6a 100644 --- a/internal/target/queue/queue_test.go +++ b/internal/target/queue/queue_test.go @@ -33,6 +33,7 @@ import ( "time" "github.com/emersion/go-message/textproto" + "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/log" @@ -104,7 +105,7 @@ type unreliableTargetDeliveryPartial struct { *unreliableTargetDelivery } -func (utd *unreliableTargetDelivery) AddRcpt(ctx context.Context, rcptTo string) error { +func (utd *unreliableTargetDelivery) AddRcpt(ctx context.Context, rcptTo string, _ smtp.RcptOptions) error { if len(utd.ut.rcptFailures)> utd.ut.passedMessages { rcptErrs := utd.ut.rcptFailures[utd.ut.passedMessages] if err := rcptErrs[rcptTo]; err != nil { @@ -610,7 +611,7 @@ func TestQueueDelivery_AbortNoDangling(t *testing.T) { t.Fatalf("unexpected Start err: %v", err) } for _, rcpt := range [...]string{"test@example.org", "test2@example.org"} { - if err := delivery.AddRcpt(context.Background(), rcpt); err != nil { + if err := delivery.AddRcpt(context.Background(), rcpt, smtp.RcptOptions{}); err != nil { t.Fatalf("unexpected AddRcpt err for %s: %v", rcpt, err) } } @@ -790,7 +791,7 @@ func TestQueueDSN_RcptRewrite(t *testing.T) { t.Fatalf("unexpected Start err: %v", err) } for _, rcpt := range [...]string{"test@example.org", "test2@example.org"} { - if err := delivery.AddRcpt(context.Background(), rcpt); err != nil { + if err := delivery.AddRcpt(context.Background(), rcpt, smtp.RcptOptions{}); err != nil { t.Fatalf("unexpected AddRcpt err for %s: %v", rcpt, err) } } diff --git a/internal/target/remote/remote.go b/internal/target/remote/remote.go index 69be9d10..6fded8e8 100644 --- a/internal/target/remote/remote.go +++ b/internal/target/remote/remote.go @@ -35,6 +35,7 @@ import ( "time" "github.com/emersion/go-message/textproto" + "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/address" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" @@ -269,7 +270,7 @@ func (rt *Target) Start(ctx context.Context, msgMeta *module.MsgMetadata, mailFr }, nil } -func (rd *remoteDelivery) AddRcpt(ctx context.Context, to string) error { +func (rd *remoteDelivery) AddRcpt(ctx context.Context, to string, opts smtp.RcptOptions) error { defer trace.StartRegion(ctx, "remote/AddRcpt").End() if rd.msgMeta.Quarantine { @@ -311,7 +312,7 @@ func (rd *remoteDelivery) AddRcpt(ctx context.Context, to string) error { return err } - if err := conn.Rcpt(ctx, to); err != nil { + if err := conn.Rcpt(ctx, to, opts); err != nil { return moduleError(err) } diff --git a/internal/target/remote/remote_test.go b/internal/target/remote/remote_test.go index d8064f67..ae561582 100644 --- a/internal/target/remote/remote_test.go +++ b/internal/target/remote/remote_test.go @@ -154,7 +154,7 @@ func TestRemoteDelivery_NoMXFallback(t *testing.T) { t.Fatal(err) } - if err := delivery.AddRcpt(context.Background(), "test@example.invalid"); err == nil { + if err := delivery.AddRcpt(context.Background(), "test@example.invalid", smtp.RcptOptions{}); err == nil { t.Fatal("Expected an error, got none") } @@ -275,7 +275,7 @@ func TestRemoteDelivery_Abort(t *testing.T) { t.Fatal(err) } - if err := delivery.AddRcpt(context.Background(), "test@example.invalid"); err != nil { + if err := delivery.AddRcpt(context.Background(), "test@example.invalid", smtp.RcptOptions{}); err != nil { t.Fatal(err) } @@ -305,7 +305,7 @@ func TestRemoteDelivery_CommitWithoutBody(t *testing.T) { t.Fatal(err) } - if err := delivery.AddRcpt(context.Background(), "test@example.invalid"); err != nil { + if err := delivery.AddRcpt(context.Background(), "test@example.invalid", smtp.RcptOptions{}); err != nil { t.Fatal(err) } @@ -342,7 +342,7 @@ func TestRemoteDelivery_MAILFROMErr(t *testing.T) { t.Fatal(err) } - err = delivery.AddRcpt(context.Background(), "test@example.invalid") + err = delivery.AddRcpt(context.Background(), "test@example.invalid", smtp.RcptOptions{}) testutils.CheckSMTPErr(t, err, 550, exterrors.EnhancedCode{5, 1, 2}, "mx.example.invalid. said: Hey") if err := delivery.Abort(context.Background()); err != nil { @@ -368,7 +368,7 @@ func TestRemoteDelivery_NoMX(t *testing.T) { t.Fatal(err) } - if err := delivery.AddRcpt(context.Background(), "test@example.invalid"); err == nil { + if err := delivery.AddRcpt(context.Background(), "test@example.invalid", smtp.RcptOptions{}); err == nil { t.Fatal("Expected an error, got none") } @@ -398,7 +398,7 @@ func TestRemoteDelivery_NullMX(t *testing.T) { t.Fatal(err) } - err = delivery.AddRcpt(context.Background(), "test@example.invalid") + err = delivery.AddRcpt(context.Background(), "test@example.invalid", smtp.RcptOptions{}) testutils.CheckSMTPErr(t, err, 556, exterrors.EnhancedCode{5, 1, 10}, "Domain does not accept email (null MX)") if err := delivery.Abort(context.Background()); err != nil { @@ -429,7 +429,7 @@ func TestRemoteDelivery_Quarantined(t *testing.T) { t.Fatal(err) } - if err := delivery.AddRcpt(context.Background(), "test@example.invalid"); err != nil { + if err := delivery.AddRcpt(context.Background(), "test@example.invalid", smtp.RcptOptions{}); err != nil { t.Fatal(err) } @@ -475,10 +475,10 @@ func TestRemoteDelivery_MAILFROMErr_Repeated(t *testing.T) { t.Fatal(err) } - err = delivery.AddRcpt(context.Background(), "test@example.invalid") + err = delivery.AddRcpt(context.Background(), "test@example.invalid", smtp.RcptOptions{}) testutils.CheckSMTPErr(t, err, 550, exterrors.EnhancedCode{5, 1, 2}, "mx.example.invalid. said: Hey") - err = delivery.AddRcpt(context.Background(), "test2@example.invalid") + err = delivery.AddRcpt(context.Background(), "test2@example.invalid", smtp.RcptOptions{}) testutils.CheckSMTPErr(t, err, 550, exterrors.EnhancedCode{5, 1, 2}, "mx.example.invalid. said: Hey") if err := delivery.Abort(context.Background()); err != nil { @@ -515,12 +515,12 @@ func TestRemoteDelivery_RcptErr(t *testing.T) { t.Fatal(err) } - err = delivery.AddRcpt(context.Background(), "test@example.invalid") + err = delivery.AddRcpt(context.Background(), "test@example.invalid", smtp.RcptOptions{}) testutils.CheckSMTPErr(t, err, 550, exterrors.EnhancedCode{5, 1, 2}, "mx.example.invalid. said: Hey") // It should be possible to, however, add another recipient and continue // delivery as if nothing happened. - if err := delivery.AddRcpt(context.Background(), "test2@example.invalid"); err != nil { + if err := delivery.AddRcpt(context.Background(), "test2@example.invalid", smtp.RcptOptions{}); err != nil { t.Fatal(err) } @@ -659,14 +659,14 @@ func TestRemoteDelivery_Split_Fail(t *testing.T) { t.Fatal(err) } - err = delivery.AddRcpt(context.Background(), "test@example.invalid") + err = delivery.AddRcpt(context.Background(), "test@example.invalid", smtp.RcptOptions{}) if err == nil { t.Fatal("Expected an error, got none") } // It should be possible to, however, add another recipient and continue // delivery as if nothing happened. - if err := delivery.AddRcpt(context.Background(), "test@example2.invalid"); err != nil { + if err := delivery.AddRcpt(context.Background(), "test@example2.invalid", smtp.RcptOptions{}); err != nil { t.Fatal(err) } @@ -712,7 +712,7 @@ func TestRemoteDelivery_BodyErr(t *testing.T) { t.Fatal(err) } - err = delivery.AddRcpt(context.Background(), "test@example.invalid") + err = delivery.AddRcpt(context.Background(), "test@example.invalid", smtp.RcptOptions{}) if err != nil { t.Fatal(err) } @@ -766,10 +766,10 @@ func TestRemoteDelivery_Split_BodyErr(t *testing.T) { t.Fatal(err) } - if err := delivery.AddRcpt(context.Background(), "test@example.invalid"); err != nil { + if err := delivery.AddRcpt(context.Background(), "test@example.invalid", smtp.RcptOptions{}); err != nil { t.Fatal(err) } - if err := delivery.AddRcpt(context.Background(), "test@example2.invalid"); err != nil { + if err := delivery.AddRcpt(context.Background(), "test@example2.invalid", smtp.RcptOptions{}); err != nil { t.Fatal(err) } @@ -822,13 +822,13 @@ func TestRemoteDelivery_Split_BodyErr_NonAtomic(t *testing.T) { t.Fatal(err) } - if err := delivery.AddRcpt(context.Background(), "test@example.invalid"); err != nil { + if err := delivery.AddRcpt(context.Background(), "test@example.invalid", smtp.RcptOptions{}); err != nil { t.Fatal(err) } - if err := delivery.AddRcpt(context.Background(), "test2@example.invalid"); err != nil { + if err := delivery.AddRcpt(context.Background(), "test2@example.invalid", smtp.RcptOptions{}); err != nil { t.Fatal(err) } - if err := delivery.AddRcpt(context.Background(), "test@example2.invalid"); err != nil { + if err := delivery.AddRcpt(context.Background(), "test@example2.invalid", smtp.RcptOptions{}); err != nil { t.Fatal(err) } diff --git a/internal/target/smtp/smtp_downstream.go b/internal/target/smtp/smtp_downstream.go index 1e631c19..f03fc827 100644 --- a/internal/target/smtp/smtp_downstream.go +++ b/internal/target/smtp/smtp_downstream.go @@ -251,8 +251,8 @@ func (d *delivery) connect(ctx context.Context) error { return nil } -func (d *delivery) AddRcpt(ctx context.Context, rcptTo string) error { - err := d.conn.Rcpt(ctx, rcptTo) +func (d *delivery) AddRcpt(ctx context.Context, rcptTo string, opts smtp.RcptOptions) error { + err := d.conn.Rcpt(ctx, rcptTo, opts) if err != nil { return d.u.moduleError(err) } diff --git a/internal/testutils/bench_delivery.go b/internal/testutils/bench_delivery.go index 834bcf41..f434efc0 100644 --- a/internal/testutils/bench_delivery.go +++ b/internal/testutils/bench_delivery.go @@ -29,6 +29,7 @@ import ( "testing" "github.com/emersion/go-message/textproto" + "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/module" ) @@ -124,7 +125,7 @@ func BenchDelivery(b *testing.B, target module.DeliveryTarget, sender string, re for i, rcptTemplate := range recipientTemplates { rcpt := strings.Replace(rcptTemplate, "X", strconv.Itoa(i), -1) - if err := delivery.AddRcpt(benchCtx, rcpt); err != nil { + if err := delivery.AddRcpt(benchCtx, rcpt, smtp.RcptOptions{}); err != nil { b.Fatal(err) } } diff --git a/internal/testutils/smtp_server.go b/internal/testutils/smtp_server.go index 7c82b640..6fb105a6 100644 --- a/internal/testutils/smtp_server.go +++ b/internal/testutils/smtp_server.go @@ -25,6 +25,7 @@ import ( "net" "reflect" "sort" + "sync/atomic" "testing" "time" @@ -53,10 +54,13 @@ type SMTPBackend struct { RcptErr map[string]error DataErr error LMTPDataErr []error + + ActiveSessionsCounter atomic.Int32 } func (be *SMTPBackend) NewSession(conn *smtp.Conn) (smtp.Session, error) { be.SessionCounter++ + be.ActiveSessionsCounter.Add(1) if be.SourceEndpoints == nil { be.SourceEndpoints = make(map[string]struct{}) } @@ -67,6 +71,10 @@ func (be *SMTPBackend) NewSession(conn *smtp.Conn) (smtp.Session, error) { }, nil } +func (be *SMTPBackend) ConnectionCount() int { + return int(be.ActiveSessionsCounter.Load()) +} + func (be *SMTPBackend) CheckMsg(t *testing.T, indx int, from string, rcptTo []string) { t.Helper() @@ -104,6 +112,7 @@ func (s *session) Reset() { } func (s *session) Logout() error { + s.backend.ActiveSessionsCounter.Add(-1) return nil } @@ -129,7 +138,7 @@ func (s *session) Mail(from string, opts *smtp.MailOptions) error { return nil } -func (s *session) Rcpt(to string) error { +func (s *session) Rcpt(to string, _ *smtp.RcptOptions) error { if err := s.backend.RcptErr[to]; err != nil { return err } @@ -368,17 +377,23 @@ func SMTPServerTLS(t *testing.T, addr string, fn ...SMTPServerConfigureFunc) (*t return clientCfg, be, s } +type smtpBackendConnCounter interface { + ConnectionCount() int +} + func CheckSMTPConnLeak(t *testing.T, srv *smtp.Server) { t.Helper() + ccb, ok := srv.Backend.(smtpBackendConnCounter) + if !ok { + t.Error("CheckSMTPConnLeak used for smtp.Server with backend without ConnectionCount method") + return + } + // Connection closure is handled asynchronously, so before failing // wait a bit for handleQuit in go-smtp to do its work. for i := 0; i < 10; i++ { - found := false - srv.ForEachConn(func(_ *smtp.Conn) { - found = true - }) - if !found { + if ccb.ConnectionCount() == 0 { return } time.Sleep(100 * time.Millisecond) diff --git a/internal/testutils/target.go b/internal/testutils/target.go index e573bdc4..68f5b394 100644 --- a/internal/testutils/target.go +++ b/internal/testutils/target.go @@ -29,6 +29,7 @@ import ( "testing" "github.com/emersion/go-message/textproto" + "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" "github.com/foxcpp/maddy/framework/exterrors" @@ -100,7 +101,7 @@ func (dt *Target) Start(ctx context.Context, msgMeta *module.MsgMetadata, mailFr }, dt.StartErr } -func (dtd *testTargetDelivery) AddRcpt(ctx context.Context, to string) error { +func (dtd *testTargetDelivery) AddRcpt(ctx context.Context, to string, _ smtp.RcptOptions) error { if dtd.tgt.RcptErr != nil { if err := dtd.tgt.RcptErr[to]; err != nil { return err @@ -219,7 +220,7 @@ func DoTestDeliveryNonAtomic(t *testing.T, c module.StatusCollector, tgt module. } for _, rcpt := range to { t.Log("-- delivery.AddRcpt", rcpt) - if err := delivery.AddRcpt(testCtx, rcpt); err != nil { + if err := delivery.AddRcpt(testCtx, rcpt, smtp.RcptOptions{}); err != nil { t.Log("-- ... delivery.AddRcpt", rcpt, err, exterrors.Fields(err)) t.Log("-- delivery.Abort") if err := delivery.Abort(testCtx); err != nil { @@ -269,7 +270,7 @@ func DoTestDeliveryErrMeta(t *testing.T, tgt module.DeliveryTarget, from string, } for _, rcpt := range to { t.Log("-- delivery.AddRcpt", rcpt) - if err := delivery.AddRcpt(testCtx, rcpt); err != nil { + if err := delivery.AddRcpt(testCtx, rcpt, smtp.RcptOptions{}); err != nil { t.Log("-- ... delivery.AddRcpt", rcpt, err, exterrors.Fields(err)) t.Log("-- delivery.Abort") if err := delivery.Abort(testCtx); err != nil { diff --git a/tests/limits_test.go b/tests/limits_test.go index f085bb36..e219add7 100644 --- a/tests/limits_test.go +++ b/tests/limits_test.go @@ -51,14 +51,14 @@ func TestConcurrencyLimit(tt *testing.T) { c1 := t.Conn("smtp") defer c1.Close() c1.SMTPNegotation("localhost", nil, nil) - c1.Writeln("MAIL FROM:") c1.ExpectPattern("250 *") // Down on semaphore. c2 := t.Conn("smtp") defer c2.Close() c2.SMTPNegotation("localhost", nil, nil) - c1.Writeln("MAIL FROM:") // Temporary error due to lock timeout. c1.ExpectPattern("451 *") } @@ -87,21 +87,21 @@ func TestPerIPConcurrency(tt *testing.T) { c1 := t.Conn("smtp") defer c1.Close() c1.SMTPNegotation("localhost", nil, nil) - c1.Writeln("MAIL FROM:") c1.ExpectPattern("250 *") // Down on semaphore. c3 := t.Conn4("127.0.0.2", "smtp") defer c3.Close() c3.SMTPNegotation("localhost", nil, nil) - c3.Writeln("MAIL FROM:") c3.ExpectPattern("250 *") // Down on semaphore (different IP). c2 := t.Conn("smtp") defer c2.Close() c2.SMTPNegotation("localhost", nil, nil) - c1.Writeln("MAIL FROM:") // Temporary error due to lock timeout. c1.ExpectPattern("451 *") } diff --git a/tests/stress_test.go b/tests/stress_test.go index 55e5f76f..74ed268b 100644 --- a/tests/stress_test.go +++ b/tests/stress_test.go @@ -60,7 +60,7 @@ func TestSMTPFlood_FullMsg_NoLimits_1Conn(tt *testing.T) { defer c.Close() c.SMTPNegotation("helo.maddy.test", nil, nil) floodSmtp(&c, []string{ - "MAIL FROM:", "RCPT TO:", "DATA", "From: ", @@ -103,7 +103,7 @@ func TestSMTPFlood_FullMsg_NoLimits_10Conns(tt *testing.T) { defer c.Close() c.SMTPNegotation("helo.maddy.test", nil, nil) floodSmtp(&c, []string{ - "MAIL FROM:", "RCPT TO:", "DATA", "From: ", @@ -151,7 +151,7 @@ func TestSMTPFlood_EnvelopeAbort_NoLimits_10Conns(tt *testing.T) { defer c.Close() c.SMTPNegotation("helo.maddy.test", nil, nil) floodSmtp(&c, []string{ - "MAIL FROM:", "RCPT TO:", "RSET", }, []string{ @@ -202,7 +202,7 @@ func TestSMTPFlood_EnvelopeAbort_Ratelimited(tt *testing.T) { defer c.Close() c.SMTPNegotation("helo.maddy.test", nil, nil) floodSmtp(&c, []string{ - "MAIL FROM:", "RCPT TO:", "RSET", }, []string{ @@ -265,7 +265,7 @@ func TestSMTPFlood_FullMsg_Ratelimited_PerSource(tt *testing.T) { defer c.Close() c.SMTPNegotation("helo.maddy.test", nil, nil) floodSmtp(&c, []string{ - "MAIL FROM:", "RCPT TO:", "DATA", "From: ", @@ -292,7 +292,7 @@ func TestSMTPFlood_FullMsg_Ratelimited_PerSource(tt *testing.T) { defer c.Close() c.SMTPNegotation("helo.maddy.test", nil, nil) floodSmtp(&c, []string{ - "MAIL FROM:", "RCPT TO:", "DATA", "From: ", @@ -364,7 +364,7 @@ func TestSMTPFlood_EnvelopeAbort_Ratelimited_PerIP(tt *testing.T) { defer c.Close() c.SMTPNegotation("helo.maddy.test", nil, nil) floodSmtp(&c, []string{ - "MAIL FROM:", "RCPT TO:", "RSET", }, []string{ @@ -383,7 +383,7 @@ func TestSMTPFlood_EnvelopeAbort_Ratelimited_PerIP(tt *testing.T) { defer c.Close() c.SMTPNegotation("helo.maddy.test", nil, nil) floodSmtp(&c, []string{ - "MAIL FROM:", "RCPT TO:", "RSET", }, []string{ From 09c9486999e7256537577570bac78d04907b99a4 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月21日 15:03:16 +0300 Subject: [PATCH 036/171] ci: Add apt-get update --- .github/workflows/cicd.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index ddff0c69..738f672b 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -14,7 +14,9 @@ jobs: steps: - uses: actions/checkout@v2 - name: "Install libpam" - run: sudo apt-get install -y libpam-dev + run: | + sudo apt-get update + sudo apt-get install -y libpam-dev - uses: actions/cache@v2 with: path: | From 5b5fb72b02c6cb744a57471d0ef12a355ba12940 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月21日 19:07:43 +0300 Subject: [PATCH 037/171] go get -u ./... && go mod tidy --- go.mod | 169 +++++++++++++----------- go.sum | 408 ++++++++++++++++++++++++++++++--------------------------- 2 files changed, 303 insertions(+), 274 deletions(-) diff --git a/go.mod b/go.mod index f6209de2..40681b53 100644 --- a/go.mod +++ b/go.mod @@ -5,32 +5,32 @@ go 1.19 require ( blitiri.com.ar/go/spf v1.5.1 github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 - github.com/caddyserver/certmagic v0.17.2 + github.com/caddyserver/certmagic v0.20.0 github.com/emersion/go-imap v1.2.2-0.20220928192137-6fac715be9cf github.com/emersion/go-imap-compress v0.0.0-20201103190257-14809af1d1b9 github.com/emersion/go-imap-sortthread v1.2.0 - github.com/emersion/go-message v0.16.0 - github.com/emersion/go-milter v0.3.3 - github.com/emersion/go-msgauth v0.6.6 - github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead + github.com/emersion/go-message v0.18.0 + github.com/emersion/go-milter v0.4.0 + github.com/emersion/go-msgauth v0.6.8 + github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 github.com/emersion/go-smtp v0.20.2-0.20240121112028-434ddca4792e github.com/foxcpp/go-dovecot-sasl v0.0.0-20200522223722-c4699d7a24bf github.com/foxcpp/go-imap-backend-tests v0.0.0-20220105184719-e80aa29a5e16 github.com/foxcpp/go-imap-i18nlevel v0.0.0-20200208001533-d6ec88553005 github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613 github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed - github.com/foxcpp/go-imap-sql v0.5.1-0.20240120174134-48f9dc0b4abf + github.com/foxcpp/go-imap-sql v0.5.1-0.20240121160244-7f314a0fe78a github.com/foxcpp/go-mockdns v1.0.0 github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8 - github.com/go-ldap/ldap/v3 v3.4.4 + github.com/go-ldap/ldap/v3 v3.4.6 github.com/go-sql-driver/mysql v1.7.1 - github.com/google/uuid v1.3.0 - github.com/hashicorp/go-hclog v1.5.0 + github.com/google/uuid v1.5.0 + github.com/hashicorp/go-hclog v1.6.2 github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c - github.com/lib/pq v1.10.6 - github.com/libdns/alidns v1.0.3-0.20220501125541-4a895238a95d + github.com/lib/pq v1.10.9 + github.com/libdns/alidns v1.0.3 github.com/libdns/cloudflare v0.1.1-0.20221006221909-9d3ab3c3cddd - github.com/libdns/digitalocean v0.0.0-20220518195853-a541bc8aa80f + github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea github.com/libdns/gandi v1.0.3-0.20220921161957-dcd0274d2c79 github.com/libdns/googleclouddns v1.1.0 github.com/libdns/hetzner v0.0.1 @@ -41,121 +41,130 @@ require ( github.com/libdns/namedotcom v0.3.3 github.com/libdns/route53 v1.3.3 github.com/libdns/vultr v1.0.0 - github.com/mattn/go-sqlite3 v2.0.3+incompatible - github.com/miekg/dns v1.1.54 - github.com/minio/minio-go/v7 v7.0.55 + github.com/mattn/go-sqlite3 v1.14.19 + github.com/miekg/dns v1.1.58 + github.com/minio/minio-go/v7 v7.0.66 github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6 - github.com/prometheus/client_golang v1.15.1 - github.com/urfave/cli/v2 v2.25.5 - go.uber.org/zap v1.24.0 - golang.org/x/crypto v0.17.0 - golang.org/x/net v0.17.0 - golang.org/x/sync v0.2.0 + github.com/prometheus/client_golang v1.18.0 + github.com/urfave/cli/v2 v2.27.1 + go.uber.org/zap v1.26.0 + golang.org/x/crypto v0.18.0 + golang.org/x/net v0.20.0 + golang.org/x/sync v0.6.0 golang.org/x/text v0.14.0 modernc.org/sqlite v1.28.0 ) require ( - cloud.google.com/go/compute v1.19.3 // indirect + cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/aws/aws-sdk-go v1.44.40 // indirect - github.com/aws/aws-sdk-go-v2 v1.18.0 // indirect - github.com/aws/aws-sdk-go-v2/config v1.18.25 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.24 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 // indirect - github.com/aws/aws-sdk-go-v2/service/route53 v1.28.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 // indirect - github.com/aws/smithy-go v1.13.5 // indirect + github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect + github.com/aws/aws-sdk-go-v2/config v1.26.5 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect + github.com/aws/aws-sdk-go-v2/service/route53 v1.37.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect + github.com/aws/smithy-go v1.19.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/digitalocean/godo v1.99.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect + github.com/digitalocean/godo v1.108.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect - github.com/fatih/color v1.15.0 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/s2a-go v0.1.4 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.9.1 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-retryablehttp v0.7.2 // indirect + github.com/hashicorp/go-retryablehttp v0.7.5 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/klauspost/compress v1.16.5 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/klauspost/compress v1.17.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/mholt/acmez v1.1.1 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mholt/acmez v1.2.0 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.10.1 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.46.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rs/xid v1.5.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 // indirect - github.com/sirupsen/logrus v1.9.2 // indirect - github.com/spf13/afero v1.9.5 // indirect - github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.15.0 // indirect - github.com/subosito/gotenv v1.4.2 // indirect - github.com/vultr/govultr/v3 v3.0.2 // indirect - github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + github.com/spf13/viper v1.18.2 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/vultr/govultr/v3 v3.6.1 // indirect + github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect + github.com/zeebo/blake3 v0.2.3 // indirect go.opencensus.io v0.24.0 // indirect - go.uber.org/atomic v1.11.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect + go.opentelemetry.io/otel v1.22.0 // indirect + go.opentelemetry.io/otel/metric v1.22.0 // indirect + go.opentelemetry.io/otel/trace v1.22.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/mod v0.10.0 // indirect - golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.9.1 // indirect - google.golang.org/api v0.124.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e // indirect - google.golang.org/grpc v1.56.3 // indirect - google.golang.org/protobuf v1.30.0 // indirect + golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/oauth2 v0.16.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.17.0 // indirect + google.golang.org/api v0.157.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect + google.golang.org/grpc v1.60.1 // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools v2.2.0+incompatible // indirect - lukechampine.com/uint128 v1.2.0 // indirect - modernc.org/cc/v3 v3.40.0 // indirect - modernc.org/ccgo/v3 v3.16.13 // indirect - modernc.org/libc v1.29.0 // indirect + lukechampine.com/uint128 v1.3.0 // indirect + modernc.org/cc/v3 v3.41.0 // indirect + modernc.org/ccgo/v3 v3.16.15 // indirect + modernc.org/libc v1.40.6 // indirect modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.7.2 // indirect modernc.org/opt v0.1.3 // indirect - modernc.org/strutil v1.1.3 // indirect - modernc.org/token v1.0.1 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect ) replace github.com/emersion/go-imap => github.com/foxcpp/go-imap v1.0.0-beta.1.0.20220623182312-df940c324887 diff --git a/go.sum b/go.sum index bdaaeeb7..5db98a55 100644 --- a/go.sum +++ b/go.sum @@ -5,7 +5,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -18,7 +17,6 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= @@ -34,7 +32,7 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9 cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= +cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= @@ -71,8 +69,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds= -cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= +cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= @@ -168,7 +166,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= @@ -183,61 +180,65 @@ cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuW cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 h1:IEjq88XO4PuBDcvmjQJcQGg+w+UaafSy8G5Kcb5tBhI= github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5/go.mod h1:exZ0C/1emQJAw5tHOaUDyY1ycttqBAPcxuzf7QbY6ec= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= +github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/aws/aws-sdk-go v1.17.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.44.40 h1:MR0qefjBJrZuXE0VoeKMQFtjS2tUeVpbQNfb7NzQNgI= github.com/aws/aws-sdk-go v1.44.40/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go-v2 v1.17.8/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2 v1.18.0 h1:882kkTpSFhdgYRKVZ/VCgf7sd0ru57p2JCxz4/oN5RY= -github.com/aws/aws-sdk-go-v2 v1.18.0/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= +github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU= +github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= github.com/aws/aws-sdk-go-v2/config v1.18.21/go.mod h1:+jPQiVPz1diRnjj6VGqWcLK6EzNmQ42l7J3OqGTLsSY= -github.com/aws/aws-sdk-go-v2/config v1.18.25 h1:JuYyZcnMPBiFqn87L2cRppo+rNwgah6YwD3VuyvaW6Q= -github.com/aws/aws-sdk-go-v2/config v1.18.25/go.mod h1:dZnYpD5wTW/dQF0rRNLVypB396zWCcPiBIvdvSWHEg4= +github.com/aws/aws-sdk-go-v2/config v1.26.5 h1:lodGSevz7d+kkFJodfauThRxK9mdJbyutUxGq1NNhvw= +github.com/aws/aws-sdk-go-v2/config v1.26.5/go.mod h1:DxHrz6diQJOc9EwDslVRh84VjjrE17g+pVZXUeSxaDU= github.com/aws/aws-sdk-go-v2/credentials v1.13.20/go.mod h1:xtZnXErtbZ8YGXC3+8WfajpMBn5Ga/3ojZdxHq6iI8o= -github.com/aws/aws-sdk-go-v2/credentials v1.13.24 h1:PjiYyls3QdCrzqUN35jMWtUK1vqVZ+zLfdOa/UPFDp0= -github.com/aws/aws-sdk-go-v2/credentials v1.13.24/go.mod h1:jYPYi99wUOPIFi0rhiOvXeSEReVOzBqFNOX5bXYoG2o= +github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8= +github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.2/go.mod h1:cDh1p6XkSGSwSRIArWRc6+UqAQ7x4alQ0QfpVR6f+co= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3 h1:jJPgroehGvjrde3XufFIJUZVK5A2L9a3KwSFgKy9n8w= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.3/go.mod h1:4Q0UFP0YJf0NrsEuEYHpM9fTSEVnD16Z3uyEF7J9JGM= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.32/go.mod h1:RudqOgadTWdcS3t/erPQo24pcVEoYyqj/kKW5Vya21I= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33 h1:kG5eQilShqmJbv11XL1VpyDbaEJzWxd4zRiCG30GSn4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.33/go.mod h1:7i0PF1ME/2eUPFcjkVIwq+DOygHEoK92t5cDqNgYbIw= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.26/go.mod h1:vq86l7956VgFr0/FWQ2BWnK07QC3WYsepKzy33qqY5U= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27 h1:vFQlirhuM8lLlpI7imKOMsjdQLuN9CPi+k44F/OFVsk= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.27/go.mod h1:UrHnn3QV/d0pBZ6QBAEQcqFLf8FAzLmoUfPVIueOvoM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.33/go.mod h1:zG2FcwjQarWaqXSCGpgcr3RSjZ6dHGguZSppUL0XR7Q= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34 h1:gGLG7yKaXG02/jBlg210R7VgQIotiQntNhsCFejawx8= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.34/go.mod h1:Etz2dj6UHYuw+Xw830KfzCfWGMzqvUTCjUj5b76GVDc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM= +github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.26/go.mod h1:Bd4C/4PkVGubtNe5iMXu5BNnaBi/9t/UsFspPt4ram8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27 h1:0iKliEXAcCa2qVtRs7Ot5hItA2MsufrphbRFlz1Owxo= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.27/go.mod h1:EOwBD4J4S5qYszS5/3DpkejfuK+Z5/1uzICfPaZLtqw= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino= github.com/aws/aws-sdk-go-v2/service/route53 v1.27.7/go.mod h1:Jhu94omkrksnqX6Xs4Qo10eA1Fx+2NYKjZMU4GvZLp0= -github.com/aws/aws-sdk-go-v2/service/route53 v1.28.1 h1:8e1fgdyer5IqBPtiWNsVLY/XFucmNTtYMqADyCFXTgQ= -github.com/aws/aws-sdk-go-v2/service/route53 v1.28.1/go.mod h1:9SEpwqaALzp34eCT6w5PTh4SDDT84wxfMRx9VJSJPsk= +github.com/aws/aws-sdk-go-v2/service/route53 v1.37.0 h1:f3hBZWtpn9clZGXJoqahQeec9ZPZnu22g8pg+zNyif0= +github.com/aws/aws-sdk-go-v2/service/route53 v1.37.0/go.mod h1:8qqfpG4mug2JLlEyWPSFhEGvJiaZ9iPmMDDMYc5Xtas= github.com/aws/aws-sdk-go-v2/service/sso v1.12.8/go.mod h1:GNIveDnP+aE3jujyUSH5aZ/rktsTM5EvtKnCqBZawdw= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.10 h1:UBQjaMTCKwyUYwiVnUt6toEJwGXsLBI6al083tpjJzY= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.10/go.mod h1:ouy2P4z6sJN70fR3ka3wD3Ro3KezSxU6eKGQI2+2fjI= +github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow= +github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.8/go.mod h1:44qFP1g7pfd+U+sQHLPalAPKnyfTZjJsYR4xIwsJy5o= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10 h1:PkHIIJs8qvq0e5QybnZoG1K/9QTrLr9OsqCIo59jOBA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.10/go.mod h1:AFvkxc8xfBe8XA+5St5XIHHrQQtkxqrRincx4hmMHOk= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8= github.com/aws/aws-sdk-go-v2/service/sts v1.18.9/go.mod h1:yyW88BEPXA2fGFyI2KCcZC3dNpiT0CZAHaF+i656/tQ= -github.com/aws/aws-sdk-go-v2/service/sts v1.19.0 h1:2DQLAKDteoEDI8zpCzqBMaZlJuoE9iTYD0gFmXVax9E= -github.com/aws/aws-sdk-go-v2/service/sts v1.19.0/go.mod h1:BgQOMsg8av8jset59jelyPW7NoZcZXLVpDsXunGDrk8= -github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= +github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0= +github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U= github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= +github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE= -github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE= +github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc= +github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -256,36 +257,35 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/digitalocean/godo v1.41.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= -github.com/digitalocean/godo v1.99.0 h1:gUHO7n9bDaZFWvbzOum4bXE0/09ZuYA9yA8idQHX57E= -github.com/digitalocean/godo v1.99.0/go.mod h1:SsS2oXo2rznfM/nORlZ/6JaUJZFhmKTib1YhopUc8NA= +github.com/digitalocean/godo v1.108.0 h1:fWyMENvtxpCpva1UbKzOFnyAS04N1FNuBWWfPeTGquQ= +github.com/digitalocean/godo v1.108.0/go.mod h1:R6EmmWI8CT1+fCtjWY9UCB+L5uufuZH13wk3YhxycCs= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a/go.mod h1:ikgISoP7pRAolqsVP64yMteJa2FIpS6ju88eBT6K1yQ= github.com/emersion/go-imap-compress v0.0.0-20201103190257-14809af1d1b9 h1:7dmV11mle4UAQ7lX+Hdzx6akKFg3hVm/UUmQ7t6VgTQ= github.com/emersion/go-imap-compress v0.0.0-20201103190257-14809af1d1b9/go.mod h1:2Ro1PbmiqYiRe5Ct2sGR5hHaKSVHeRpVZwXx8vyYt98= github.com/emersion/go-imap-move v0.0.0-20180601155324-5eb20cb834bf/go.mod h1:QuMaZcKFDVI0yCrnAbPLfbwllz1wtOrZH8/vZ5yzp4w= -github.com/emersion/go-imap-sortthread v1.1.1-0.20200727121200-18e5fb409fed/go.mod h1:opHOzblOHZKQM1JEy+GPk1217giNLa7kleyWTN06qnc= github.com/emersion/go-imap-sortthread v1.2.0 h1:EMVEJXPWAhXMWECjR82Rn/tza6MddcvTwGAdTu1vJKU= github.com/emersion/go-imap-sortthread v1.2.0/go.mod h1:UhenCBupR+vSYRnqJkpjSq84INUCsyAK1MLpogv14pE= github.com/emersion/go-message v0.11.2/go.mod h1:C4jnca5HOTo4bGN9YdqNQM9sITuT3Y0K6bSUw9RklvY= github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4= -github.com/emersion/go-message v0.16.0 h1:uZLz8ClLv3V5fSFF/fFdW9jXjrZkXIpE1Fn8fKx7pO4= -github.com/emersion/go-message v0.16.0/go.mod h1:pDJDgf/xeUIF+eicT6B/hPX/ZbEorKkUMPOxrPVG2eQ= -github.com/emersion/go-milter v0.3.3 h1:DiP9Xmw2FqEuosNCd01XPDBb1K3OziNmt7BG2ddFlgs= -github.com/emersion/go-milter v0.3.3/go.mod h1:ablHK0pbLB83kMFBznp/Rj8aV+Kc3jw8cxzzmCNLIOY= -github.com/emersion/go-msgauth v0.6.6 h1:buv5lL8v/3v4RpHnQFS2IPhE3nxSRX+AxnrEJbDbHhA= -github.com/emersion/go-msgauth v0.6.6/go.mod h1:A+/zaz9bzukLM6tRWRgJ3BdrBi+TFKTvQ3fGMFOI9SM= -github.com/emersion/go-sasl v0.0.0-20190817083125-240c8404624e/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k= +github.com/emersion/go-message v0.18.0 h1:7LxAXHRpSeoO/Wom3ZApVZYG7c3d17yCScYce8WiXA8= +github.com/emersion/go-message v0.18.0/go.mod h1:Zi69ACvzaoV/MBnrxfVBPV3xWEuCmC2nEN39oJF4B8A= +github.com/emersion/go-milter v0.4.0 h1:HysxeAzNEToJw1VQEwLrjJqgmd1iuDzYg2329T/q6/Y= +github.com/emersion/go-milter v0.4.0/go.mod h1:ablHK0pbLB83kMFBznp/Rj8aV+Kc3jw8cxzzmCNLIOY= +github.com/emersion/go-msgauth v0.6.8 h1:kW/0E9E8Zx5CdKsERC/WnAvnXvX7q9wTHia1OA4944A= +github.com/emersion/go-msgauth v0.6.8/go.mod h1:YDwuyTCUHu9xxmAeVj0eW4INnwB6NNZoPdLerpSxRrc= github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k= github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= -github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead h1:fI1Jck0vUrXT8bnphprS1EoVRe2Q5CKCX8iDlpqjQ/Y= -github.com/emersion/go-sasl v0.0.0-20220912192320-0145f2c60ead/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= +github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 h1:hH4PQfOndHDlpzYfLAAfl63E8Le6F2+EL/cdhlkyRJY= +github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= github.com/emersion/go-smtp v0.20.2-0.20240121112028-434ddca4792e h1:WAPhaiA+bDO/mFgCDQJKCQI/RbH/73lCcis4Jb8Y2ec= github.com/emersion/go-smtp v0.20.2-0.20240121112028-434ddca4792e/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= @@ -302,8 +302,10 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/foxcpp/go-dovecot-sasl v0.0.0-20200522223722-c4699d7a24bf h1:rmBPY5fryjp9zLQYsUmQqqgsYq7qeVfrjtr96Tf9vD8= github.com/foxcpp/go-dovecot-sasl v0.0.0-20200522223722-c4699d7a24bf/go.mod h1:5yZUmwr851vgjyAfN7OEfnrmKOh/qLA5dbGelXYsu1E= github.com/foxcpp/go-imap v1.0.0-beta.1.0.20220623182312-df940c324887 h1:qUoaaHyrRpQw85ru6VQcC6JowdhrWl7lSbI1zRX1FTM= @@ -312,31 +314,34 @@ github.com/foxcpp/go-imap-backend-tests v0.0.0-20220105184719-e80aa29a5e16 h1:qh github.com/foxcpp/go-imap-backend-tests v0.0.0-20220105184719-e80aa29a5e16/go.mod h1:OPP1AgKxMPo3aHX5pcEZLQhhh5sllFcB8aUN9f6a6X8= github.com/foxcpp/go-imap-i18nlevel v0.0.0-20200208001533-d6ec88553005 h1:pfoFtkTTQ473qStSN79jhCFBWqMQt/3DQ3NGuXvT+50= github.com/foxcpp/go-imap-i18nlevel v0.0.0-20200208001533-d6ec88553005/go.mod h1:34FwxnjC2N+EFs2wMtsHevrZLWRKRuVU8wEcHWKq/nE= -github.com/foxcpp/go-imap-mess v0.0.0-20220625145025-3c40e241d099/go.mod h1:yESOLBW3uVSa7ncJYtDO1tnapt/xb9v1rrn8D5eXups= github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613 h1:fw9OWfPxP1CK4D+XAEEg0JzhvFGo04L+F5Xw55t9s3E= github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613/go.mod h1:P/O/qz4gaVkefzJ40BUtN/ZzBnaEg0YYe1no/SMp7Aw= github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed h1:1Jo7geyvunrPSjL6F6D9EcXoNApS5v3LQaro7aUNPnE= github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed/go.mod h1:Shows1vmkBWO40ChOClaUe6DUnZrsP1UPAuoWzIUdgQ= -github.com/foxcpp/go-imap-sql v0.5.1-0.20240120174134-48f9dc0b4abf h1:tqkJhHCPp1LL0tFqe0GwPjw2BMug8ivMtKJbQ7ZUA/g= -github.com/foxcpp/go-imap-sql v0.5.1-0.20240120174134-48f9dc0b4abf/go.mod h1:8uUTN2RRWZrETuA9pDvDr4SjV1hCvEYG2WOlXuupj+g= +github.com/foxcpp/go-imap-sql v0.5.1-0.20240121160244-7f314a0fe78a h1:/c5NvIHDrrU6+7glgr4YHwN3REH1bGb1l8s9S6ruORg= +github.com/foxcpp/go-imap-sql v0.5.1-0.20240121160244-7f314a0fe78a/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= github.com/foxcpp/go-mockdns v0.0.0-20191216195825-5eabd8dbfe1f/go.mod h1:tPg4cp4nseejPd+UKxtCVQ2hUxNTZ7qQZJa7CLriIeo= github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8 h1:k8w0iy6GP9oeSZWUH3p2DqZHaXDKZGNs3NZGZMGfQHc= github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8/go.mod h1:HO1YOCbBM8KjpgThMMFejHx6K/UsnEv2Oh9YGtBIlOU= github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= -github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= +github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs= -github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= +github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -390,8 +395,9 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= @@ -412,7 +418,6 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= @@ -420,16 +425,18 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc= -github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -439,18 +446,17 @@ github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99 github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.9.1 h1:DpTpJqzZ3NvX9zqjhIuI1oVzYZMvboZe+3LoeEIJjHM= -github.com/googleapis/gax-go/v2 v2.9.1/go.mod h1:4FG3gMrVZlyMp5itSYKMU9z/lBE7+SbnUOvzH2HqbEY= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0= -github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I= +github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= +github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -473,27 +479,25 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= -github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/lib/pq v1.4.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= -github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libdns/alidns v1.0.3-0.20220501125541-4a895238a95d h1:UiGXId+q/C65kEY3MJhdmK3d4QiS4yrWljeDjc8tZ0E= -github.com/libdns/alidns v1.0.3-0.20220501125541-4a895238a95d/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ= +github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE= github.com/libdns/cloudflare v0.1.1-0.20221006221909-9d3ab3c3cddd h1:c5hc0b5/pFqFeyQaOTVmYJbyr+QwZZFcMnjgtZGIk6k= github.com/libdns/cloudflare v0.1.1-0.20221006221909-9d3ab3c3cddd/go.mod h1:ob9J/elFVmPWKNHOMynwtH0h+T3pBrEL18amCSliwAQ= -github.com/libdns/digitalocean v0.0.0-20220518195853-a541bc8aa80f h1:Y0JkwI0Uip+Zrh71aHLmNz150cKnWuC+535v/zLS8zo= -github.com/libdns/digitalocean v0.0.0-20220518195853-a541bc8aa80f/go.mod h1:B2TChhOTxvBflpRTHlguXWtwa1Ha5WI6JkB6aCViM+0= +github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea h1:IGlMNZCUp8Ho7NYYorpP5ZJgg2mFXARs6eHs/pSqFkA= +github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea/go.mod h1:B2TChhOTxvBflpRTHlguXWtwa1Ha5WI6JkB6aCViM+0= github.com/libdns/gandi v1.0.3-0.20220921161957-dcd0274d2c79 h1:s5zuoIehXkSKg6Yfd5Oh1jEfvWXSn+eAttVHufSzDPE= github.com/libdns/gandi v1.0.3-0.20220921161957-dcd0274d2c79/go.mod h1:VN+Lh8Teq6nYszNsPSLKdIv24hOCcQu0rJWHQa2jPZc= github.com/libdns/googleclouddns v1.1.0 h1:murPR1LfTZZObLV2OLxUVmymWH25glkMFKpDjkk2m0E= @@ -519,7 +523,6 @@ github.com/libdns/vultr v1.0.0 h1:W8B4+k2bm9ro3bZLSZV9hMOQI+uO6Svu+GmD+Olz7ZI= github.com/libdns/vultr v1.0.0/go.mod h1:8K1HJExcbeHS4YPkFHRZpqpXZzZ+DZAA0m0VikJgEqk= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8= @@ -530,22 +533,20 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= -github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mholt/acmez v1.1.1 h1:sYeeYd/EHVm9cSmLdWey5oW/fXFVAq5pNLjSczN2ZUg= -github.com/mholt/acmez v1.1.1/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= +github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= +github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI= -github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.55 h1:ZXqUO/8cgfHzI+08h/zGuTTFpISSA32BZmBE3FCLJas= -github.com/minio/minio-go/v7 v7.0.55/go.mod h1:NUDy4A4oXPq1l2yK6LTSvCEzAMeIcoz9lcj5dbzSrRE= +github.com/minio/minio-go/v7 v7.0.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0Dzw= +github.com/minio/minio-go/v7 v7.0.66/go.mod h1:DHAgmyQEGdW3Cif0UooKOyrT3Vxs82zNdV6tkKhRtbs= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -559,26 +560,24 @@ github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6 h1:TsF5Cl0Mj5JMv github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6/go.mod h1:4PEbISVqRCQaXaDAt289w3nK9UhoF8/ZOLy31Hbv7ds= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd h1:4yVpQ/+li28lQ/daYCWeDB08obRmjaoAw2qfFFaCQ40= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd/go.mod h1:wpK5wqysOJU1w2OxgG65du8M7UqBkxzsNaJdjwiRqAs= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= -github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= +github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= -github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= -github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y= +github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= @@ -590,22 +589,26 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 h1:J6qvD6rbmOil46orKqJaRPG+zTpoGlBTUdyv8ki63L0= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM= -github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= -github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= -github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= +github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= +github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -619,23 +622,29 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli/v2 v2.25.5 h1:d0NIAyhh5shGscroL7ek/Ya9QYQE0KNabJgiUinIQkc= -github.com/urfave/cli/v2 v2.25.5/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc= -github.com/vultr/govultr/v3 v3.0.2 h1:rrYiuF9adB3rjnhp0ev+mkJXKEzuYa/AGfezYPr3EMs= -github.com/vultr/govultr/v3 v3.0.2/go.mod h1:Pd3D6VKmQKyKWsdV1xLx4VKclEV23adMs3YoI7rh7gA= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= +github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= +github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/vultr/govultr/v3 v3.6.1 h1:l1hAXGtqWVnobBpLRzW/BxoocYFI7SSBwQHw65ntLk4= +github.com/vultr/govultr/v3 v3.6.1/go.mod h1:rt9v2x114jZmmLAE/h5N5jnxTmsK9ewwS2oQZ0UBQzM= +github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI= +github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 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= +github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= +github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= +github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= +github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= +github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -646,14 +655,20 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= +go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= +go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= +go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= +go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= +go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= +go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= -go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -661,14 +676,10 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220518034528-6f7dac969898/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -679,6 +690,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -705,8 +718,9 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/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.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -740,13 +754,11 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -759,8 +771,10 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -786,8 +800,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -802,8 +816,9 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/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.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -839,13 +854,11 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -870,19 +883,23 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -894,13 +911,16 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -948,7 +968,6 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -956,8 +975,9 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= 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= @@ -1014,16 +1034,17 @@ google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.124.0 h1:dP6Ef1VgOGqQ8eiv4GiY8RhmeyqzovcXBYPDUYG8Syo= -google.golang.org/api v0.124.0/go.mod h1:xu2HQurE5gi/3t1aFCvhPD781p0a3p11sdunTJ2BlP4= +google.golang.org/api v0.157.0 h1:ORAeqmbrrozeyw5NjnMxh7peHO0UzV4wWYSwZeCUb20= +google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1059,9 +1080,7 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -1127,10 +1146,10 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55/go.mod h1:45EK0dUbEZ2NHjCeAd2LXmyjAgGUGrpGROgjhC3ADck= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a h1:HiYVD+FGJkTo+9zj1gqz0anapsa1JxjiSrN+BJKyUmE= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e h1:NumxXLPfHSndr3wBBdeKiVHjGVFzi9RX2HwwQke94iY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg= +google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1166,8 +1185,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= -google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= +google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= +google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -1184,8 +1203,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= @@ -1197,6 +1216,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -1209,16 +1229,16 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019年2月3日/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020年1月3日/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020年1月4日/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= -modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= -modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= -modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= +lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= +lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= +modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= +modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0= +modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI= modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= -modernc.org/libc v1.29.0 h1:tTFRFq69YKCF2QyGNuRUQxKBm1uZZLubf6Cjh/pVHXs= -modernc.org/libc v1.29.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ= +modernc.org/libc v1.40.6 h1:141JHq3SjhOOCjECBgD4K8VgTFOy19CnHwroC08DAig= +modernc.org/libc v1.40.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= @@ -1227,11 +1247,11 @@ modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= -modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= -modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= -modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= From ab94e0bb95428775f92bd0f25815595d1744f9c1 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月21日 20:01:00 +0300 Subject: [PATCH 038/171] check/spf: Handle empty MAIL FROM in accordance with RFC 7208 See #652. See #603. --- internal/check/spf/spf.go | 12 +++++++++++- tests/smtp_test.go | 13 +++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/internal/check/spf/spf.go b/internal/check/spf/spf.go index 783ef207..c94dd915 100644 --- a/internal/check/spf/spf.go +++ b/internal/check/spf/spf.go @@ -314,7 +314,17 @@ func (s *state) CheckConnection(ctx context.Context) module.CheckResult { return module.CheckResult{} } - mailFrom, err := prepareMailFrom(s.msgMeta.OriginalFrom) + mailFromOriginal := s.msgMeta.OriginalFrom + if mailFromOriginal == "" { + // RFC 7208 Section 2.4. + //>When the reverse-path is null, this document + //>defines the "MAIL FROM" identity to be the mailbox composed of the + //>local-part "postmaster" and the "HELO" identity (which might or might + //>not have been checked separately before). + mailFromOriginal = "postmaster@" + s.msgMeta.Conn.Hostname + } + + mailFrom, err := prepareMailFrom(mailFromOriginal) if err != nil { s.skip = true return module.CheckResult{ diff --git a/tests/smtp_test.go b/tests/smtp_test.go index 85a5173a..cd9249f1 100644 --- a/tests/smtp_test.go +++ b/tests/smtp_test.go @@ -122,13 +122,22 @@ func TestCheckSPF(tt *testing.T) { conn := t.Conn("smtp") defer conn.Close() - conn.SMTPNegotation("localhost", nil, nil) + conn.SMTPNegotation("fail.maddy.test", nil, nil) conn.Writeln("MAIL FROM:") conn.ExpectPattern("250 *") conn.Writeln("RSET") conn.ExpectPattern("250 *") + // Actually checks fail.maddy.test. + conn.Writeln("MAIL FROM:") + conn.ExpectPattern("552 5.7.0 *") + + conn.SMTPNegotation("pass.maddy.test", nil, nil) + + conn.Writeln("MAIL FROM:") + conn.ExpectPattern("250 *") + conn.Writeln("MAIL FROM:") conn.ExpectPattern("551 5.7.0 *") @@ -364,7 +373,7 @@ func TestCheckAuthorizeSender(tt *testing.T) { auth_normalize precis_casefold user_to_email static { entry "test-user1" "test@example1.org" - entry "test-user2" "é@example1.org" + entry "test-user2" "é@example1.org" } } } From a8e8c4a085a406e05007ac2e64ba9da9d5da716d Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月21日 20:57:49 +0300 Subject: [PATCH 039/171] Pin libdns/alidns by commit hash again Like 44dc130e437abc8ee55117ed0dfb1e0a2f858ef3. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 40681b53..fb7c1e85 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/hashicorp/go-hclog v1.6.2 github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c github.com/lib/pq v1.10.9 - github.com/libdns/alidns v1.0.3 + github.com/libdns/alidns v1.0.3-0.20230628155627-8d5d630d5516 github.com/libdns/cloudflare v0.1.1-0.20221006221909-9d3ab3c3cddd github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea github.com/libdns/gandi v1.0.3-0.20220921161957-dcd0274d2c79 diff --git a/go.sum b/go.sum index 5db98a55..3d025b0c 100644 --- a/go.sum +++ b/go.sum @@ -492,8 +492,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ= -github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE= +github.com/libdns/alidns v1.0.3-0.20230628155627-8d5d630d5516 h1:tPVSANkA4lo+K65YjsQcaQ1uh6sb0zRBQDz78l1Fo4Y= +github.com/libdns/alidns v1.0.3-0.20230628155627-8d5d630d5516/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE= github.com/libdns/cloudflare v0.1.1-0.20221006221909-9d3ab3c3cddd h1:c5hc0b5/pFqFeyQaOTVmYJbyr+QwZZFcMnjgtZGIk6k= github.com/libdns/cloudflare v0.1.1-0.20221006221909-9d3ab3c3cddd/go.mod h1:ob9J/elFVmPWKNHOMynwtH0h+T3pBrEL18amCSliwAQ= github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea h1:IGlMNZCUp8Ho7NYYorpP5ZJgg2mFXARs6eHs/pSqFkA= From a2f8916183028c96909c452068a2d58ad4aa3a0d Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月21日 20:58:05 +0300 Subject: [PATCH 040/171] docs: Fix README CI badge Finally --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d5a79b6f..312fb848 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ daemon with uniform configuration and minimal maintenance cost. feature-packed implementation you may want to use Dovecot instead. maddy still can handle message delivery business. -[![CI status](https://img.shields.io/github/workflow/status/foxcpp/maddy/Testing%20and%20release%20preparation?style=flat-square)](https://github.com/foxcpp/maddy/actions/workflows/cicd.yml) -[![Issues tracker](https://img.shields.io/github/issues/foxcpp/maddy)](https://github.com/foxcpp/maddy) +[![CI status](https://img.shields.io/github/actions/workflow/status/foxcpp/maddy/cicd.yml?style=flat-square)](https://github.com/foxcpp/maddy/actions/workflows/cicd.yml) +[![Issues tracker](https://img.shields.io/github/issues/foxcpp/maddy?style=flat-square)](https://github.com/foxcpp/maddy) * [Setup tutorial](https://maddy.email/tutorials/setting-up/) * [Documentation](https://maddy.email/) From 28bdf6d33f1035fe41b4acdb77844c0c3a2fd9be Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月21日 21:57:00 +0300 Subject: [PATCH 041/171] Make it easier to avoid permission issues when setting up maddy 1. Clarify that you need to manually create the user and group when building from source. ./build.sh does not do that since it is a packaging tool, not system configuration one. 2. Do not require "go" command to be present when running ./build.sh install. go installation may be user-specific and unavailable when running with sudo. 3. Ease UMask restrictions. Allow group access. This allows CLI commands to be run by any user in maddy group. See #569. --- build.sh | 21 +++++++++++++++++---- dist/systemd/maddy.service | 5 +++-- dist/systemd/maddy@.service | 5 +++-- docs/tutorials/building-from-source.md | 10 ++++++---- docs/tutorials/setting-up.md | 3 +++ 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/build.sh b/build.sh index f612d647..68e71dbd 100755 --- a/build.sh +++ b/build.sh @@ -146,10 +146,23 @@ install() { # Attempt to install systemd units only for Linux. # Check is done using GOOS instead of uname -s to account for possible # package cross-compilation. - if [ "$(go env GOOS)" = "linux" ]; then - command install -m 0755 -d "${destdir}/${prefix}/lib/systemd/system/" - command install -m 0644 "${builddir}"/systemd/*.service "${destdir}/${prefix}/lib/systemd/system/" - fi + # Though go command might be unavailable if build.sh is run + # with sudo and go installation is user-specific, so fallback + # to using uname -s in the end. + set +e + if command -v go>/dev/null 2>/dev/null; then + set -e + if [ "$(go env GOOS)" = "linux" ]; then + command install -m 0755 -d "${destdir}/${prefix}/lib/systemd/system/" + command install -m 0644 "${builddir}"/systemd/*.service "${destdir}/${prefix}/lib/systemd/system/" + fi + else + set -e + if [ "$(uname -s)" = "Linux" ]; then + command install -m 0755 -d "${destdir}/${prefix}/lib/systemd/system/" + command install -m 0644 "${builddir}"/systemd/*.service "${destdir}/${prefix}/lib/systemd/system/" + fi + fi if [ -e "${builddir}"/man ]; then command install -m 0755 -d "${destdir}/${prefix}/share/man/man1/" diff --git a/dist/systemd/maddy.service b/dist/systemd/maddy.service index b1598502..ec1ac29c 100644 --- a/dist/systemd/maddy.service +++ b/dist/systemd/maddy.service @@ -54,8 +54,9 @@ KillSignal=SIGTERM AmbientCapabilities=CAP_NET_BIND_SERVICE CapabilityBoundingSet=CAP_NET_BIND_SERVICE -# Force all files created by maddy to be only readable by it. -UMask=0027 +# Force all files created by maddy to be only readable by it +# and maddy group. +UMask=0007 # Bump FD limitations. Even idle mail server can have a lot of FDs open (think # of idle IMAP connections, especially ones abandoned on the other end and diff --git a/dist/systemd/maddy@.service b/dist/systemd/maddy@.service index 015dcd60..ea60ff84 100644 --- a/dist/systemd/maddy@.service +++ b/dist/systemd/maddy@.service @@ -50,8 +50,9 @@ KillSignal=SIGTERM AmbientCapabilities=CAP_NET_BIND_SERVICE CapabilityBoundingSet=CAP_NET_BIND_SERVICE -# Force all files created by maddy to be only readable by it. -UMask=0027 +# Force all files created by maddy to be only readable by it and +# maddy group. +UMask=0007 # Bump FD limitations. Even idle mail server can have a lot of FDs open (think # of idle IMAP connections, especially ones abandoned on the other end and diff --git a/docs/tutorials/building-from-source.md b/docs/tutorials/building-from-source.md index 3b3d9291..a2e37971 100644 --- a/docs/tutorials/building-from-source.md +++ b/docs/tutorials/building-from-source.md @@ -34,17 +34,19 @@ $ git clone https://github.com/foxcpp/maddy.git $ cd maddy ``` -3. Select the appropriate version to build: +2. Select the appropriate version to build: ``` $ git checkout v0.7.0 # a specific release $ git checkout master # next bugfix release $ git checkout dev # next feature release ``` -2. Build & install it +3. Build & install it ``` $ ./build.sh -# ./build.sh install +$ sudo ./build.sh install ``` -3. Have fun! +4. Finish setup as described in [Setting up](../setting-up) (starting from System configuration). + + diff --git a/docs/tutorials/setting-up.md b/docs/tutorials/setting-up.md index fd4e4aec..2ec83519 100644 --- a/docs/tutorials/setting-up.md +++ b/docs/tutorials/setting-up.md @@ -246,6 +246,9 @@ storage account: $ maddy imap-acct create postmaster@example.org ``` +Note: to run `maddy` CLI commands, your user should be in the `maddy` +group. Alternatively, just use `sudo -u maddy`. + That is it. Now you have your first e-mail address. when authenticating using your e-mail client, do not forget the username is "postmaster@example.org", not just "postmaster". From ede85b4cd121666e7da4a54455d85e399a83085c Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月22日 00:29:16 +0300 Subject: [PATCH 042/171] docs: Explicitly mention that referencing config block from global directive won't work See #577. --- docs/reference/tls-acme.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/reference/tls-acme.md b/docs/reference/tls-acme.md index 930e390c..9baccb5d 100644 --- a/docs/reference/tls-acme.md +++ b/docs/reference/tls-acme.md @@ -32,6 +32,9 @@ tls { } ``` +Note: `tls &local_tls` as a global directive won't work because +global directives are initialized before other configuration blocks. + Currently the only supported challenge is `dns-01` one therefore you also need to configure the DNS provider: @@ -88,7 +91,7 @@ Currently only filesystem-based store is supported. --- -### ca _url_ +### ca _url_ Default: Let's Encrypt production CA URL of ACME directory to use. From c67955ef0d5615d493f69bba3fd23f46d27a8d0a Mon Sep 17 00:00:00 2001 From: Martin Matous Date: 2023年8月24日 18:17:08 +0200 Subject: [PATCH 043/171] fix(session): detect canceled lookup correctly cancelation is not DNSError, so UnwrapDNSErr() returns "" as reason Signed-off-by: Martin Matous --- internal/endpoint/smtp/session.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/endpoint/smtp/session.go b/internal/endpoint/smtp/session.go index dfa19ea0..6f2c68e5 100644 --- a/internal/endpoint/smtp/session.go +++ b/internal/endpoint/smtp/session.go @@ -319,13 +319,13 @@ func (s *Session) fetchRDNSName(ctx context.Context) { return } - reason, misc := exterrors.UnwrapDNSErr(err) - misc["reason"] = reason - if !strings.HasSuffix(reason, "canceled") { + if !errors.Is(err, context.Canceled) { // Often occurs when transaction completes before rDNS lookup and // rDNS name was not actually needed. So do not log cancelation // error if that's the case. + reason, misc := exterrors.UnwrapDNSErr(err) + misc["reason"] = reason s.log.Error("rDNS error", exterrors.WithFields(err, misc), "src_ip", s.connState.RemoteAddr) } s.connState.RDNSName.Set(nil, err) From 59d9435afa92ddeeb2091b3afcf24722a1e6e43e Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月24日 00:14:41 +0300 Subject: [PATCH 044/171] maddy 0.7.1 --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index faef31a4..39e898a4 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.7.0 +0.7.1 From 7ce4e6a759955ca491581b1153cf4ef5eb1d570e Mon Sep 17 00:00:00 2001 From: Adrien Mulattieri Date: 2024年1月25日 16:34:58 +0100 Subject: [PATCH 045/171] Remove duplicated test case --- internal/dmarc/evaluate_test.go | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/internal/dmarc/evaluate_test.go b/internal/dmarc/evaluate_test.go index a40f1f6a..c44bbbed 100644 --- a/internal/dmarc/evaluate_test.go +++ b/internal/dmarc/evaluate_test.go @@ -315,24 +315,6 @@ func TestEvaluateAlignment(t *testing.T) { output: authres.ResultFail, }, { // 16 - fromDomain: "example.com", - record: &Record{ - SPFAlignment: dmarc.AlignmentStrict, - }, - results: []authres.Result{ - &authres.SPFResult{ - Value: authres.ResultPass, - From: "", - Helo: "mx.example.com", - }, - &authres.DKIMResult{ - Value: authres.ResultNone, - Domain: "example.org", - }, - }, - output: authres.ResultFail, - }, - { // 17 fromDomain: "example.com", record: &Record{}, results: []authres.Result{ @@ -348,7 +330,7 @@ func TestEvaluateAlignment(t *testing.T) { }, output: authres.ResultTempError, }, - { // 18 + { // 17 fromDomain: "example.com", record: &Record{}, results: []authres.Result{ @@ -364,7 +346,7 @@ func TestEvaluateAlignment(t *testing.T) { }, output: authres.ResultTempError, }, - { // 19 + { // 18 fromDomain: "example.com", record: &Record{}, results: []authres.Result{ @@ -380,7 +362,7 @@ func TestEvaluateAlignment(t *testing.T) { }, output: authres.ResultPass, }, - { // 20 + { // 19 fromDomain: "example.com", record: &Record{}, results: []authres.Result{ @@ -396,7 +378,7 @@ func TestEvaluateAlignment(t *testing.T) { }, output: authres.ResultPass, }, - { // 21 + { // 20 fromDomain: "example.org", record: &Record{}, results: []authres.Result{ @@ -416,7 +398,7 @@ func TestEvaluateAlignment(t *testing.T) { }, output: authres.ResultPass, }, - { // 22 + { // 21 fromDomain: "example.org", record: &Record{}, results: []authres.Result{ @@ -436,7 +418,7 @@ func TestEvaluateAlignment(t *testing.T) { }, output: authres.ResultTempError, }, - { // 23 + { // 22 fromDomain: "example.org", record: &Record{}, results: []authres.Result{ @@ -452,7 +434,7 @@ func TestEvaluateAlignment(t *testing.T) { }, output: authres.ResultFail, }, - { // 21 + { // 23 fromDomain: "sub.example.org", record: &Record{}, results: []authres.Result{ From 07b40e24432f9d8a7eb858dc9ac3c796c0e68687 Mon Sep 17 00:00:00 2001 From: xiliuya Date: 2024年1月26日 09:42:08 +0800 Subject: [PATCH 046/171] docs: fix docker command not open 465 --- docs/docker.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docker.md b/docs/docker.md index 2898a07b..55539852 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -65,6 +65,7 @@ docker run \ -v maddydata:/data \ -p 25:25 \ -p 143:143 \ + -p 465:465 \ -p 587:587 \ -p 993:993 \ foxcpp/maddy:0.6 From 31537181ae97332d2cdfcbbd0a0c69d6bb5a9710 Mon Sep 17 00:00:00 2001 From: xiliuya Date: 2024年1月26日 09:47:34 +0800 Subject: [PATCH 047/171] docs: change docker latest version --- docs/docker.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docker.md b/docs/docker.md index 55539852..561f0541 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -40,8 +40,8 @@ To run management commands, create a temporary container with the same /data directory and put the command after the image name, like this: ``` -docker run --rm -it -v maddydata:/data foxcpp/maddy:0.6.0 creds create foxcpp@maddy.test -docker run --rm -it -v maddydata:/data foxcpp/maddy:0.6.0 imap-acct create foxcpp@maddy.test +docker run --rm -it -v maddydata:/data foxcpp/maddy:latest creds create foxcpp@maddy.test +docker run --rm -it -v maddydata:/data foxcpp/maddy:latest imap-acct create foxcpp@maddy.test ``` Use the same image version as the running server. Things may break badly @@ -68,7 +68,7 @@ docker run \ -p 465:465 \ -p 587:587 \ -p 993:993 \ - foxcpp/maddy:0.6 + foxcpp/maddy:latest ``` It will fail on first startup. Copy TLS certificate to /data/tls/fullchain.pem From 3a09117e9b048359a6843d13f40986406f814815 Mon Sep 17 00:00:00 2001 From: Alex Giurgiu Date: 2024年1月26日 15:23:01 +0200 Subject: [PATCH 048/171] upgraded to the latest version of gandi libdns, which fixes an issue where new records could not be created --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index fb7c1e85..69bf2827 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/libdns/alidns v1.0.3-0.20230628155627-8d5d630d5516 github.com/libdns/cloudflare v0.1.1-0.20221006221909-9d3ab3c3cddd github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea - github.com/libdns/gandi v1.0.3-0.20220921161957-dcd0274d2c79 + github.com/libdns/gandi v1.0.3 github.com/libdns/googleclouddns v1.1.0 github.com/libdns/hetzner v0.0.1 github.com/libdns/leaseweb v0.3.1 diff --git a/go.sum b/go.sum index 3d025b0c..089dd1b3 100644 --- a/go.sum +++ b/go.sum @@ -500,6 +500,8 @@ github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea h1:IGlMNZCUp8H github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea/go.mod h1:B2TChhOTxvBflpRTHlguXWtwa1Ha5WI6JkB6aCViM+0= github.com/libdns/gandi v1.0.3-0.20220921161957-dcd0274d2c79 h1:s5zuoIehXkSKg6Yfd5Oh1jEfvWXSn+eAttVHufSzDPE= github.com/libdns/gandi v1.0.3-0.20220921161957-dcd0274d2c79/go.mod h1:VN+Lh8Teq6nYszNsPSLKdIv24hOCcQu0rJWHQa2jPZc= +github.com/libdns/gandi v1.0.3 h1:FIvipWOg/O4zi75fPRmtcolRKqI6MgrbpFy2p5KYdUk= +github.com/libdns/gandi v1.0.3/go.mod h1:G6dw58Xnji2xX+lb+uZxGbtmfxKllm1CGHE2bOPG3WA= github.com/libdns/googleclouddns v1.1.0 h1:murPR1LfTZZObLV2OLxUVmymWH25glkMFKpDjkk2m0E= github.com/libdns/googleclouddns v1.1.0/go.mod h1:3tzd056dfqKlf71V8Oy19En4WjJ3ybyuWx6P9bQSCIw= github.com/libdns/hetzner v0.0.1 h1:WsmcsOKnfpKmzwhfyqhGQEIlEeEaEUvb7ezoJgBKaqU= From 08671bc0e89e07d54736c1b540328e2684efb9c4 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月27日 16:30:38 +0300 Subject: [PATCH 049/171] libdns/gandi: Re-add API keys support with deprecation warning --- go.mod | 2 ++ go.sum | 6 ++---- internal/libdns/gandi.go | 15 ++++++++++++++- internal/libdns/provider_module.go | 8 +++++++- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 69bf2827..663eb055 100644 --- a/go.mod +++ b/go.mod @@ -168,3 +168,5 @@ require ( ) replace github.com/emersion/go-imap => github.com/foxcpp/go-imap v1.0.0-beta.1.0.20220623182312-df940c324887 + +replace github.com/libdns/gandi => github.com/foxcpp/libdns-gandi v1.0.4-0.20240127130558-4782f9d5ce3e // v1.0.3+maddy.1 diff --git a/go.sum b/go.sum index 089dd1b3..4455bdc6 100644 --- a/go.sum +++ b/go.sum @@ -325,6 +325,8 @@ github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6 github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8 h1:k8w0iy6GP9oeSZWUH3p2DqZHaXDKZGNs3NZGZMGfQHc= github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8/go.mod h1:HO1YOCbBM8KjpgThMMFejHx6K/UsnEv2Oh9YGtBIlOU= +github.com/foxcpp/libdns-gandi v1.0.4-0.20240127130558-4782f9d5ce3e h1:hKk+CGUtwnKDGKINPEojeo91kx0tnV6V4tlzHehJPfg= +github.com/foxcpp/libdns-gandi v1.0.4-0.20240127130558-4782f9d5ce3e/go.mod h1:G6dw58Xnji2xX+lb+uZxGbtmfxKllm1CGHE2bOPG3WA= github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= @@ -498,10 +500,6 @@ github.com/libdns/cloudflare v0.1.1-0.20221006221909-9d3ab3c3cddd h1:c5hc0b5/pFq github.com/libdns/cloudflare v0.1.1-0.20221006221909-9d3ab3c3cddd/go.mod h1:ob9J/elFVmPWKNHOMynwtH0h+T3pBrEL18amCSliwAQ= github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea h1:IGlMNZCUp8Ho7NYYorpP5ZJgg2mFXARs6eHs/pSqFkA= github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea/go.mod h1:B2TChhOTxvBflpRTHlguXWtwa1Ha5WI6JkB6aCViM+0= -github.com/libdns/gandi v1.0.3-0.20220921161957-dcd0274d2c79 h1:s5zuoIehXkSKg6Yfd5Oh1jEfvWXSn+eAttVHufSzDPE= -github.com/libdns/gandi v1.0.3-0.20220921161957-dcd0274d2c79/go.mod h1:VN+Lh8Teq6nYszNsPSLKdIv24hOCcQu0rJWHQa2jPZc= -github.com/libdns/gandi v1.0.3 h1:FIvipWOg/O4zi75fPRmtcolRKqI6MgrbpFy2p5KYdUk= -github.com/libdns/gandi v1.0.3/go.mod h1:G6dw58Xnji2xX+lb+uZxGbtmfxKllm1CGHE2bOPG3WA= github.com/libdns/googleclouddns v1.1.0 h1:murPR1LfTZZObLV2OLxUVmymWH25glkMFKpDjkk2m0E= github.com/libdns/googleclouddns v1.1.0/go.mod h1:3tzd056dfqKlf71V8Oy19En4WjJ3ybyuWx6P9bQSCIw= github.com/libdns/hetzner v0.0.1 h1:WsmcsOKnfpKmzwhfyqhGQEIlEeEaEUvb7ezoJgBKaqU= diff --git a/internal/libdns/gandi.go b/internal/libdns/gandi.go index 91e8d87e..62c7c2d0 100644 --- a/internal/libdns/gandi.go +++ b/internal/libdns/gandi.go @@ -4,7 +4,10 @@ package libdns import ( + "fmt" + "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" "github.com/libdns/gandi" ) @@ -16,7 +19,17 @@ func init() { RecordDeleter: &p, RecordAppender: &p, setConfig: func(c *config.Map) { - c.String("api_token", false, true, "", &p.APIToken) + c.String("api_token", false, false, "", &p.APIToken) + c.String("personal_token", false, false, "", &p.BearerToken) + }, + afterConfig: func() error { + if p.APIToken != "" { + log.Println("libdns.gandi: api_token is deprecated, use personal_token instead (https://api.gandi.net/docs/authentication/)") + } + if p.APIToken == "" && p.BearerToken == "" { + return fmt.Errorf("libdns.gandi: either api_token or personal_token should be specified") + } + return nil }, instName: instName, modName: modName, diff --git a/internal/libdns/provider_module.go b/internal/libdns/provider_module.go index 74d201ef..75561501 100644 --- a/internal/libdns/provider_module.go +++ b/internal/libdns/provider_module.go @@ -8,7 +8,8 @@ import ( type ProviderModule struct { libdns.RecordDeleter libdns.RecordAppender - setConfig func(c *config.Map) + setConfig func(c *config.Map) + afterConfig func() error instName string modName string @@ -17,6 +18,11 @@ type ProviderModule struct { func (p *ProviderModule) Init(cfg *config.Map) error { p.setConfig(cfg) _, err := cfg.Process() + if p.afterConfig != nil { + if err := p.afterConfig(); err != nil { + return err + } + } return err } From dd06ffe435ef0811a8715c9de8b8663f32e3e135 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月27日 16:32:15 +0300 Subject: [PATCH 050/171] Add missing tls_client directive to ReadGlobals See #674. --- maddy.go | 1 + 1 file changed, 1 insertion(+) diff --git a/maddy.go b/maddy.go index 9b765803..16aa0eb0 100644 --- a/maddy.go +++ b/maddy.go @@ -294,6 +294,7 @@ func ReadGlobals(cfg []config.Node) (map[string]interface{}, []config.Node, erro globals.String("hostname", false, false, "", nil) globals.String("autogenerated_msg_domain", false, false, "", nil) globals.Custom("tls", false, false, nil, tls.TLSDirective, nil) + globals.Custom("tls_client", false, false, nil, tls.TLSClientBlock, nil) globals.Bool("storage_perdomain", false, false, nil) globals.Bool("auth_perdomain", false, false, nil) globals.StringList("auth_domains", false, false, nil, nil) From 65bfb1b0daaf13470420922c5d186ce5ca8bf156 Mon Sep 17 00:00:00 2001 From: xiliuya Date: 2024年1月29日 13:30:16 +0800 Subject: [PATCH 051/171] docs: change docker 0.7 version --- docs/docker.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docker.md b/docs/docker.md index 561f0541..e3895899 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -40,8 +40,8 @@ To run management commands, create a temporary container with the same /data directory and put the command after the image name, like this: ``` -docker run --rm -it -v maddydata:/data foxcpp/maddy:latest creds create foxcpp@maddy.test -docker run --rm -it -v maddydata:/data foxcpp/maddy:latest imap-acct create foxcpp@maddy.test +docker run --rm -it -v maddydata:/data foxcpp/maddy:0.7 creds create foxcpp@maddy.test +docker run --rm -it -v maddydata:/data foxcpp/maddy:0.7 imap-acct create foxcpp@maddy.test ``` Use the same image version as the running server. Things may break badly @@ -68,7 +68,7 @@ docker run \ -p 465:465 \ -p 587:587 \ -p 993:993 \ - foxcpp/maddy:latest + foxcpp/maddy:0.7 ``` It will fail on first startup. Copy TLS certificate to /data/tls/fullchain.pem From dd5f8a68da91e029450be83f37769f51f8701442 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月29日 23:32:45 +0300 Subject: [PATCH 052/171] tls/acme: Actually use test_ca --- internal/tls/acme/acme.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/tls/acme/acme.go b/internal/tls/acme/acme.go index 34683c03..39d6213d 100644 --- a/internal/tls/acme/acme.go +++ b/internal/tls/acme/acme.go @@ -95,6 +95,7 @@ func (l *Loader) Init(cfg *config.Map) error { issuer := certmagic.NewACMEIssuer(l.cfg, certmagic.ACMEIssuer{ Logger: cmLog, CA: caPath, + TestCA: testCAPath, Email: email, Agreed: agreed, }) From 7bdc981eac3ff1e83585184563949ac1171e643e Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月29日 23:45:25 +0300 Subject: [PATCH 053/171] target/remote: Improve handling of stale connections in pool 1. Apply conn_max_idle_time to each connection individually, not pool bucket. 2. Include local_addr in some log messages to help identify individual connections in the pool. 3. Run conn.Close outside of keysLock and asynchronously. Ensures slow server or dead connection won't cause pool operations to hang. 4. Set 5 second timeout for QUIT call in conn.Close. To detect dead connections faster, there is no reason for any server to take more than 5 seconds to respond to QUIT. See #675. --- internal/smtpconn/pool/pool.go | 27 +++++++++----- internal/smtpconn/smtpconn.go | 59 ++++++++++++++++++++++++------- internal/target/remote/connect.go | 22 ++++++++---- internal/target/remote/remote.go | 12 ++++--- 4 files changed, 88 insertions(+), 32 deletions(-) diff --git a/internal/smtpconn/pool/pool.go b/internal/smtpconn/pool/pool.go index 35ab27ba..4b700ee4 100644 --- a/internal/smtpconn/pool/pool.go +++ b/internal/smtpconn/pool/pool.go @@ -26,6 +26,7 @@ import ( type Conn interface { Usable() bool + LastUseAt() time.Time Close() error } @@ -95,33 +96,38 @@ func (p *P) CleanUp(ctx context.Context) { close(v.c) for conn := range v.c { - conn.Close() + go conn.Close() } delete(p.keys, k) } } func (p *P) Get(ctx context.Context, key string) (Conn, error) { - // TODO: See if it is possible to get rid of this lock. p.keysLock.Lock() - defer p.keysLock.Unlock() bucket, ok := p.keys[key] if !ok { + p.keysLock.Unlock() return p.cfg.New(ctx, key) } if time.Now().Unix()-bucket.lastUse> p.cfg.MaxConnLifetimeSec { // Drop bucket. + delete(p.keys, key) close(bucket.c) + + // Close might take some time, unlock early. + p.keysLock.Unlock() + for conn := range bucket.c { conn.Close() } - delete(p.keys, key) return p.cfg.New(ctx, key) } + p.keysLock.Unlock() + for { var conn Conn select { @@ -134,7 +140,12 @@ func (p *P) Get(ctx context.Context, key string) (Conn, error) { } if !conn.Usable() { - conn.Close() + // Close might take some time, run in parallel. + go conn.Close() + continue + } + if conn.LastUseAt().Add(time.Duration(p.cfg.MaxConnLifetimeSec) * time.Second).Before(time.Now()) { + go conn.Close() continue } @@ -158,12 +169,12 @@ func (p *P) Return(key string, c Conn) { if v.lastUse+p.cfg.StaleKeyLifetimeSec> time.Now().Unix() { continue } - + delete(p.keys, k) close(v.c) + for conn := range v.c { conn.Close() } - delete(p.keys, k) } } @@ -179,7 +190,7 @@ func (p *P) Return(key string, c Conn) { bucket.lastUse = time.Now().Unix() default: // Let it go, let it go... - c.Close() + go c.Close() } } diff --git a/internal/smtpconn/smtpconn.go b/internal/smtpconn/smtpconn.go index 451a8442..4dca9575 100644 --- a/internal/smtpconn/smtpconn.go +++ b/internal/smtpconn/smtpconn.go @@ -79,6 +79,7 @@ type C struct { // "ADDRESS said: ..." AddrInSMTPMsg bool + conn net.Conn serverName string cl *smtp.Client rcpts []string @@ -163,26 +164,28 @@ func (c *C) wrapClientErr(err error, serverName string) error { // Connect actually estabilishes the network connection with the remote host, // executes HELO/EHLO and optionally STARTTLS command. func (c *C) Connect(ctx context.Context, endp config.Endpoint, starttls bool, tlsConfig *tls.Config) (didTLS bool, err error) { - didTLS, cl, err := c.attemptConnect(ctx, false, endp, starttls, tlsConfig) + didTLS, cl, conn, err := c.attemptConnect(ctx, false, endp, starttls, tlsConfig) if err != nil { return false, c.wrapClientErr(err, endp.Host) } c.serverName = endp.Host c.cl = cl + c.conn = conn return didTLS, nil } // ConnectLMTP estabilishes the network connection with the remote host and // sends LHLO command, negotiating LMTP use. func (c *C) ConnectLMTP(ctx context.Context, endp config.Endpoint, starttls bool, tlsConfig *tls.Config) (didTLS bool, err error) { - didTLS, cl, err := c.attemptConnect(ctx, true, endp, starttls, tlsConfig) + didTLS, cl, conn, err := c.attemptConnect(ctx, true, endp, starttls, tlsConfig) if err != nil { return false, c.wrapClientErr(err, endp.Host) } c.serverName = endp.Host c.cl = cl + c.conn = conn return didTLS, nil } @@ -203,14 +206,27 @@ func (err TLSError) Unwrap() error { return err.Err } -func (c *C) attemptConnect(ctx context.Context, lmtp bool, endp config.Endpoint, starttls bool, tlsConfig *tls.Config) (didTLS bool, cl *smtp.Client, err error) { - var conn net.Conn +func (c *C) LocalAddr() net.Addr { + if c.conn == nil { + return nil + } + return c.conn.LocalAddr() +} + +func (c *C) RemoteAddr() net.Addr { + if c.conn == nil { + return nil + } + return c.conn.RemoteAddr() +} + +func (c *C) attemptConnect(ctx context.Context, lmtp bool, endp config.Endpoint, starttls bool, tlsConfig *tls.Config) (didTLS bool, cl *smtp.Client, conn net.Conn, err error) { dialCtx, cancel := context.WithTimeout(ctx, c.ConnectTimeout) conn, err = c.Dialer(dialCtx, endp.Network(), endp.Address()) cancel() if err != nil { - return false, nil, err + return false, nil, nil, err } if endp.IsTLS() { @@ -233,15 +249,15 @@ func (c *C) attemptConnect(ctx context.Context, lmtp bool, endp config.Endpoint, // i18n: hostname is already expected to be in A-labels form. if err := cl.Hello(c.Hostname); err != nil { cl.Close() - return false, nil, err + return false, nil, nil, err } if endp.IsTLS() || !starttls { - return endp.IsTLS(), cl, nil + return endp.IsTLS(), cl, nil, nil } if ok, _ := cl.Extension("STARTTLS"); !ok { - return false, cl, nil + return false, cl, nil, nil } cfg := tlsConfig.Clone() @@ -255,10 +271,10 @@ func (c *C) attemptConnect(ctx context.Context, lmtp bool, endp config.Endpoint, cl.Close() } - return false, nil, TLSError{err} + return false, nil, nil, TLSError{err} } - return true, cl, nil + return true, cl, conn, nil } // Mail sends the MAIL FROM command to the remote server. @@ -307,7 +323,8 @@ func (c *C) Mail(ctx context.Context, from string, opts smtp.MailOptions) error return c.wrapClientErr(err, c.serverName) } - c.Log.DebugMsg("connected", "remote_server", c.serverName) + c.Log.DebugMsg("connected", "remote_server", c.serverName, + "local_addr", c.LocalAddr(), "remote_addr", c.RemoteAddr()) return nil } @@ -482,11 +499,27 @@ func (c *C) Noop() error { return c.cl.Noop() } -// Close sends the QUIT command, if it fail - it directly closes the +// Close sends the QUIT command, if it fails - it directly closes the // connection. func (c *C) Close() error { + c.cl.CommandTimeout = 5 * time.Second + if err := c.cl.Quit(); err != nil { - c.Log.Error("QUIT error", c.wrapClientErr(err, c.serverName)) + var smtpErr *smtp.SMTPError + var netErr *net.OpError + if errors.As(err, &smtpErr) && smtpErr.Code == 421 { + // 421 "Service not available" is typically sent + // when idle timeout happens. + c.Log.DebugMsg("QUIT error", "reason", c.wrapClientErr(err, c.serverName)) + } else if errors.As(err, &netErr) && + (netErr.Timeout() || netErr.Err.Error() == "write: broken pipe" || netErr.Err.Error() == "read: connection reset") { + + // The case for silently closed connections. + c.Log.DebugMsg("QUIT error", "reason", c.wrapClientErr(err, c.serverName)) + } else { + c.Log.Error("QUIT error", c.wrapClientErr(err, c.serverName)) + } + return c.cl.Close() } diff --git a/internal/target/remote/connect.go b/internal/target/remote/connect.go index 0824d4aa..2290d137 100644 --- a/internal/target/remote/connect.go +++ b/internal/target/remote/connect.go @@ -26,6 +26,7 @@ import ( "net" "runtime/trace" "sort" + "time" "github.com/foxcpp/maddy/framework/config" "github.com/foxcpp/maddy/framework/dns" @@ -48,6 +49,7 @@ type mxConn struct { // Amount of times connection was used for an SMTP transaction. transactions int + lastUseAt time.Time // MX/TLS security level established for this connection. mxLevel module.MXLevel @@ -55,12 +57,16 @@ type mxConn struct { } func (c *mxConn) Usable() bool { - if c.C == nil || c.transactions> c.reuseLimit || c.C.Client() == nil { + if c.C == nil || c.transactions> c.reuseLimit || c.C.Client() == nil || c.errored { return false } return c.C.Client().Reset() == nil } +func (c *mxConn) LastUseAt() time.Time { + return c.lastUseAt +} + func (c *mxConn) Close() error { return c.C.Close() } @@ -196,9 +202,9 @@ func (rd *remoteDelivery) attemptMX(ctx context.Context, conn *mxConn, record *n return nil } -func (rd *remoteDelivery) connectionForDomain(ctx context.Context, domain string) (*smtpconn.C, error) { +func (rd *remoteDelivery) connectionForDomain(ctx context.Context, domain string) (*mxConn, error) { if c, ok := rd.connections[domain]; ok { - return c.C, nil + return c, nil } pooledConn, err := rd.rt.pool.Get(ctx, domain) @@ -212,7 +218,8 @@ func (rd *remoteDelivery) connectionForDomain(ctx context.Context, domain string // connection with weaker security. if pooledConn != nil && !rd.msgMeta.SMTPOpts.RequireTLS { conn = pooledConn.(*mxConn) - rd.Log.Msg("reusing cached connection", "domain", domain, "transactions_counter", conn.transactions) + rd.Log.Msg("reusing cached connection", "domain", domain, "transactions_counter", conn.transactions, + "local_addr", conn.LocalAddr(), "remote_addr", conn.RemoteAddr()) } else { rd.Log.DebugMsg("opening new connection", "domain", domain, "cache_ignored", pooledConn != nil) conn, err = rd.newConn(ctx, domain) @@ -249,6 +256,7 @@ func (rd *remoteDelivery) connectionForDomain(ctx context.Context, domain string region := trace.StartRegion(ctx, "remote/limits.TakeDest") if err := rd.rt.limits.TakeDest(ctx, domain); err != nil { region.End() + conn.Close() return nil, err } region.End() @@ -269,9 +277,10 @@ func (rd *remoteDelivery) connectionForDomain(ctx context.Context, domain string conn.Close() return nil, err } + conn.lastUseAt = time.Now() rd.connections[domain] = conn - return conn.C, nil + return conn, nil } func (rd *remoteDelivery) newConn(ctx context.Context, domain string) (*mxConn, error) { @@ -279,6 +288,7 @@ func (rd *remoteDelivery) newConn(ctx context.Context, domain string) (*mxConn, reuseLimit: rd.rt.connReuseLimit, C: smtpconn.New(), domain: domain, + lastUseAt: time.Now(), } conn.Dialer = rd.rt.dialer @@ -329,7 +339,7 @@ func (rd *remoteDelivery) newConn(ctx context.Context, domain string) (*mxConn, } region.End() - // Stil not connected? Bail out. + // Still not connected? Bail out. if conn.Client() == nil { return nil, &exterrors.SMTPError{ Code: exterrors.SMTPCode(lastErr, 451, 550), diff --git a/internal/target/remote/remote.go b/internal/target/remote/remote.go index 6fded8e8..3e661c4f 100644 --- a/internal/target/remote/remote.go +++ b/internal/target/remote/remote.go @@ -148,7 +148,7 @@ func (rt *Target) Init(cfg *config.Map) error { MaxConnLifetimeSec: 150, // 2.5 mins, half of recommended idle time from RFC 5321 StaleKeyLifetimeSec: 60 * 5, // should be bigger than MaxConnLifetimeSec } - cfg.Int("conn_max_idle_count", false, false, 10, &poolCfg.MaxConnsPerKey) + cfg.Int("conn_max_idle_count", false, false, 5, &poolCfg.MaxConnsPerKey) cfg.Int64("conn_max_idle_time", false, false, 150, &poolCfg.MaxConnLifetimeSec) if _, err := cfg.Process(); err != nil { @@ -315,6 +315,7 @@ func (rd *remoteDelivery) AddRcpt(ctx context.Context, to string, opts smtp.Rcpt if err := conn.Rcpt(ctx, to, opts); err != nil { return moduleError(err) } + conn.lastUseAt = time.Now() rd.recipients = append(rd.recipients, to) return nil @@ -425,6 +426,7 @@ func (rd *remoteDelivery) BodyNonAtomic(ctx context.Context, c module.StatusColl c.SetStatus(rcpt, err) } rd.connections[i].errored = err != nil + conn.lastUseAt = time.Now() }() } @@ -446,12 +448,12 @@ func (rd *remoteDelivery) Close() error { rd.rt.limits.ReleaseDest(conn.domain) conn.transactions++ - if conn.C == nil || conn.transactions> rd.rt.connReuseLimit || conn.C.Client() == nil || conn.errored { - rd.Log.Debugf("disconnected from %s (errored=%v,transactions=%v,disconnected before=%v)", - conn.ServerName(), conn.errored, conn.transactions, conn.C.Client() == nil) + if !conn.Usable() { + rd.Log.Debugf("disconnected %v from %s (errored=%v,transactions=%v,disconnected before=%v)", + conn.LocalAddr(), conn.ServerName(), conn.errored, conn.transactions, conn.C.Client() == nil) conn.Close() } else { - rd.Log.Debugf("returning connection for %s to pool", conn.ServerName()) + rd.Log.Debugf("returning connection %v for %s to pool", conn.LocalAddr(), conn.ServerName()) rd.rt.pool.Return(conn.domain, conn) } } From 4a69c9e9441014bd4631a5ba444b95db21d800d3 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年1月30日 01:24:44 +0300 Subject: [PATCH 054/171] Fix-up 7bdc981eac3ff1e83585184563949ac1171e643e --- internal/smtpconn/smtpconn.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/internal/smtpconn/smtpconn.go b/internal/smtpconn/smtpconn.go index 4dca9575..d7b42459 100644 --- a/internal/smtpconn/smtpconn.go +++ b/internal/smtpconn/smtpconn.go @@ -172,6 +172,10 @@ func (c *C) Connect(ctx context.Context, endp config.Endpoint, starttls bool, tl c.serverName = endp.Host c.cl = cl c.conn = conn + + c.Log.DebugMsg("connected", "remote_server", c.serverName, + "local_addr", c.LocalAddr(), "remote_addr", c.RemoteAddr()) + return didTLS, nil } @@ -186,6 +190,10 @@ func (c *C) ConnectLMTP(ctx context.Context, endp config.Endpoint, starttls bool c.serverName = endp.Host c.cl = cl c.conn = conn + + c.Log.DebugMsg("connected", "remote_server", c.serverName, + "local_addr", c.LocalAddr(), "remote_addr", c.RemoteAddr()) + return didTLS, nil } @@ -221,7 +229,6 @@ func (c *C) RemoteAddr() net.Addr { } func (c *C) attemptConnect(ctx context.Context, lmtp bool, endp config.Endpoint, starttls bool, tlsConfig *tls.Config) (didTLS bool, cl *smtp.Client, conn net.Conn, err error) { - dialCtx, cancel := context.WithTimeout(ctx, c.ConnectTimeout) conn, err = c.Dialer(dialCtx, endp.Network(), endp.Address()) cancel() @@ -253,11 +260,11 @@ func (c *C) attemptConnect(ctx context.Context, lmtp bool, endp config.Endpoint, } if endp.IsTLS() || !starttls { - return endp.IsTLS(), cl, nil, nil + return endp.IsTLS(), cl, conn, nil } if ok, _ := cl.Extension("STARTTLS"); !ok { - return false, cl, nil, nil + return false, cl, conn, nil } cfg := tlsConfig.Clone() @@ -323,8 +330,6 @@ func (c *C) Mail(ctx context.Context, from string, opts smtp.MailOptions) error return c.wrapClientErr(err, c.serverName) } - c.Log.DebugMsg("connected", "remote_server", c.serverName, - "local_addr", c.LocalAddr(), "remote_addr", c.RemoteAddr()) return nil } From cee577790b4c6c9533c46492282d520eef420f7e Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Thu, 8 Feb 2024 12:31:20 +0300 Subject: [PATCH 055/171] address: Special-case null return-path in normalization functions See #629. --- framework/address/norm.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/framework/address/norm.go b/framework/address/norm.go index 510fb63b..6998e662 100644 --- a/framework/address/norm.go +++ b/framework/address/norm.go @@ -36,6 +36,10 @@ import ( // // On error, case-folded addr is also returned. func ForLookup(addr string) (string, error) { + if addr == "" { // Null return-path case. + return "", nil + } + mbox, domain, err := Split(addr) if err != nil { return strings.ToLower(addr), err @@ -64,6 +68,10 @@ func ForLookup(addr string) (string, error) { // // Original value is also returned on the error. func CleanDomain(addr string) (string, error) { + if addr == "" { // Null return-path + return "", nil + } + mbox, domain, err := Split(addr) if err != nil { return addr, err From 2da4eceafb1c927cf6e96409be2370733fb2ce38 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年2月12日 22:17:50 +0300 Subject: [PATCH 056/171] target/queue: Use>= to check q.maxTries See #678. --- internal/target/queue/queue.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/target/queue/queue.go b/internal/target/queue/queue.go index 2c7a0394..525e40e0 100644 --- a/internal/target/queue/queue.go +++ b/internal/target/queue/queue.go @@ -402,7 +402,7 @@ func (q *Queue) tryDelivery(meta *QueueMetadata, header textproto.Header, body b meta.RcptErrs[rcpt] = toSMTPErr(rcptErr) temporary := exterrors.IsTemporaryOrUnspec(rcptErr) - if !temporary || meta.TriesCount[rcpt]+1 == q.maxTries { + if !temporary || meta.TriesCount[rcpt]+1>= q.maxTries { delete(meta.TriesCount, rcpt) dl.Msg("not delivered, permanent error", "rcpt", rcpt) failedRcpts = append(failedRcpts, rcpt) From 250ee6fd876298e7df24974e887dcf643df07557 Mon Sep 17 00:00:00 2001 From: reind33r Date: 2024年2月12日 23:25:25 +0100 Subject: [PATCH 057/171] libdns/rfc2136 added as a provider --- docs/reference/tls-acme.md | 11 +++++++++++ go.mod | 3 ++- go.sum | 30 +++++++++++++++--------------- internal/libdns/rfc2136.go | 28 ++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 16 deletions(-) create mode 100644 internal/libdns/rfc2136.go diff --git a/docs/reference/tls-acme.md b/docs/reference/tls-acme.md index 9baccb5d..1cc7423c 100644 --- a/docs/reference/tls-acme.md +++ b/docs/reference/tls-acme.md @@ -263,3 +263,14 @@ dns namedotcom { } ``` +- rfc2136 (non-default) + +``` +dns rfc2136 { + KeyName "..." + Key "..." + KeyAlg "..." + Server "..." +} +``` + diff --git a/go.mod b/go.mod index 68bf8a7a..7b2334a9 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.19 require ( blitiri.com.ar/go/spf v1.5.1 github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 + github.com/c0va23/go-proxyprotocol v0.9.1 github.com/caddyserver/certmagic v0.20.0 github.com/emersion/go-imap v1.2.2-0.20220928192137-6fac715be9cf github.com/emersion/go-imap-compress v0.0.0-20201103190257-14809af1d1b9 @@ -39,6 +40,7 @@ require ( github.com/libdns/metaname v0.3.0 github.com/libdns/namecheap v0.0.0-20211109042440-fc7440785c8e github.com/libdns/namedotcom v0.3.3 + github.com/libdns/rfc2136 v0.1.0 github.com/libdns/route53 v1.3.3 github.com/libdns/vultr v1.0.0 github.com/mattn/go-sqlite3 v1.14.19 @@ -75,7 +77,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect github.com/aws/smithy-go v1.19.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/c0va23/go-proxyprotocol v0.9.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/digitalocean/godo v1.108.0 // indirect diff --git a/go.sum b/go.sum index 005925b8..672f327f 100644 --- a/go.sum +++ b/go.sum @@ -237,6 +237,7 @@ github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/c0va23/go-proxyprotocol v0.9.1 h1:5BCkp0fDJOhzzH1lhjUgHhmZz9VvRMMif1U2D31hb34= github.com/c0va23/go-proxyprotocol v0.9.1/go.mod h1:TNjUV+llvk8TvWJxlPYAeAYZgSzT/iicNr3nWBWX320= github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE= @@ -261,6 +262,8 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -341,6 +344,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -348,6 +352,7 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -476,10 +481,12 @@ github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c h1:lx/uPI+m github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c/go.mod h1:LIAXxPvcUXwOcTIj9LSNSUpE9/eMHalTWxsP/kmWxQI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -489,6 +496,7 @@ github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -521,6 +529,8 @@ github.com/libdns/namecheap v0.0.0-20211109042440-fc7440785c8e h1:WCcKyxiiK/sJnS github.com/libdns/namecheap v0.0.0-20211109042440-fc7440785c8e/go.mod h1:dED6sMLZxIcilF1GjrcpwgVoCglXGMn86irqQzRhqRY= github.com/libdns/namedotcom v0.3.3 h1:R10C7+IqQGVeC4opHHMiFNBxdNBg1bi65ZwqLESl+jE= github.com/libdns/namedotcom v0.3.3/go.mod h1:GbYzsAF2yRUpI0WgIK5fs5UX+kDVUPaYCFLpTnKQm0s= +github.com/libdns/rfc2136 v0.1.0 h1:BlGOPfx/R3xqKrgHT9TlreA8Ulw8ti8+VtJj8E0H9hE= +github.com/libdns/rfc2136 v0.1.0/go.mod h1:tgXWavE+5OiAfdKxBnuG8OBEwQFAu7uuiS3+laspAGs= github.com/libdns/route53 v1.3.3 h1:16sTxbbRGm0zODz0p0aVHHIyTqtHzEn3j0s4dGzQvNI= github.com/libdns/route53 v1.3.3/go.mod h1:n1Xy55lpfdxMIx4CVWAM16GQac+/OZcnm1xBjMyhZAo= github.com/libdns/vultr v1.0.0 h1:W8B4+k2bm9ro3bZLSZV9hMOQI+uO6Svu+GmD+Olz7ZI= @@ -553,6 +563,7 @@ github.com/minio/minio-go/v7 v7.0.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0 github.com/minio/minio-go/v7 v7.0.66/go.mod h1:DHAgmyQEGdW3Cif0UooKOyrT3Vxs82zNdV6tkKhRtbs= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -560,10 +571,12 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6 h1:TsF5Cl0Mj5JMvPOP2ySVq+CZoiPrTGwvNPbuQotuSAE= github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6/go.mod h1:4PEbISVqRCQaXaDAt289w3nK9UhoF8/ZOLy31Hbv7ds= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd h1:4yVpQ/+li28lQ/daYCWeDB08obRmjaoAw2qfFFaCQ40= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd/go.mod h1:wpK5wqysOJU1w2OxgG65du8M7UqBkxzsNaJdjwiRqAs= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= @@ -572,7 +585,6 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -586,7 +598,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -597,8 +608,8 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 h1:J6qvD6rbmOil46orKqJaRPG+zTpoGlBTUdyv8ki63L0= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= @@ -609,6 +620,7 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= @@ -626,7 +638,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= @@ -643,11 +654,9 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de 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= -github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= -github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -668,7 +677,6 @@ go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xC go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= @@ -1150,8 +1158,6 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55/go.mod h1:45EK0dUbEZ2NHjCeAd2LXmyjAgGUGrpGROgjhC3ADck= -google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg= -google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM= google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= @@ -1211,7 +1217,6 @@ google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7 google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -1219,7 +1224,6 @@ gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3M gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= @@ -1239,8 +1243,6 @@ modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0= modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI= -modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= -modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= modernc.org/libc v1.40.6 h1:141JHq3SjhOOCjECBgD4K8VgTFOy19CnHwroC08DAig= modernc.org/libc v1.40.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= @@ -1253,10 +1255,8 @@ modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= -modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/libdns/rfc2136.go b/internal/libdns/rfc2136.go new file mode 100644 index 00000000..aa755623 --- /dev/null +++ b/internal/libdns/rfc2136.go @@ -0,0 +1,28 @@ +//go:build libdns_rfc2136 || libdns_all +// +build libdns_rfc2136 libdns_all + +package libdns + +import ( + "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/module" + "github.com/libdns/rfc2136" +) + +func init() { + module.Register("libdns.rfc2136", func(modName, instName string, _, _ []string) (module.Module, error) { + p := rfc2136.Provider{} + return &ProviderModule{ + RecordDeleter: &p, + RecordAppender: &p, + setConfig: func(c *config.Map) { + c.String("KeyName", false, true, "", &p.KeyName) + c.String("Key", false, true, "", &p.Key) + c.String("KeyAlg", false, true, "", &p.KeyAlg) + c.String("Server", false, true, "", &p.Server) + }, + instName: instName, + modName: modName, + }, nil + }) +} From 1d6cd8c35f4c4a1279084ae974ecfcf73a426743 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年2月14日 20:22:51 +0300 Subject: [PATCH 058/171] Bump go-imap-sql version See #681. --- go.mod | 2 +- go.sum | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 663eb055..a1cb1c60 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/foxcpp/go-imap-i18nlevel v0.0.0-20200208001533-d6ec88553005 github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613 github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed - github.com/foxcpp/go-imap-sql v0.5.1-0.20240121160244-7f314a0fe78a + github.com/foxcpp/go-imap-sql v0.5.1-0.20240214172211-ee5bc28d4278 github.com/foxcpp/go-mockdns v1.0.0 github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8 github.com/go-ldap/ldap/v3 v3.4.6 diff --git a/go.sum b/go.sum index 4455bdc6..de8d842f 100644 --- a/go.sum +++ b/go.sum @@ -237,6 +237,7 @@ github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc= github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -257,6 +258,8 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -320,6 +323,8 @@ github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed h1:1Jo7ge github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed/go.mod h1:Shows1vmkBWO40ChOClaUe6DUnZrsP1UPAuoWzIUdgQ= github.com/foxcpp/go-imap-sql v0.5.1-0.20240121160244-7f314a0fe78a h1:/c5NvIHDrrU6+7glgr4YHwN3REH1bGb1l8s9S6ruORg= github.com/foxcpp/go-imap-sql v0.5.1-0.20240121160244-7f314a0fe78a/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= +github.com/foxcpp/go-imap-sql v0.5.1-0.20240214172211-ee5bc28d4278 h1:7LGp/ryQH/MOTWgWgv7+cPEFKgKH1aADCEnus13G5Kg= +github.com/foxcpp/go-imap-sql v0.5.1-0.20240214172211-ee5bc28d4278/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= github.com/foxcpp/go-mockdns v0.0.0-20191216195825-5eabd8dbfe1f/go.mod h1:tPg4cp4nseejPd+UKxtCVQ2hUxNTZ7qQZJa7CLriIeo= github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= @@ -339,6 +344,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -346,6 +352,7 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -474,10 +481,12 @@ github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c h1:lx/uPI+m github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c/go.mod h1:LIAXxPvcUXwOcTIj9LSNSUpE9/eMHalTWxsP/kmWxQI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -487,6 +496,7 @@ github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -549,6 +559,7 @@ github.com/minio/minio-go/v7 v7.0.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0 github.com/minio/minio-go/v7 v7.0.66/go.mod h1:DHAgmyQEGdW3Cif0UooKOyrT3Vxs82zNdV6tkKhRtbs= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -556,10 +567,12 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6 h1:TsF5Cl0Mj5JMvPOP2ySVq+CZoiPrTGwvNPbuQotuSAE= github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6/go.mod h1:4PEbISVqRCQaXaDAt289w3nK9UhoF8/ZOLy31Hbv7ds= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd h1:4yVpQ/+li28lQ/daYCWeDB08obRmjaoAw2qfFFaCQ40= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd/go.mod h1:wpK5wqysOJU1w2OxgG65du8M7UqBkxzsNaJdjwiRqAs= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= @@ -595,6 +608,7 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 h1:J6qvD6rbmOil46orKqJaRPG+zTpoGlBTUdyv8ki63L0= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= @@ -605,6 +619,7 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= From eeb49621f48886300452975dc32437bd8d8a7d07 Mon Sep 17 00:00:00 2001 From: Louis Guidez reind33r Date: 2024年2月15日 23:55:55 +0100 Subject: [PATCH 059/171] libdns/rfc2136 use snake_case instead of PascalCase for configuration directives; clarify the meaning of the configuration directives --- docs/reference/tls-acme.md | 12 ++++++++---- internal/libdns/rfc2136.go | 8 ++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/reference/tls-acme.md b/docs/reference/tls-acme.md index 1cc7423c..d6479de1 100644 --- a/docs/reference/tls-acme.md +++ b/docs/reference/tls-acme.md @@ -267,10 +267,14 @@ dns namedotcom { ``` dns rfc2136 { - KeyName "..." - Key "..." - KeyAlg "..." - Server "..." + key_name "..." + # Secret + key "..." + # HMAC algorithm used to generate the key, lowercase, e.g. hmac-sha512 + key_alg "..." + # server to which the dynamic update will be sent, e.g. 127.0.0.1 + # you can also specify the port: 127.0.0.1:53 + server "..." } ``` diff --git a/internal/libdns/rfc2136.go b/internal/libdns/rfc2136.go index aa755623..19751f6f 100644 --- a/internal/libdns/rfc2136.go +++ b/internal/libdns/rfc2136.go @@ -16,10 +16,10 @@ func init() { RecordDeleter: &p, RecordAppender: &p, setConfig: func(c *config.Map) { - c.String("KeyName", false, true, "", &p.KeyName) - c.String("Key", false, true, "", &p.Key) - c.String("KeyAlg", false, true, "", &p.KeyAlg) - c.String("Server", false, true, "", &p.Server) + c.String("key_name", false, true, "", &p.KeyName) + c.String("key", false, true, "", &p.Key) + c.String("key_alg", false, true, "", &p.KeyAlg) + c.String("server", false, true, "", &p.Server) }, instName: instName, modName: modName, From bb2c0c9bcc8a52af22991a7445d718f762c24c68 Mon Sep 17 00:00:00 2001 From: Louis Guidez reind33r Date: 2024年2月16日 00:14:44 +0100 Subject: [PATCH 060/171] docs: corrected typo for rspamd check configuration --- docs/third-party/rspamd.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/third-party/rspamd.md b/docs/third-party/rspamd.md index b528215c..3d2ce483 100644 --- a/docs/third-party/rspamd.md +++ b/docs/third-party/rspamd.md @@ -7,7 +7,7 @@ If rspamd is running locally, it is enough to just add `rspamd` check with default configuration into appropriate check block (probably in local_routing): ``` -checks { +check { ... rspamd } From 62799b4b1e23735851939d40c9b31aba67b126fa Mon Sep 17 00:00:00 2001 From: oidq Date: 2024年3月13日 09:26:57 +0100 Subject: [PATCH 061/171] build: make "build.sh install" reusable * prevent the script form blindly overwriting the current configuration * force maddyctl linking so that we can reuse the script * fix indentation in build.sh --- build.sh | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/build.sh b/build.sh index 68e71dbd..3dc76de2 100755 --- a/build.sh +++ b/build.sh @@ -3,6 +3,7 @@ destdir=/ builddir="$PWD/build" prefix=/usr/local +configdir="${destdir}etc/maddy" version= static=0 if [ "${GOFLAGS}" = "" ]; then @@ -139,9 +140,18 @@ install() { command install -m 0755 -d "${destdir}/${prefix}/bin/" command install -m 0755 "${builddir}/maddy" "${destdir}/${prefix}/bin/" - command ln -s maddy "${destdir}/${prefix}/bin/maddyctl" - command install -m 0755 -d "${destdir}/etc/maddy/" - command install -m 0644 ./maddy.conf "${destdir}/etc/maddy/maddy.conf" + command ln -sf maddy "${destdir}/${prefix}/bin/maddyctl" + command install -m 0755 -d "${configdir}" + + + # We do not want to overwrite existing configuration. + # If the file exists, then save it with .default suffix and warn user. + if [ ! -e "${configdir}/maddy.conf" ]; then + command install -m 0644 ./maddy.conf "${configdir}/maddy.conf" + else + echo "-- [!] Configuration file ${configdir}/maddy.conf exists, saving to ${configdir}/maddy.conf.default">&2 + command install -m 0644 ./maddy.conf "${configdir}/maddy.conf.default" + fi # Attempt to install systemd units only for Linux. # Check is done using GOOS instead of uname -s to account for possible @@ -150,19 +160,19 @@ install() { # with sudo and go installation is user-specific, so fallback # to using uname -s in the end. set +e - if command -v go>/dev/null 2>/dev/null; then - set -e - if [ "$(go env GOOS)" = "linux" ]; then - command install -m 0755 -d "${destdir}/${prefix}/lib/systemd/system/" - command install -m 0644 "${builddir}"/systemd/*.service "${destdir}/${prefix}/lib/systemd/system/" - fi - else - set -e - if [ "$(uname -s)" = "Linux" ]; then - command install -m 0755 -d "${destdir}/${prefix}/lib/systemd/system/" - command install -m 0644 "${builddir}"/systemd/*.service "${destdir}/${prefix}/lib/systemd/system/" - fi - fi + if command -v go>/dev/null 2>/dev/null; then + set -e + if [ "$(go env GOOS)" = "linux" ]; then + command install -C -m 0755 -d "${destdir}/${prefix}/lib/systemd/system/" + command install -C -m 0644 "${builddir}"/systemd/*.service "${destdir}/${prefix}/lib/systemd/system/" + fi + else + set -e + if [ "$(uname -s)" = "Linux" ]; then + command install -C -m 0755 -d "${destdir}/${prefix}/lib/systemd/system/" + command install -C -m 0644 "${builddir}"/systemd/*.service "${destdir}/${prefix}/lib/systemd/system/" + fi + fi if [ -e "${builddir}"/man ]; then command install -m 0755 -d "${destdir}/${prefix}/share/man/man1/" From 198a9a3fb57da4f69c897ac4f2489d1d7415736e Mon Sep 17 00:00:00 2001 From: Robert Coleman Date: 2024年3月16日 16:03:57 +1300 Subject: [PATCH 062/171] add ACME-DNS provider for libdns --- docs/reference/tls-acme.md | 12 +++++++++++- go.mod | 1 + go.sum | 2 ++ internal/libdns/acmedns.go | 28 ++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 internal/libdns/acmedns.go diff --git a/docs/reference/tls-acme.md b/docs/reference/tls-acme.md index 9baccb5d..be29f94a 100644 --- a/docs/reference/tls-acme.md +++ b/docs/reference/tls-acme.md @@ -149,7 +149,7 @@ To be able to use these, you need to compile maddy with "libdns_PROVIDER" build tag. E.g. ``` -./build.sh -tags 'libdns_googleclouddns' +./build.sh --tags 'libdns_googleclouddns' ``` - gandi @@ -263,3 +263,13 @@ dns namedotcom { } ``` +- acmedns (non-default) + +``` +dns acmedns { + username "..." + password "..." + subdomain "..." + server_url "..." +} +``` diff --git a/go.mod b/go.mod index 68bf8a7a..3ac84825 100644 --- a/go.mod +++ b/go.mod @@ -103,6 +103,7 @@ require ( github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/klauspost/compress v1.17.4 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/libdns/acmedns v0.2.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect diff --git a/go.sum b/go.sum index 005925b8..be1dc81b 100644 --- a/go.sum +++ b/go.sum @@ -496,6 +496,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/libdns/acmedns v0.2.0 h1:zTXdHZwe3r2issdVRyqt5/4X2yHpiBVmFnTrwBA29ik= +github.com/libdns/acmedns v0.2.0/go.mod h1:XlKHilQQK/IGHYY//vCb903PdG4Wc/XnDQzcMp2hV3g= github.com/libdns/alidns v1.0.3-0.20230628155627-8d5d630d5516 h1:tPVSANkA4lo+K65YjsQcaQ1uh6sb0zRBQDz78l1Fo4Y= github.com/libdns/alidns v1.0.3-0.20230628155627-8d5d630d5516/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE= github.com/libdns/cloudflare v0.1.1-0.20221006221909-9d3ab3c3cddd h1:c5hc0b5/pFqFeyQaOTVmYJbyr+QwZZFcMnjgtZGIk6k= diff --git a/internal/libdns/acmedns.go b/internal/libdns/acmedns.go new file mode 100644 index 00000000..cb657ee2 --- /dev/null +++ b/internal/libdns/acmedns.go @@ -0,0 +1,28 @@ +//go:build libdns_acmedns || libdns_all +// +build libdns_acmedns libdns_all + +package libdns + +import ( + "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/module" + "github.com/libdns/acmedns" +) + +func init() { + module.Register("libdns.acmedns", func(modName, instName string, _, _ []string) (module.Module, error) { + p := acmedns.Provider{} + return &ProviderModule{ + RecordDeleter: &p, + RecordAppender: &p, + setConfig: func(c *config.Map) { + c.String("username", false, true, "", &p.Username) + c.String("password", false, true, "", &p.Password) + c.String("subdomain", false, true, "", &p.Subdomain) + c.String("server_url", false, true, "", &p.ServerURL) + }, + instName: instName, + modName: modName, + }, nil + }) +} From 3d81feeeaadcc951eb177e0d346a049c247c00ea Mon Sep 17 00:00:00 2001 From: Robert Coleman Date: 2024年3月16日 16:17:46 +1300 Subject: [PATCH 063/171] Add additional build tags to Dockerfile --- Dockerfile | 4 +++- docs/docker.md | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 5ceb6f7f..0cad071e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,7 @@ FROM golang:1.19-alpine AS build-env +ARG ADDITIONAL_BUILD_TAGS="" + RUN set -ex && \ apk upgrade --no-cache --available && \ apk add --no-cache build-base @@ -12,7 +14,7 @@ RUN go mod download COPY . ./ RUN mkdir -p /pkg/data && \ cp maddy.conf.docker /pkg/data/maddy.conf && \ - ./build.sh --builddir /tmp --destdir /pkg/ --tags docker build install + ./build.sh --builddir /tmp --destdir /pkg/ --tags "docker ${ADDITIONAL_BUILD_TAGS}" build install FROM alpine:3.18.4 LABEL maintainer="fox.cpp@disroot.org" diff --git a/docs/docker.md b/docs/docker.md index 2898a07b..decb7870 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -54,6 +54,11 @@ command. One way to it is to run it using `docker exec` instead of `docker run`: docker exec -it container_name_here maddy creds create foxcpp@maddy.test ``` +## Build Tags + +Some Maddy features (such as automatic certificate management via ACME with [a non-default libdns provider](../reference/tls-acme/#dns-providers)) require build tags to be passed to Maddy's `build.sh`, as this is run in the Dockerfile you must compile your own Docker image. Build tags can be set via the docker build argument `ADDITIONAL_BUILD_TAGS` e.g. `docker build --build-arg ADDITIONAL_BUILD_TAGS="libdns_acmedns libdns_route53" -t yourorgname/maddy:yourtagname .`. + + ## TL;DR ``` From 5b3a8685cd3cd2ab1a96b6efa01e12b25ddce9cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: 2024年4月19日 13:01:20 +0000 Subject: [PATCH 064/171] build(deps): bump golang.org/x/net from 0.20.0 to 0.23.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.20.0 to 0.23.0. - [Commits](https://github.com/golang/net/compare/v0.20.0...v0.23.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 27 ++++++--------------------- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index a1cb1c60..49fa98b0 100644 --- a/go.mod +++ b/go.mod @@ -48,8 +48,8 @@ require ( github.com/prometheus/client_golang v1.18.0 github.com/urfave/cli/v2 v2.27.1 go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.18.0 - golang.org/x/net v0.20.0 + golang.org/x/crypto v0.21.0 + golang.org/x/net v0.23.0 golang.org/x/sync v0.6.0 golang.org/x/text v0.14.0 modernc.org/sqlite v1.28.0 @@ -145,7 +145,7 @@ require ( golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/oauth2 v0.16.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/sys v0.18.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.17.0 // indirect google.golang.org/api v0.157.0 // indirect diff --git a/go.sum b/go.sum index de8d842f..4262d4ab 100644 --- a/go.sum +++ b/go.sum @@ -237,7 +237,6 @@ github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc= github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -258,8 +257,6 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -321,8 +318,6 @@ github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613 h1:fw9OWfPxP1C github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613/go.mod h1:P/O/qz4gaVkefzJ40BUtN/ZzBnaEg0YYe1no/SMp7Aw= github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed h1:1Jo7geyvunrPSjL6F6D9EcXoNApS5v3LQaro7aUNPnE= github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed/go.mod h1:Shows1vmkBWO40ChOClaUe6DUnZrsP1UPAuoWzIUdgQ= -github.com/foxcpp/go-imap-sql v0.5.1-0.20240121160244-7f314a0fe78a h1:/c5NvIHDrrU6+7glgr4YHwN3REH1bGb1l8s9S6ruORg= -github.com/foxcpp/go-imap-sql v0.5.1-0.20240121160244-7f314a0fe78a/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= github.com/foxcpp/go-imap-sql v0.5.1-0.20240214172211-ee5bc28d4278 h1:7LGp/ryQH/MOTWgWgv7+cPEFKgKH1aADCEnus13G5Kg= github.com/foxcpp/go-imap-sql v0.5.1-0.20240214172211-ee5bc28d4278/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= github.com/foxcpp/go-mockdns v0.0.0-20191216195825-5eabd8dbfe1f/go.mod h1:tPg4cp4nseejPd+UKxtCVQ2hUxNTZ7qQZJa7CLriIeo= @@ -344,7 +339,6 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -352,7 +346,6 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -481,12 +474,10 @@ github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c h1:lx/uPI+m github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c/go.mod h1:LIAXxPvcUXwOcTIj9LSNSUpE9/eMHalTWxsP/kmWxQI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -496,7 +487,6 @@ github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -559,7 +549,6 @@ github.com/minio/minio-go/v7 v7.0.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0 github.com/minio/minio-go/v7 v7.0.66/go.mod h1:DHAgmyQEGdW3Cif0UooKOyrT3Vxs82zNdV6tkKhRtbs= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -567,12 +556,10 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6 h1:TsF5Cl0Mj5JMvPOP2ySVq+CZoiPrTGwvNPbuQotuSAE= github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6/go.mod h1:4PEbISVqRCQaXaDAt289w3nK9UhoF8/ZOLy31Hbv7ds= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd h1:4yVpQ/+li28lQ/daYCWeDB08obRmjaoAw2qfFFaCQ40= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd/go.mod h1:wpK5wqysOJU1w2OxgG65du8M7UqBkxzsNaJdjwiRqAs= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= @@ -608,7 +595,6 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 h1:J6qvD6rbmOil46orKqJaRPG+zTpoGlBTUdyv8ki63L0= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= @@ -619,7 +605,6 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= @@ -693,8 +678,8 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh 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.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -788,8 +773,8 @@ golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfS golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -907,8 +892,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= From dd41af02521400aa1cbeef696bd574ca5ff9de4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: 2024年5月29日 10:29:02 +0300 Subject: [PATCH 065/171] Fix typo receipients -> recipients --- internal/target/queue/queue.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/target/queue/queue.go b/internal/target/queue/queue.go index 525e40e0..264a70fb 100644 --- a/internal/target/queue/queue.go +++ b/internal/target/queue/queue.go @@ -498,7 +498,7 @@ func (q *Queue) deliver(meta *QueueMetadata, header textproto.Header, body buffe } if len(acceptedRcpts) == 0 { - dl.Debugf("delivery.Abort (no accepted receipients)") + dl.Debugf("delivery.Abort (no accepted recipients)") if err := delivery.Abort(msgCtx); err != nil { dl.Error("delivery.Abort failed", err) } From bbceeb0ce6b61af3513fc9c1ceb191ac0cd54766 Mon Sep 17 00:00:00 2001 From: w1kee <31793948+w1kee@users.noreply.github.com> Date: Mon, 3 Jun 2024 23:47:28 +0200 Subject: [PATCH 066/171] fix possible missing "not" from documentation this changes the meaning of the sentence, but i think the "not" was intended to be there because the sentence sounds weird without it --- docs/internals/unicode.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/internals/unicode.md b/docs/internals/unicode.md index 21ef8b05..91997624 100644 --- a/docs/internals/unicode.md +++ b/docs/internals/unicode.md @@ -1,7 +1,7 @@ # Unicode support maddy has the first-class Unicode support in all components (modules). You do -have to take any actions to make it work with internationalized domains, +not have to take any actions to make it work with internationalized domains, mailbox names or non-ASCII message headers. Internally, all text fields in maddy are represented in UTF-8 and handled using From f3a6f3241e9388034bdc996d904ff3bd0f8be1f6 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年7月23日 23:00:11 +0300 Subject: [PATCH 067/171] config/tls: Set min TLS version to 1.0 Better to have TLS 1.0 than no encryption at all. Default Go client setting of TLS 1.2 is too restrictive for mail infrastructure with lots of outdated servers. --- framework/config/tls/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/config/tls/server.go b/framework/config/tls/server.go index f40b3fd7..4d30d2cc 100644 --- a/framework/config/tls/server.go +++ b/framework/config/tls/server.go @@ -95,7 +95,7 @@ func readTLSBlock(globals map[string]interface{}, blockNode config.Node) (*TLSCo }, &loader) childM.Custom("protocols", false, false, func() (interface{}, error) { - return [2]uint16{0, 0}, nil + return [2]uint16{tls.VersionTLS10, 0}, nil }, TLSVersionsDirective, &tlsVersions) childM.Custom("ciphers", false, false, func() (interface{}, error) { From cbeadf169c8dfc3a824c9b83ab605fd792a300b7 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年8月31日 15:26:32 +0300 Subject: [PATCH 068/171] storage/imapsql: Bump go-imap-sql version Update includes proper check for modernc driver. See #723 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index a1cb1c60..1dce63c3 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/foxcpp/go-imap-i18nlevel v0.0.0-20200208001533-d6ec88553005 github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613 github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed - github.com/foxcpp/go-imap-sql v0.5.1-0.20240214172211-ee5bc28d4278 + github.com/foxcpp/go-imap-sql v0.5.1-0.20240831122236-655e4cb87d20 github.com/foxcpp/go-mockdns v1.0.0 github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8 github.com/go-ldap/ldap/v3 v3.4.6 diff --git a/go.sum b/go.sum index de8d842f..432eaa2f 100644 --- a/go.sum +++ b/go.sum @@ -325,6 +325,8 @@ github.com/foxcpp/go-imap-sql v0.5.1-0.20240121160244-7f314a0fe78a h1:/c5NvIHDrr github.com/foxcpp/go-imap-sql v0.5.1-0.20240121160244-7f314a0fe78a/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= github.com/foxcpp/go-imap-sql v0.5.1-0.20240214172211-ee5bc28d4278 h1:7LGp/ryQH/MOTWgWgv7+cPEFKgKH1aADCEnus13G5Kg= github.com/foxcpp/go-imap-sql v0.5.1-0.20240214172211-ee5bc28d4278/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= +github.com/foxcpp/go-imap-sql v0.5.1-0.20240831122236-655e4cb87d20 h1:q4NtuuK7Kuf8zHC1CF8p1GB1owd/IyN+zfqy8DQL9Ig= +github.com/foxcpp/go-imap-sql v0.5.1-0.20240831122236-655e4cb87d20/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= github.com/foxcpp/go-mockdns v0.0.0-20191216195825-5eabd8dbfe1f/go.mod h1:tPg4cp4nseejPd+UKxtCVQ2hUxNTZ7qQZJa7CLriIeo= github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= From fc179fc54b089e275c945394ce4119ab70375143 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2024年11月24日 19:11:52 +0300 Subject: [PATCH 069/171] config/tls: Disable TLS session tickets Workaround for Outlook delivery issues. See https://github.com/foxcpp/maddy/issues/730 --- framework/config/tls/server.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/framework/config/tls/server.go b/framework/config/tls/server.go index 4d30d2cc..c23fdc32 100644 --- a/framework/config/tls/server.go +++ b/framework/config/tls/server.go @@ -69,7 +69,10 @@ func TLSDirective(m *config.Map, node config.Node) (interface{}, error) { } func readTLSBlock(globals map[string]interface{}, blockNode config.Node) (*TLSConfig, error) { - baseCfg := tls.Config{} + baseCfg := tls.Config{ + // Workaround for issue https://github.com/foxcpp/maddy/issues/730 + SessionTicketsDisabled: true, + } var loader module.TLSLoader if len(blockNode.Args)> 0 { From 72416b0456726805c1c3b91aad825261c7098aa2 Mon Sep 17 00:00:00 2001 From: Matthias Schneider Date: Mon, 9 Dec 2024 09:03:45 +0100 Subject: [PATCH 070/171] #736: dmarc evalute added check if fromDomain is a TLD --- internal/dmarc/evaluate.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/dmarc/evaluate.go b/internal/dmarc/evaluate.go index adae7b8c..ff978e5a 100644 --- a/internal/dmarc/evaluate.go +++ b/internal/dmarc/evaluate.go @@ -207,6 +207,10 @@ func isAligned(fromDomain, authDomain string, mode AlignmentMode) bool { return strings.EqualFold(fromDomain, authDomain) } + tld, _ := publicsuffix.PublicSuffix(fromDomain) + if strings.EqualFold(fromDomain, tld) { + return strings.EqualFold(fromDomain, authDomain) + } orgDomainFrom, err := publicsuffix.EffectiveTLDPlusOne(fromDomain) if err != nil { return false From 5be9baa7be094adca6c2b461ee43ca58e8569e1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: 2024年12月11日 23:57:09 +0000 Subject: [PATCH 071/171] build(deps): bump golang.org/x/crypto from 0.21.0 to 0.31.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.21.0 to 0.31.0. - [Commits](https://github.com/golang/crypto/compare/v0.21.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- go.mod | 14 +++++++------- go.sum | 29 ++++++++++++++--------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index 8ece169e..fc1b5104 100644 --- a/go.mod +++ b/go.mod @@ -48,10 +48,10 @@ require ( github.com/prometheus/client_golang v1.18.0 github.com/urfave/cli/v2 v2.27.1 go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.21.0 - golang.org/x/net v0.23.0 - golang.org/x/sync v0.6.0 - golang.org/x/text v0.14.0 + golang.org/x/crypto v0.31.0 + golang.org/x/net v0.25.0 + golang.org/x/sync v0.10.0 + golang.org/x/text v0.21.0 modernc.org/sqlite v1.28.0 ) @@ -143,11 +143,11 @@ require ( go.opentelemetry.io/otel/trace v1.22.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect - golang.org/x/mod v0.14.0 // indirect + golang.org/x/mod v0.17.0 // indirect golang.org/x/oauth2 v0.16.0 // indirect - golang.org/x/sys v0.18.0 // indirect + golang.org/x/sys v0.28.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.17.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/api v0.157.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect diff --git a/go.sum b/go.sum index 09293af0..db449327 100644 --- a/go.sum +++ b/go.sum @@ -318,8 +318,6 @@ github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613 h1:fw9OWfPxP1C github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613/go.mod h1:P/O/qz4gaVkefzJ40BUtN/ZzBnaEg0YYe1no/SMp7Aw= github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed h1:1Jo7geyvunrPSjL6F6D9EcXoNApS5v3LQaro7aUNPnE= github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed/go.mod h1:Shows1vmkBWO40ChOClaUe6DUnZrsP1UPAuoWzIUdgQ= -github.com/foxcpp/go-imap-sql v0.5.1-0.20240214172211-ee5bc28d4278 h1:7LGp/ryQH/MOTWgWgv7+cPEFKgKH1aADCEnus13G5Kg= -github.com/foxcpp/go-imap-sql v0.5.1-0.20240214172211-ee5bc28d4278/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= github.com/foxcpp/go-imap-sql v0.5.1-0.20240831122236-655e4cb87d20 h1:q4NtuuK7Kuf8zHC1CF8p1GB1owd/IyN+zfqy8DQL9Ig= github.com/foxcpp/go-imap-sql v0.5.1-0.20240831122236-655e4cb87d20/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= github.com/foxcpp/go-mockdns v0.0.0-20191216195825-5eabd8dbfe1f/go.mod h1:tPg4cp4nseejPd+UKxtCVQ2hUxNTZ7qQZJa7CLriIeo= @@ -680,8 +678,8 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh 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.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -721,8 +719,8 @@ golang.org/x/mod v0.4.1/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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -775,8 +773,8 @@ golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfS golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -819,8 +817,8 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -894,8 +892,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -916,8 +914,9 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -978,8 +977,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 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= From c0fd00de3e1dd7202dda756618cfed10f3ef0acc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: 2025年1月24日 13:51:38 +0000 Subject: [PATCH 072/171] build(deps): bump golang.org/x/net from 0.25.0 to 0.33.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.25.0 to 0.33.0. - [Commits](https://github.com/golang/net/compare/v0.25.0...v0.33.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fc1b5104..0655d423 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/urfave/cli/v2 v2.27.1 go.uber.org/zap v1.26.0 golang.org/x/crypto v0.31.0 - golang.org/x/net v0.25.0 + golang.org/x/net v0.33.0 golang.org/x/sync v0.10.0 golang.org/x/text v0.21.0 modernc.org/sqlite v1.28.0 diff --git a/go.sum b/go.sum index db449327..e1f551df 100644 --- a/go.sum +++ b/go.sum @@ -773,8 +773,8 @@ golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfS golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= From a8d639d374707d4f10420b74519bcde3a1da375e Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月24日 17:02:52 +0300 Subject: [PATCH 073/171] Bump go-imap-sql version See #723 comments --- go.mod | 2 +- go.sum | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 0655d423..12173b91 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/foxcpp/go-imap-i18nlevel v0.0.0-20200208001533-d6ec88553005 github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613 github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed - github.com/foxcpp/go-imap-sql v0.5.1-0.20240831122236-655e4cb87d20 + github.com/foxcpp/go-imap-sql v0.5.1-0.20250124140007-8da5567429d5 github.com/foxcpp/go-mockdns v1.0.0 github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8 github.com/go-ldap/ldap/v3 v3.4.6 diff --git a/go.sum b/go.sum index e1f551df..e958abc4 100644 --- a/go.sum +++ b/go.sum @@ -237,6 +237,7 @@ github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc= github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -257,6 +258,8 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -320,6 +323,8 @@ github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed h1:1Jo7ge github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed/go.mod h1:Shows1vmkBWO40ChOClaUe6DUnZrsP1UPAuoWzIUdgQ= github.com/foxcpp/go-imap-sql v0.5.1-0.20240831122236-655e4cb87d20 h1:q4NtuuK7Kuf8zHC1CF8p1GB1owd/IyN+zfqy8DQL9Ig= github.com/foxcpp/go-imap-sql v0.5.1-0.20240831122236-655e4cb87d20/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= +github.com/foxcpp/go-imap-sql v0.5.1-0.20250124140007-8da5567429d5 h1:jMxhw9qmwqg70qfMDWq0ImRHAduQjkTZOC9vBs5t2ug= +github.com/foxcpp/go-imap-sql v0.5.1-0.20250124140007-8da5567429d5/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= github.com/foxcpp/go-mockdns v0.0.0-20191216195825-5eabd8dbfe1f/go.mod h1:tPg4cp4nseejPd+UKxtCVQ2hUxNTZ7qQZJa7CLriIeo= github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= @@ -339,6 +344,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -346,6 +352,7 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -474,10 +481,12 @@ github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c h1:lx/uPI+m github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c/go.mod h1:LIAXxPvcUXwOcTIj9LSNSUpE9/eMHalTWxsP/kmWxQI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -487,6 +496,7 @@ github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -549,6 +559,7 @@ github.com/minio/minio-go/v7 v7.0.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0 github.com/minio/minio-go/v7 v7.0.66/go.mod h1:DHAgmyQEGdW3Cif0UooKOyrT3Vxs82zNdV6tkKhRtbs= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -556,10 +567,12 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6 h1:TsF5Cl0Mj5JMvPOP2ySVq+CZoiPrTGwvNPbuQotuSAE= github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6/go.mod h1:4PEbISVqRCQaXaDAt289w3nK9UhoF8/ZOLy31Hbv7ds= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd h1:4yVpQ/+li28lQ/daYCWeDB08obRmjaoAw2qfFFaCQ40= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd/go.mod h1:wpK5wqysOJU1w2OxgG65du8M7UqBkxzsNaJdjwiRqAs= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= @@ -595,6 +608,7 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 h1:J6qvD6rbmOil46orKqJaRPG+zTpoGlBTUdyv8ki63L0= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= @@ -605,6 +619,7 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= From 87f3ed165adf3ca9794c3e93fe1b81fb025e896e Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月24日 18:56:33 +0300 Subject: [PATCH 074/171] maddy 0.8.0 --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index 39e898a4..a3df0a69 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.7.1 +0.8.0 From 78e4600a17c2eb660958daa8e07cd9d6d8c4438d Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月24日 21:29:48 +0300 Subject: [PATCH 075/171] Upgrade all dependencies 1. go-smtp is replaced by a fork that reverts StartTLS removal. 2. SASL LOGIN is no longer supported by upstream go-sasl, readded disabled by default. 3. Updated endpoint code to match new go-smtp authentication interfaces. 4. certmagic repo had some renames 5. Minimum Go version increased to 1.23 to match dependencies. --- .golangci.yml | 5 +- Dockerfile | 4 +- docs/reference/endpoints/imap.md | 11 + docs/reference/endpoints/smtp.md | 9 + docs/reference/targets/smtp.md | 18 +- docs/tutorials/building-from-source.md | 13 +- go.mod | 210 ++++--- go.sum | 544 ++++++++++-------- internal/auth/sasl.go | 33 +- internal/auth/sasl_test.go | 6 +- internal/auth/sasllogin/sasllogin.go | 54 ++ .../endpoint/dovecot_sasld/dovecot_sasl.go | 3 +- internal/endpoint/imap/imap.go | 3 +- internal/endpoint/smtp/session.go | 23 +- internal/endpoint/smtp/smtp.go | 19 +- internal/smtpconn/smtpconn.go | 12 + internal/target/remote/connect.go | 26 +- internal/testutils/smtp_server.go | 33 +- internal/tls/acme/acme.go | 10 +- tests/basic_test.go | 3 +- 20 files changed, 608 insertions(+), 431 deletions(-) create mode 100644 internal/auth/sasllogin/sasllogin.go diff --git a/.golangci.yml b/.golangci.yml index 47b5bb3e..8617544c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,12 +1,9 @@ linters: enable: - gosimple - - structcheck - - varcheck - errcheck - staticcheck - ineffassign - - deadcode - typecheck - govet - unused @@ -17,4 +14,4 @@ linters: - whitespace - nakedret - dogsled - - exportloopref + - copyloopvar diff --git a/Dockerfile b/Dockerfile index 0cad071e..2da6211f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.19-alpine AS build-env +FROM golang:1.23-alpine AS build-env ARG ADDITIONAL_BUILD_TAGS="" @@ -16,7 +16,7 @@ RUN mkdir -p /pkg/data && \ cp maddy.conf.docker /pkg/data/maddy.conf && \ ./build.sh --builddir /tmp --destdir /pkg/ --tags "docker ${ADDITIONAL_BUILD_TAGS}" build install -FROM alpine:3.18.4 +FROM alpine:3.21.2 LABEL maintainer="fox.cpp@disroot.org" LABEL org.opencontainers.image.source=https://github.com/foxcpp/maddy diff --git a/docs/reference/endpoints/imap.md b/docs/reference/endpoints/imap.md index 0382da28..ec0d2e60 100644 --- a/docs/reference/endpoints/imap.md +++ b/docs/reference/endpoints/imap.md @@ -18,6 +18,7 @@ imap tcp://0.0.0.0:143 tls://0.0.0.0:993 { io_debug no debug no insecure_auth no + sasl_login no auth pam storage &local_mailboxes auth_map identity @@ -88,6 +89,16 @@ Enable verbose logging. ### insecure_auth _boolean_ Default: `no` (`yes` if TLS is disabled) +Allow plain-text authentication over unencrypted connections. + +--- + +### sasl_login _boolean_ +Default: `no` + +Enable support for SASL LOGIN authentication mechanism used by +some outdated clients. + --- ### auth _module-reference_ diff --git a/docs/reference/endpoints/smtp.md b/docs/reference/endpoints/smtp.md index 740d41a0..4dfa723a 100644 --- a/docs/reference/endpoints/smtp.md +++ b/docs/reference/endpoints/smtp.md @@ -11,6 +11,7 @@ smtp tcp://0.0.0.0:25 { io_debug no debug no insecure_auth no + sasl_login no read_timeout 10m write_timeout 1m max_message_size 32M @@ -102,6 +103,14 @@ Allow plain-text authentication over unencrypted connections. Not recommended! --- +### sasl_login _boolean_ +Default: `no` + +Enable support for SASL LOGIN authentication mechanism used by +some outdated clients. + +--- + ### read_timeout _duration_ Default: `10m` diff --git a/docs/reference/targets/smtp.md b/docs/reference/targets/smtp.md index 7ee56abd..ebbc4308 100644 --- a/docs/reference/targets/smtp.md +++ b/docs/reference/targets/smtp.md @@ -49,19 +49,27 @@ Advanced TLS client configuration options. See [TLS configuration / Client](/ref --- +### starttls _boolean_ +Default: `yes` (`no` for `target.lmtp`) + +Use STARTTLS to enable TLS encryption. If STARTTLS is not supported +by the remote server - connection will fail. + +maddy will use `localhost` as HELO hostname before STARTTLS +and will only send its actual hostname after STARTTLS. + ### attempt_starttls _boolean_ Default: `yes` (`no` for `target.lmtp`) -Attempt to use STARTTLS if it is supported by the remote server. -If TLS handshake fails, connection will be retried without STARTTLS -unless `require_tls` is also specified. +DEPRECATED: Equivalent to `starttls`. Plaintext fallback is no longer +supported. --- ### require_tls _boolean_ Default: `no` -Refuse to pass messages over plain-text connections. +DEPRECATED: Ignored. Set `starttls yes` to use STARTLS. --- @@ -112,4 +120,4 @@ Same as for target.remote. ### submission_timeout _duration_ Default: `12m` -Same as for target.remote. \ No newline at end of file +Same as for target.remote. diff --git a/docs/tutorials/building-from-source.md b/docs/tutorials/building-from-source.md index a2e37971..55b6852e 100644 --- a/docs/tutorials/building-from-source.md +++ b/docs/tutorials/building-from-source.md @@ -6,7 +6,7 @@ You need C toolchain, Go toolchain and Make: On Debian-based system this should work: ``` -apt-get install golang-1.19 gcc libc6-dev make +apt-get install golang-1.23 gcc libc6-dev make ``` Additionally, if you want manual pages, you should also have scdoc installed. @@ -18,10 +18,13 @@ reader (for Ubuntu 22.04 LTS it is in repositories). maddy depends on a rather recent Go toolchain version that may not be available in some distributions (*cough* Debian *cough*). -It should not be hard to grab a recent built toolchain from golang.org: +`go` command in Go 1.21 or newer will automatically download up-to-date +toolchain to build maddy. It is necessary to run commands below only +if you have `go` command version older than 1.21. + ``` -wget "https://dl.google.com/go/go1.19.9.linux-amd64.tar.gz" -tar xf "go1.19.19.linux-amd64.tar.gz" +wget "https://go.dev/dl/go1.23.5.linux-amd64.tar.gz" +tar xf "go1.23.5.linux-amd64.tar.gz" export GOROOT="$PWD/go" export PATH="$PWD/go/bin:$PATH" ``` @@ -36,7 +39,7 @@ $ cd maddy 2. Select the appropriate version to build: ``` -$ git checkout v0.7.0 # a specific release +$ git checkout v0.8.0 # a specific release $ git checkout master # next bugfix release $ git checkout dev # next feature release ``` diff --git a/go.mod b/go.mod index 70ac58cb..bfa5673c 100644 --- a/go.mod +++ b/go.mod @@ -1,175 +1,169 @@ module github.com/foxcpp/maddy -go 1.19 +go 1.23 require ( blitiri.com.ar/go/spf v1.5.1 github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 github.com/c0va23/go-proxyprotocol v0.9.1 - github.com/caddyserver/certmagic v0.20.0 + github.com/caddyserver/certmagic v0.21.7 github.com/emersion/go-imap v1.2.2-0.20220928192137-6fac715be9cf github.com/emersion/go-imap-compress v0.0.0-20201103190257-14809af1d1b9 github.com/emersion/go-imap-sortthread v1.2.0 - github.com/emersion/go-message v0.18.0 - github.com/emersion/go-milter v0.4.0 + github.com/emersion/go-message v0.18.2 + github.com/emersion/go-milter v0.4.1 github.com/emersion/go-msgauth v0.6.8 - github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 - github.com/emersion/go-smtp v0.20.2-0.20240121112028-434ddca4792e + github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 + github.com/emersion/go-smtp v0.21.3 github.com/foxcpp/go-dovecot-sasl v0.0.0-20200522223722-c4699d7a24bf github.com/foxcpp/go-imap-backend-tests v0.0.0-20220105184719-e80aa29a5e16 github.com/foxcpp/go-imap-i18nlevel v0.0.0-20200208001533-d6ec88553005 github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613 github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed github.com/foxcpp/go-imap-sql v0.5.1-0.20250124140007-8da5567429d5 - github.com/foxcpp/go-mockdns v1.0.0 - github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8 - github.com/go-ldap/ldap/v3 v3.4.6 - github.com/go-sql-driver/mysql v1.7.1 - github.com/google/uuid v1.5.0 - github.com/hashicorp/go-hclog v1.6.2 + github.com/foxcpp/go-mockdns v1.1.0 + github.com/foxcpp/go-mtasts v0.0.0-20240130093538-1438da2e5932 + github.com/go-ldap/ldap/v3 v3.4.10 + github.com/go-sql-driver/mysql v1.8.1 + github.com/google/uuid v1.6.0 + github.com/hashicorp/go-hclog v1.6.3 github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c github.com/lib/pq v1.10.9 - github.com/libdns/alidns v1.0.3-0.20230628155627-8d5d630d5516 - github.com/libdns/cloudflare v0.1.1-0.20221006221909-9d3ab3c3cddd + github.com/libdns/acmedns v0.2.0 + github.com/libdns/alidns v1.0.3 + github.com/libdns/cloudflare v0.1.1 github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea github.com/libdns/gandi v1.0.3 github.com/libdns/googleclouddns v1.1.0 github.com/libdns/hetzner v0.0.1 - github.com/libdns/leaseweb v0.3.1 - github.com/libdns/libdns v0.2.2-0.20230227175549-2dc480633939 + github.com/libdns/leaseweb v0.4.0 + github.com/libdns/libdns v0.2.2 github.com/libdns/metaname v0.3.0 github.com/libdns/namecheap v0.0.0-20211109042440-fc7440785c8e github.com/libdns/namedotcom v0.3.3 - github.com/libdns/rfc2136 v0.1.0 - github.com/libdns/route53 v1.3.3 + github.com/libdns/rfc2136 v0.1.1 + github.com/libdns/route53 v1.5.1 github.com/libdns/vultr v1.0.0 - github.com/mattn/go-sqlite3 v1.14.19 - github.com/miekg/dns v1.1.58 - github.com/minio/minio-go/v7 v7.0.66 - github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6 - github.com/prometheus/client_golang v1.18.0 - github.com/urfave/cli/v2 v2.27.1 - go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.31.0 - golang.org/x/net v0.33.0 + github.com/mattn/go-sqlite3 v1.14.24 + github.com/miekg/dns v1.1.63 + github.com/minio/minio-go/v7 v7.0.84 + github.com/netauth/netauth v0.6.2 + github.com/prometheus/client_golang v1.20.5 + github.com/urfave/cli/v2 v2.27.5 + go.uber.org/zap v1.27.0 + golang.org/x/crypto v0.32.0 + golang.org/x/net v0.34.0 golang.org/x/sync v0.10.0 golang.org/x/text v0.21.0 - modernc.org/sqlite v1.28.0 + modernc.org/sqlite v1.34.5 ) require ( - cloud.google.com/go/compute v1.23.3 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect + cloud.google.com/go/auth v0.14.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect + cloud.google.com/go/compute/metadata v0.6.0 // indirect + filippo.io/edwards25519 v1.1.0 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect github.com/aws/aws-sdk-go v1.44.40 // indirect - github.com/aws/aws-sdk-go-v2 v1.24.1 // indirect - github.com/aws/aws-sdk-go-v2/config v1.26.5 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.16.16 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect - github.com/aws/aws-sdk-go-v2/service/route53 v1.37.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect - github.com/aws/smithy-go v1.19.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.33.0 // indirect + github.com/aws/aws-sdk-go-v2/config v1.29.1 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.54 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.24 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.9 // indirect + github.com/aws/aws-sdk-go-v2/service/route53 v1.48.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.11 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.10 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.9 // indirect + github.com/aws/smithy-go v1.22.2 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect - github.com/digitalocean/godo v1.108.0 // indirect + github.com/caddyserver/zerossl v0.1.3 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect + github.com/digitalocean/godo v1.134.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect - github.com/fatih/color v1.16.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect - github.com/go-logr/logr v1.4.1 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect + github.com/go-ini/ini v1.67.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/goccy/go-json v0.10.4 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/s2a-go v0.1.7 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-retryablehttp v0.7.5 // indirect + github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/klauspost/compress v1.17.4 // indirect - github.com/klauspost/cpuid/v2 v2.2.6 // indirect - github.com/libdns/acmedns v0.2.0 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/magiconair/properties v1.8.9 // indirect + github.com/mailru/easyjson v0.9.0 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mholt/acmez v1.2.0 // indirect + github.com/mholt/acmez/v3 v3.0.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect - github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd // indirect - github.com/pelletier/go-toml/v2 v2.1.1 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.46.0 // indirect - github.com/prometheus/procfs v0.12.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect - github.com/rs/xid v1.5.0 // indirect + github.com/rs/xid v1.6.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.18.2 // indirect + github.com/spf13/viper v1.19.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/vultr/govultr/v3 v3.6.1 // indirect - github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect - github.com/zeebo/blake3 v0.2.3 // indirect - go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect - go.opentelemetry.io/otel v1.22.0 // indirect - go.opentelemetry.io/otel/metric v1.22.0 // indirect - go.opentelemetry.io/otel/trace v1.22.0 // indirect + github.com/vultr/govultr/v3 v3.14.1 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect + github.com/zeebo/blake3 v0.2.4 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/oauth2 v0.16.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - google.golang.org/api v0.157.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect - google.golang.org/grpc v1.60.1 // indirect - google.golang.org/protobuf v1.32.0 // indirect + go.uber.org/zap/exp v0.3.0 // indirect + golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/oauth2 v0.25.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/time v0.9.0 // indirect + golang.org/x/tools v0.29.0 // indirect + google.golang.org/api v0.218.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 // indirect + google.golang.org/grpc v1.70.0 // indirect + google.golang.org/protobuf v1.36.4 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools v2.2.0+incompatible // indirect - lukechampine.com/uint128 v1.3.0 // indirect - modernc.org/cc/v3 v3.41.0 // indirect - modernc.org/ccgo/v3 v3.16.15 // indirect - modernc.org/libc v1.40.6 // indirect - modernc.org/mathutil v1.6.0 // indirect - modernc.org/memory v1.7.2 // indirect - modernc.org/opt v0.1.3 // indirect - modernc.org/strutil v1.2.0 // indirect - modernc.org/token v1.1.0 // indirect + modernc.org/libc v1.61.9 // indirect + modernc.org/mathutil v1.7.1 // indirect + modernc.org/memory v1.8.2 // indirect ) replace github.com/emersion/go-imap => github.com/foxcpp/go-imap v1.0.0-beta.1.0.20220623182312-df940c324887 +replace github.com/emersion/go-smtp => github.com/foxcpp/go-smtp v1.21.4-0.20250124171104-c8519ae4fb23 // v1.21.3+maddy.1 + replace github.com/libdns/gandi => github.com/foxcpp/libdns-gandi v1.0.4-0.20240127130558-4782f9d5ce3e // v1.0.3+maddy.1 diff --git a/go.sum b/go.sum index 4feb0755..2f99728a 100644 --- a/go.sum +++ b/go.sum @@ -32,7 +32,8 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9 cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= +cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= +cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= @@ -47,6 +48,10 @@ cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjby cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/auth v0.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM= +cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A= +cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= +cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= @@ -69,10 +74,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= -cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= +cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= @@ -180,6 +183,8 @@ cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuW cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -188,67 +193,53 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 h1:IEjq88XO4PuBDcvmjQJcQGg+w+UaafSy8G5Kcb5tBhI= github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5/go.mod h1:exZ0C/1emQJAw5tHOaUDyY1ycttqBAPcxuzf7QbY6ec= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA= -github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= +github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/aws/aws-sdk-go v1.17.4/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.44.40 h1:MR0qefjBJrZuXE0VoeKMQFtjS2tUeVpbQNfb7NzQNgI= github.com/aws/aws-sdk-go v1.44.40/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go-v2 v1.17.8/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= -github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU= -github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= -github.com/aws/aws-sdk-go-v2/config v1.18.21/go.mod h1:+jPQiVPz1diRnjj6VGqWcLK6EzNmQ42l7J3OqGTLsSY= -github.com/aws/aws-sdk-go-v2/config v1.26.5 h1:lodGSevz7d+kkFJodfauThRxK9mdJbyutUxGq1NNhvw= -github.com/aws/aws-sdk-go-v2/config v1.26.5/go.mod h1:DxHrz6diQJOc9EwDslVRh84VjjrE17g+pVZXUeSxaDU= -github.com/aws/aws-sdk-go-v2/credentials v1.13.20/go.mod h1:xtZnXErtbZ8YGXC3+8WfajpMBn5Ga/3ojZdxHq6iI8o= -github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8= -github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.2/go.mod h1:cDh1p6XkSGSwSRIArWRc6+UqAQ7x4alQ0QfpVR6f+co= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.32/go.mod h1:RudqOgadTWdcS3t/erPQo24pcVEoYyqj/kKW5Vya21I= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.26/go.mod h1:vq86l7956VgFr0/FWQ2BWnK07QC3WYsepKzy33qqY5U= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.33/go.mod h1:zG2FcwjQarWaqXSCGpgcr3RSjZ6dHGguZSppUL0XR7Q= -github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.26/go.mod h1:Bd4C/4PkVGubtNe5iMXu5BNnaBi/9t/UsFspPt4ram8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 h1:DBYTXwIGQSGs9w4jKm60F5dmCQ3EEruxdc0MFh+3EY4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10/go.mod h1:wohMUQiFdzo0NtxbBg0mSRGZ4vL3n0dKjLTINdcIino= -github.com/aws/aws-sdk-go-v2/service/route53 v1.27.7/go.mod h1:Jhu94omkrksnqX6Xs4Qo10eA1Fx+2NYKjZMU4GvZLp0= -github.com/aws/aws-sdk-go-v2/service/route53 v1.37.0 h1:f3hBZWtpn9clZGXJoqahQeec9ZPZnu22g8pg+zNyif0= -github.com/aws/aws-sdk-go-v2/service/route53 v1.37.0/go.mod h1:8qqfpG4mug2JLlEyWPSFhEGvJiaZ9iPmMDDMYc5Xtas= -github.com/aws/aws-sdk-go-v2/service/sso v1.12.8/go.mod h1:GNIveDnP+aE3jujyUSH5aZ/rktsTM5EvtKnCqBZawdw= -github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow= -github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.8/go.mod h1:44qFP1g7pfd+U+sQHLPalAPKnyfTZjJsYR4xIwsJy5o= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8= -github.com/aws/aws-sdk-go-v2/service/sts v1.18.9/go.mod h1:yyW88BEPXA2fGFyI2KCcZC3dNpiT0CZAHaF+i656/tQ= -github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0= -github.com/aws/aws-sdk-go-v2/service/sts v1.26.7/go.mod h1:6h2YuIoxaMSCFf5fi1EgZAwdfkGMgDY+DVfa61uLe4U= -github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= -github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= +github.com/aws/aws-sdk-go-v2 v1.33.0 h1:Evgm4DI9imD81V0WwD+TN4DCwjUMdc94TrduMLbgZJs= +github.com/aws/aws-sdk-go-v2 v1.33.0/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2/config v1.29.1 h1:JZhGawAyZ/EuJeBtbQYnaoftczcb2drR2Iq36Wgz4sQ= +github.com/aws/aws-sdk-go-v2/config v1.29.1/go.mod h1:7bR2YD5euaxBhzt2y/oDkt3uNRb6tjFp98GlTFueRwk= +github.com/aws/aws-sdk-go-v2/credentials v1.17.54 h1:4UmqeOqJPvdvASZWrKlhzpRahAulBfyTJQUaYy4+hEI= +github.com/aws/aws-sdk-go-v2/credentials v1.17.54/go.mod h1:RTdfo0P0hbbTxIhmQrOsC/PquBZGabEPnCaxxKRPSnI= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.24 h1:5grmdTdMsovn9kPZPI23Hhvp0ZyNm5cRO+IZFIYiAfw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.24/go.mod h1:zqi7TVKTswH3Ozq28PkmBmgzG1tona7mo9G2IJg4Cis= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28 h1:igORFSiH3bfq4lxKFkTSYDhJEUCYo6C8VKiWJjYwQuQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.28/go.mod h1:3So8EA/aAYm36L7XIvCVwLa0s5N0P7o2b1oqnx/2R4g= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28 h1:1mOW9zAUMhTSrMDssEHS/ajx8JcAj/IcftzcmNlmVLI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.28/go.mod h1:kGlXVIWDfvt2Ox5zEaNglmq0hXPHgQFNMix33Tw22jA= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.9 h1:TQmKDyETFGiXVhZfQ/I0cCFziqqX58pi4tKJGYGFSz0= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.9/go.mod h1:HVLPK2iHQBUx7HfZeOQSEu3v2ubZaAY2YPbAm5/WUyY= +github.com/aws/aws-sdk-go-v2/service/route53 v1.48.2 h1:Rxg1R0CHxVb9ggQLufOkr4an3yFEkTDN+N5+LFU4aEg= +github.com/aws/aws-sdk-go-v2/service/route53 v1.48.2/go.mod h1:TN4PcCL0lvqmYcv+AV8iZFC4Sd0FM06QDaoBXrFEftU= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.11 h1:kuIyu4fTT38Kj7YCC7ouNbVZSSpqkZ+LzIfhCr6Dg+I= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.11/go.mod h1:Ro744S4fKiCCuZECXgOi760TiYylUM8ZBf6OGiZzJtY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.10 h1:l+dgv/64iVlQ3WsBbnn+JSbkj01jIi+SM0wYsj3y/hY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.10/go.mod h1:Fzsj6lZEb8AkTE5S68OhcbBqeWPsR8RnGuKPr8Todl8= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.9 h1:BRVDbewN6VZcwr+FBOszDKvYeXY1kJ+GGMCcpghlw0U= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.9/go.mod h1:f6vjfZER1M17Fokn0IzssOTMT2N8ZSq+7jnNF0tArvw= +github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= +github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/c0va23/go-proxyprotocol v0.9.1 h1:5BCkp0fDJOhzzH1lhjUgHhmZz9VvRMMif1U2D31hb34= github.com/c0va23/go-proxyprotocol v0.9.1/go.mod h1:TNjUV+llvk8TvWJxlPYAeAYZgSzT/iicNr3nWBWX320= -github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE= -github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE= -github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc= -github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg= +github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg= +github.com/caddyserver/certmagic v0.21.7/go.mod h1:LCPG3WLxcnjVKl/xpjzM0gqh0knrKKKiO5WVttX2eEI= +github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= +github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -262,17 +253,17 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/digitalocean/godo v1.41.0/go.mod h1:p7dOjjtSBqCTUksqtA5Fd3uaKs9kyTq2xcz76ulEJRU= -github.com/digitalocean/godo v1.108.0 h1:fWyMENvtxpCpva1UbKzOFnyAS04N1FNuBWWfPeTGquQ= -github.com/digitalocean/godo v1.108.0/go.mod h1:R6EmmWI8CT1+fCtjWY9UCB+L5uufuZH13wk3YhxycCs= +github.com/digitalocean/godo v1.134.0 h1:dT7aQR9jxNOQEZwzP+tAYcxlj5szFZScC33+PAYGQVM= +github.com/digitalocean/godo v1.134.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emersion/go-imap-appendlimit v0.0.0-20190308131241-25671c986a6a/go.mod h1:ikgISoP7pRAolqsVP64yMteJa2FIpS6ju88eBT6K1yQ= @@ -281,22 +272,20 @@ github.com/emersion/go-imap-compress v0.0.0-20201103190257-14809af1d1b9/go.mod h github.com/emersion/go-imap-move v0.0.0-20180601155324-5eb20cb834bf/go.mod h1:QuMaZcKFDVI0yCrnAbPLfbwllz1wtOrZH8/vZ5yzp4w= github.com/emersion/go-imap-sortthread v1.2.0 h1:EMVEJXPWAhXMWECjR82Rn/tza6MddcvTwGAdTu1vJKU= github.com/emersion/go-imap-sortthread v1.2.0/go.mod h1:UhenCBupR+vSYRnqJkpjSq84INUCsyAK1MLpogv14pE= -github.com/emersion/go-message v0.11.2/go.mod h1:C4jnca5HOTo4bGN9YdqNQM9sITuT3Y0K6bSUw9RklvY= github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4= -github.com/emersion/go-message v0.18.0 h1:7LxAXHRpSeoO/Wom3ZApVZYG7c3d17yCScYce8WiXA8= github.com/emersion/go-message v0.18.0/go.mod h1:Zi69ACvzaoV/MBnrxfVBPV3xWEuCmC2nEN39oJF4B8A= -github.com/emersion/go-milter v0.4.0 h1:HysxeAzNEToJw1VQEwLrjJqgmd1iuDzYg2329T/q6/Y= -github.com/emersion/go-milter v0.4.0/go.mod h1:ablHK0pbLB83kMFBznp/Rj8aV+Kc3jw8cxzzmCNLIOY= +github.com/emersion/go-message v0.18.1/go.mod h1:XpJyL70LwRvq2a8rVbHXikPgKj8+aI0kGdHlg16ibYA= +github.com/emersion/go-message v0.18.2 h1:rl55SQdjd9oJcIoQNhubD2Acs1E6IzlZISRTK7x/Lpg= +github.com/emersion/go-message v0.18.2/go.mod h1:XpJyL70LwRvq2a8rVbHXikPgKj8+aI0kGdHlg16ibYA= +github.com/emersion/go-milter v0.4.1 h1:gLs9QD0zEHF8omgEw8M+aGz6iwBNpWLAcwgSur0ra4M= +github.com/emersion/go-milter v0.4.1/go.mod h1:erCQVl0mH4SX9jEvwe+wyndit0rQtmvMLH86V6NGtkI= github.com/emersion/go-msgauth v0.6.8 h1:kW/0E9E8Zx5CdKsERC/WnAvnXvX7q9wTHia1OA4944A= github.com/emersion/go-msgauth v0.6.8/go.mod h1:YDwuyTCUHu9xxmAeVj0eW4INnwB6NNZoPdLerpSxRrc= github.com/emersion/go-sasl v0.0.0-20191210011802-430746ea8b9b/go.mod h1:G/dpzLu16WtQpBfQ/z3LYiYJn3ZhKSGWn83fyoyQe/k= github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= -github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 h1:hH4PQfOndHDlpzYfLAAfl63E8Le6F2+EL/cdhlkyRJY= github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= -github.com/emersion/go-smtp v0.20.2-0.20240121112028-434ddca4792e h1:WAPhaiA+bDO/mFgCDQJKCQI/RbH/73lCcis4Jb8Y2ec= -github.com/emersion/go-smtp v0.20.2-0.20240121112028-434ddca4792e/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= -github.com/emersion/go-textwrapper v0.0.0-20160606182133-d0e65e56babe/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= -github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 h1:IbFBtwoTQyw0fIM5xv1HF+Y+3ZijDR839WMulgxCcUY= +github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk= +github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -309,8 +298,8 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= -github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/foxcpp/go-dovecot-sasl v0.0.0-20200522223722-c4699d7a24bf h1:rmBPY5fryjp9zLQYsUmQqqgsYq7qeVfrjtr96Tf9vD8= @@ -325,43 +314,46 @@ github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613 h1:fw9OWfPxP1C github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613/go.mod h1:P/O/qz4gaVkefzJ40BUtN/ZzBnaEg0YYe1no/SMp7Aw= github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed h1:1Jo7geyvunrPSjL6F6D9EcXoNApS5v3LQaro7aUNPnE= github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed/go.mod h1:Shows1vmkBWO40ChOClaUe6DUnZrsP1UPAuoWzIUdgQ= -github.com/foxcpp/go-imap-sql v0.5.1-0.20240831122236-655e4cb87d20 h1:q4NtuuK7Kuf8zHC1CF8p1GB1owd/IyN+zfqy8DQL9Ig= -github.com/foxcpp/go-imap-sql v0.5.1-0.20240831122236-655e4cb87d20/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= github.com/foxcpp/go-imap-sql v0.5.1-0.20250124140007-8da5567429d5 h1:jMxhw9qmwqg70qfMDWq0ImRHAduQjkTZOC9vBs5t2ug= github.com/foxcpp/go-imap-sql v0.5.1-0.20250124140007-8da5567429d5/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= github.com/foxcpp/go-mockdns v0.0.0-20191216195825-5eabd8dbfe1f/go.mod h1:tPg4cp4nseejPd+UKxtCVQ2hUxNTZ7qQZJa7CLriIeo= -github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= -github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= -github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8 h1:k8w0iy6GP9oeSZWUH3p2DqZHaXDKZGNs3NZGZMGfQHc= -github.com/foxcpp/go-mtasts v0.0.0-20191219193356-62bc3f1f74b8/go.mod h1:HO1YOCbBM8KjpgThMMFejHx6K/UsnEv2Oh9YGtBIlOU= +github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= +github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= +github.com/foxcpp/go-mtasts v0.0.0-20240130093538-1438da2e5932 h1:p04U/s8IZEc+PVWIDWGUgdqGq3xsixI7XRZ6Bp/xZbQ= +github.com/foxcpp/go-mtasts v0.0.0-20240130093538-1438da2e5932/go.mod h1:RtHIZCsScdjIzXpTTjmEljtUrIjQbPBTvw7F1tKQbKk= +github.com/foxcpp/go-smtp v1.21.4-0.20250124171104-c8519ae4fb23 h1:JSnsCrRrHNBlgfKVFBxFzp3fN/wS21t8fAHcZ9B1uWI= +github.com/foxcpp/go-smtp v1.21.4-0.20250124171104-c8519ae4fb23/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ= github.com/foxcpp/libdns-gandi v1.0.4-0.20240127130558-4782f9d5ce3e h1:hKk+CGUtwnKDGKINPEojeo91kx0tnV6V4tlzHehJPfg= github.com/foxcpp/libdns-gandi v1.0.4-0.20240127130558-4782f9d5ce3e/go.mod h1:G6dw58Xnji2xX+lb+uZxGbtmfxKllm1CGHE2bOPG3WA= github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= -github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk= +github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A= -github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-ldap/ldap/v3 v3.4.10 h1:ot/iwPOhfpNVgB1o+AVXljizWZ9JTp7YF5oeyONmcJU= +github.com/go-ldap/ldap/v3 v3.4.10/go.mod h1:JXh4Uxgi40P6E9rdsYqpUtbW46D9UTjJ9QSwGRznplY= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM= +github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -371,6 +363,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +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.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -389,8 +382,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -414,7 +407,6 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -436,20 +428,20 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -459,23 +451,39 @@ github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99 github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= -github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= +github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= +github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I= -github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= -github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= +github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -485,154 +493,147 @@ github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c h1:lx/uPI+m github.com/johannesboyne/gofakes3 v0.0.0-20210704111953-6a9f95c2941c/go.mod h1:LIAXxPvcUXwOcTIj9LSNSUpE9/eMHalTWxsP/kmWxQI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= -github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libdns/acmedns v0.2.0 h1:zTXdHZwe3r2issdVRyqt5/4X2yHpiBVmFnTrwBA29ik= github.com/libdns/acmedns v0.2.0/go.mod h1:XlKHilQQK/IGHYY//vCb903PdG4Wc/XnDQzcMp2hV3g= -github.com/libdns/alidns v1.0.3-0.20230628155627-8d5d630d5516 h1:tPVSANkA4lo+K65YjsQcaQ1uh6sb0zRBQDz78l1Fo4Y= -github.com/libdns/alidns v1.0.3-0.20230628155627-8d5d630d5516/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE= -github.com/libdns/cloudflare v0.1.1-0.20221006221909-9d3ab3c3cddd h1:c5hc0b5/pFqFeyQaOTVmYJbyr+QwZZFcMnjgtZGIk6k= -github.com/libdns/cloudflare v0.1.1-0.20221006221909-9d3ab3c3cddd/go.mod h1:ob9J/elFVmPWKNHOMynwtH0h+T3pBrEL18amCSliwAQ= +github.com/libdns/alidns v1.0.3 h1:LFHuGnbseq5+HCeGa1aW8awyX/4M2psB9962fdD2+yQ= +github.com/libdns/alidns v1.0.3/go.mod h1:e18uAG6GanfRhcJj6/tps2rCMzQJaYVcGKT+ELjdjGE= +github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn054= +github.com/libdns/cloudflare v0.1.1/go.mod h1:9VK91idpOjg6v7/WbjkEW49bSCxj00ALesIFDhJ8PBU= github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea h1:IGlMNZCUp8Ho7NYYorpP5ZJgg2mFXARs6eHs/pSqFkA= github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea/go.mod h1:B2TChhOTxvBflpRTHlguXWtwa1Ha5WI6JkB6aCViM+0= github.com/libdns/googleclouddns v1.1.0 h1:murPR1LfTZZObLV2OLxUVmymWH25glkMFKpDjkk2m0E= github.com/libdns/googleclouddns v1.1.0/go.mod h1:3tzd056dfqKlf71V8Oy19En4WjJ3ybyuWx6P9bQSCIw= github.com/libdns/hetzner v0.0.1 h1:WsmcsOKnfpKmzwhfyqhGQEIlEeEaEUvb7ezoJgBKaqU= github.com/libdns/hetzner v0.0.1/go.mod h1:Jj12aJipO9Ir7OGaXueJ5J1RnerFMD0auGa6k9kujG4= -github.com/libdns/leaseweb v0.3.1 h1:39R0d4RYAP6llXUnTsvM5ge7WMjLN9Zkinzj3/J+qVY= -github.com/libdns/leaseweb v0.3.1/go.mod h1:OeZtd+s2M1RfC3wIJF9SHZDFpD7H5RRiC6OPK3AWYjA= +github.com/libdns/leaseweb v0.4.0 h1:WG9R5AwewpYM4goymFwnG2SB0qwL8gMsSzwRHZHee/U= +github.com/libdns/leaseweb v0.4.0/go.mod h1:dvTvEn11JN6+ebhAQ60l+jiaBiEqyJFs3EIo0YBcQkU= github.com/libdns/libdns v0.1.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= -github.com/libdns/libdns v0.2.2-0.20230227175549-2dc480633939 h1:EvTiXkv78P20yfk4CUPmAkH3Cmumt3s/48WWiC2babY= -github.com/libdns/libdns v0.2.2-0.20230227175549-2dc480633939/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= +github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= +github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/libdns/metaname v0.3.0 h1:HJudLYthdv52TupOPczojip/nEQHW7xqk5+whGReva4= github.com/libdns/metaname v0.3.0/go.mod h1:a3hqEgj59tjWaWlF4WxQGhvMVtjz1E4Ngs1GfVS+VhQ= github.com/libdns/namecheap v0.0.0-20211109042440-fc7440785c8e h1:WCcKyxiiK/sJnST1ulVBKNg4J8luCYDdgUrp2ySMO2s= github.com/libdns/namecheap v0.0.0-20211109042440-fc7440785c8e/go.mod h1:dED6sMLZxIcilF1GjrcpwgVoCglXGMn86irqQzRhqRY= github.com/libdns/namedotcom v0.3.3 h1:R10C7+IqQGVeC4opHHMiFNBxdNBg1bi65ZwqLESl+jE= github.com/libdns/namedotcom v0.3.3/go.mod h1:GbYzsAF2yRUpI0WgIK5fs5UX+kDVUPaYCFLpTnKQm0s= -github.com/libdns/rfc2136 v0.1.0 h1:BlGOPfx/R3xqKrgHT9TlreA8Ulw8ti8+VtJj8E0H9hE= -github.com/libdns/rfc2136 v0.1.0/go.mod h1:tgXWavE+5OiAfdKxBnuG8OBEwQFAu7uuiS3+laspAGs= -github.com/libdns/route53 v1.3.3 h1:16sTxbbRGm0zODz0p0aVHHIyTqtHzEn3j0s4dGzQvNI= -github.com/libdns/route53 v1.3.3/go.mod h1:n1Xy55lpfdxMIx4CVWAM16GQac+/OZcnm1xBjMyhZAo= +github.com/libdns/rfc2136 v0.1.1 h1:GKh2r08xt4aYeGlXR9eFrJMfFKD5i9QHBOpT1FIww/U= +github.com/libdns/rfc2136 v0.1.1/go.mod h1:tgXWavE+5OiAfdKxBnuG8OBEwQFAu7uuiS3+laspAGs= +github.com/libdns/route53 v1.5.1 h1:dkdcc2CKY/EHBBzAKqE0Cko7MKR8uVJ3GvpzwKu/UKM= +github.com/libdns/route53 v1.5.1/go.mod h1:joT4hKmaTNKHEwb7GmZ65eoDz1whTu7KKYPS8ZqIh6Q= github.com/libdns/vultr v1.0.0 h1:W8B4+k2bm9ro3bZLSZV9hMOQI+uO6Svu+GmD+Olz7ZI= github.com/libdns/vultr v1.0.0/go.mod h1:8K1HJExcbeHS4YPkFHRZpqpXZzZ+DZAA0m0VikJgEqk= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= +github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= -github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= +github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= +github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mholt/acmez/v3 v3.0.1 h1:4PcjKjaySlgXK857aTfDuRbmnM5gb3Ruz3tvoSJAUp8= +github.com/mholt/acmez/v3 v3.0.1/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= -github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= +github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= +github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.66 h1:bnTOXOHjOqv/gcMuiVbN9o2ngRItvqE774dG9nq0Dzw= -github.com/minio/minio-go/v7 v7.0.66/go.mod h1:DHAgmyQEGdW3Cif0UooKOyrT3Vxs82zNdV6tkKhRtbs= -github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= -github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/minio/minio-go/v7 v7.0.84 h1:D1HVmAF8JF8Bpi6IU4V9vIEj+8pc+xU88EWMs2yed0E= +github.com/minio/minio-go/v7 v7.0.84/go.mod h1:57YXpvc5l3rjPdhqNrDsvVlY0qPI6UTk1bflAe+9doY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6 h1:TsF5Cl0Mj5JMvPOP2ySVq+CZoiPrTGwvNPbuQotuSAE= -github.com/netauth/netauth v0.6.2-0.20220831214440-1df568cd25d6/go.mod h1:4PEbISVqRCQaXaDAt289w3nK9UhoF8/ZOLy31Hbv7ds= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/netauth/netauth v0.6.2 h1:Gtx/Xxa6YUaGny+iVvWyp+FAmtLQ1IlbB2uWTZEpWxQ= +github.com/netauth/netauth v0.6.2/go.mod h1:4PEbISVqRCQaXaDAt289w3nK9UhoF8/ZOLy31Hbv7ds= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd h1:4yVpQ/+li28lQ/daYCWeDB08obRmjaoAw2qfFFaCQ40= github.com/netauth/protocol v0.0.0-20210918062754-7fee492ffcbd/go.mod h1:wpK5wqysOJU1w2OxgG65du8M7UqBkxzsNaJdjwiRqAs= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= -github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= -github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= -github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y= -github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ= -github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= -github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63 h1:J6qvD6rbmOil46orKqJaRPG+zTpoGlBTUdyv8ki63L0= github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= +github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= -github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= @@ -643,24 +644,28 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA= -github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= -github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= -github.com/vultr/govultr/v3 v3.6.1 h1:l1hAXGtqWVnobBpLRzW/BxoocYFI7SSBwQHw65ntLk4= -github.com/vultr/govultr/v3 v3.6.1/go.mod h1:rt9v2x114jZmmLAE/h5N5jnxTmsK9ewwS2oQZ0UBQzM= -github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI= -github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= +github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/vultr/govultr/v3 v3.14.1 h1:9BpyZgsWasuNoR39YVMcq44MSaF576Z4D+U3ro58eJQ= +github.com/vultr/govultr/v3 v3.14.1/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 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= +github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= -github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= +github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= +github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= +github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -670,32 +675,46 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= -go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= -go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= -go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= -go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= -go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= -go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= +go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -706,8 +725,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= -golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= +golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -735,8 +754,12 @@ golang.org/x/mod v0.4.1/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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -788,9 +811,17 @@ golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfS golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -816,8 +847,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -833,6 +864,11 @@ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -899,23 +935,34 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/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-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -925,19 +972,19 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -993,8 +1040,11 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= +golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= 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= @@ -1051,8 +1101,8 @@ google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.157.0 h1:ORAeqmbrrozeyw5NjnMxh7peHO0UzV4wWYSwZeCUb20= -google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g= +google.golang.org/api v0.218.0 h1:x6JCjEWeZ9PFCRe9z0FBrNwj7pB7DOAqT35N+IPnAUA= +google.golang.org/api v0.218.0/go.mod h1:5VGHBAkxrA/8EFjLVEYmMUJ8/8+gWWQ3s4cFH0FxG2M= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1060,8 +1110,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1163,8 +1211,11 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221018160656-63c7b68cfc55/go.mod h1:45EK0dUbEZ2NHjCeAd2LXmyjAgGUGrpGROgjhC3ADck= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 h1:91mG8dNTpkC0uChJUQ9zCiRqx3GEEFOWaRZ0mI6Oj2I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1200,8 +1251,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= -google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -1218,10 +1269,12 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -1229,6 +1282,7 @@ gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3M gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= @@ -1242,24 +1296,28 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019年2月3日/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020年1月3日/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020年1月4日/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= -lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= -modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= -modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0= -modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI= -modernc.org/libc v1.40.6 h1:141JHq3SjhOOCjECBgD4K8VgTFOy19CnHwroC08DAig= -modernc.org/libc v1.40.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= -modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= -modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= -modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= -modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= -modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= -modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= -modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= -modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0= +modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/ccgo/v4 v4.23.13 h1:PFiaemQwE/jdwi8XEHyEV+qYWoIuikLP3T4rvDeJb00= +modernc.org/ccgo/v4 v4.23.13/go.mod h1:vdN4h2WR5aEoNondUx26K7G8X+nuBscYnAEWSRmN2/0= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.6.1 h1:+Qf6xdG8l7B27TQ8D8lw/iFMUj1RXRBOuMUWziJOsk8= +modernc.org/gc/v2 v2.6.1/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/libc v1.61.9 h1:PLSBXVkifXGELtJ5BOnBUyAHr7lsatNwFU/RRo4kfJM= +modernc.org/libc v1.61.9/go.mod h1:61xrnzk/aR8gr5bR7Uj/lLFLuXu2/zMpIjcry63Eumk= +modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU= +modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg= +modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI= +modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU= +modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= +modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= +modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= +modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= +modernc.org/sqlite v1.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g= +modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE= +modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= +modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/internal/auth/sasl.go b/internal/auth/sasl.go index d34ba168..00d25ffa 100644 --- a/internal/auth/sasl.go +++ b/internal/auth/sasl.go @@ -29,6 +29,7 @@ import ( modconfig "github.com/foxcpp/maddy/framework/config/module" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/internal/auth/sasllogin" "github.com/foxcpp/maddy/internal/authz" ) @@ -48,6 +49,7 @@ var ( type SASLAuth struct { Log log.Logger OnlyFirstID bool + EnableLogin bool AuthMap module.Table AuthNormalize authz.NormalizeFunc @@ -59,7 +61,10 @@ func (s *SASLAuth) SASLMechanisms() []string { var mechs []string if len(s.Plain) != 0 { - mechs = append(mechs, sasl.Plain, sasl.Login) + mechs = append(mechs, sasl.Plain) + if s.OnlyFirstID { + mechs = append(mechs, sasl.Login) + } } return mechs @@ -114,8 +119,16 @@ func (s *SASLAuth) AuthPlain(username, password string) error { return fmt.Errorf("no auth. provider accepted creds, last err: %w", lastErr) } +type ContextData struct { + // Authentication username. May be different from identity. + Username string + + // Password used for password-based mechanisms. + Password string +} + // CreateSASL creates the sasl.Server instance for the corresponding mechanism. -func (s *SASLAuth) CreateSASL(mech string, remoteAddr net.Addr, successCb func(identity string) error) sasl.Server { +func (s *SASLAuth) CreateSASL(mech string, remoteAddr net.Addr, successCb func(identity string, data ContextData) error) sasl.Server { switch mech { case sasl.Plain: return sasl.NewPlainServer(func(identity, username, password string) error { @@ -132,17 +145,27 @@ func (s *SASLAuth) CreateSASL(mech string, remoteAddr net.Addr, successCb func(i return ErrInvalidAuthCred } - return successCb(identity) + return successCb(identity, ContextData{ + Username: username, + Password: password, + }) }) case sasl.Login: - return sasl.NewLoginServer(func(username, password string) error { + if !s.EnableLogin { + return FailingSASLServ{Err: ErrUnsupportedMech} + } + + return sasllogin.NewLoginServer(func(username, password string) error { err := s.AuthPlain(username, password) if err != nil { s.Log.Error("authentication failed", err, "username", username, "src_ip", remoteAddr) return ErrInvalidAuthCred } - return successCb(username) + return successCb(username, ContextData{ + Username: username, + Password: password, + }) }) } return FailingSASLServ{Err: ErrUnsupportedMech} diff --git a/internal/auth/sasl_test.go b/internal/auth/sasl_test.go index fcc193aa..a59cfc79 100644 --- a/internal/auth/sasl_test.go +++ b/internal/auth/sasl_test.go @@ -52,7 +52,7 @@ func TestCreateSASL(t *testing.T) { } t.Run("XWHATEVER", func(t *testing.T) { - srv := a.CreateSASL("XWHATEVER", &net.TCPAddr{}, func(string) error { return nil }) + srv := a.CreateSASL("XWHATEVER", &net.TCPAddr{}, func(string, ContextData) error { return nil }) _, _, err := srv.Next([]byte("")) if err == nil { t.Error("No error for XWHATEVER use") @@ -60,7 +60,7 @@ func TestCreateSASL(t *testing.T) { }) t.Run("PLAIN", func(t *testing.T) { - srv := a.CreateSASL("PLAIN", &net.TCPAddr{}, func(id string) error { + srv := a.CreateSASL("PLAIN", &net.TCPAddr{}, func(id string, data ContextData) error { if id != "user1" { t.Fatal("Wrong auth. identities passed to callback:", id) } @@ -74,7 +74,7 @@ func TestCreateSASL(t *testing.T) { }) t.Run("PLAIN with authorization identity", func(t *testing.T) { - srv := a.CreateSASL("PLAIN", &net.TCPAddr{}, func(id string) error { + srv := a.CreateSASL("PLAIN", &net.TCPAddr{}, func(id string, data ContextData) error { if id != "user1" { t.Fatal("Wrong authorization identity passed:", id) } diff --git a/internal/auth/sasllogin/sasllogin.go b/internal/auth/sasllogin/sasllogin.go new file mode 100644 index 00000000..fac50260 --- /dev/null +++ b/internal/auth/sasllogin/sasllogin.go @@ -0,0 +1,54 @@ +package sasllogin + +import "github.com/emersion/go-sasl" + +// Copy-pasted from old emersion/go-sasl version + +// Authenticates users with an username and a password. +type LoginAuthenticator func(username, password string) error +type loginState int + +const ( + loginNotStarted loginState = iota + loginWaitingUsername + loginWaitingPassword +) + +type loginServer struct { + state loginState + username, password string + authenticate LoginAuthenticator +} + +// A server implementation of the LOGIN authentication mechanism, as described +// in https://tools.ietf.org/html/draft-murchison-sasl-login-00. +// +// LOGIN is obsolete and should only be enabled for legacy clients that cannot +// be updated to use PLAIN. +func NewLoginServer(authenticator LoginAuthenticator) sasl.Server { + return &loginServer{authenticate: authenticator} +} + +func (a *loginServer) Next(response []byte) (challenge []byte, done bool, err error) { + switch a.state { + case loginNotStarted: + // Check for initial response field, as per RFC4422 section 3 + if response == nil { + challenge = []byte("Username:") + break + } + a.state++ + fallthrough + case loginWaitingUsername: + a.username = string(response) + challenge = []byte("Password:") + case loginWaitingPassword: + a.password = string(response) + err = a.authenticate(a.username, a.password) + done = true + default: + err = sasl.ErrUnexpectedClientResponse + } + a.state++ + return +} diff --git a/internal/endpoint/dovecot_sasld/dovecot_sasl.go b/internal/endpoint/dovecot_sasld/dovecot_sasl.go index df8c3d1a..b4067bb5 100644 --- a/internal/endpoint/dovecot_sasld/dovecot_sasl.go +++ b/internal/endpoint/dovecot_sasld/dovecot_sasl.go @@ -72,6 +72,7 @@ func (endp *Endpoint) Init(cfg *config.Map) error { cfg.Callback("auth", func(m *config.Map, node config.Node) error { return endp.saslAuth.AddProvider(m, node) }) + cfg.Bool("sasl_login", false, false, &endp.saslAuth.EnableLogin) config.EnumMapped(cfg, "auth_map_normalize", true, false, authz.NormalizeFuncs, authz.NormalizeAuto, &endp.authNormalize) modconfig.Table(cfg, "auth_map", true, false, nil, &endp.authMap) @@ -92,7 +93,7 @@ func (endp *Endpoint) Init(cfg *config.Map) error { remoteAddr = &net.TCPAddr{IP: req.RemoteIP, Port: int(req.RemotePort)} } - return endp.saslAuth.CreateSASL(mech, remoteAddr, func(_ string) error { return nil }) + return endp.saslAuth.CreateSASL(mech, remoteAddr, func(_ string, _ auth.ContextData) error { return nil }) }) } diff --git a/internal/endpoint/imap/imap.go b/internal/endpoint/imap/imap.go index d047e882..1be76ec8 100644 --- a/internal/endpoint/imap/imap.go +++ b/internal/endpoint/imap/imap.go @@ -90,6 +90,7 @@ func (endp *Endpoint) Init(cfg *config.Map) error { cfg.Callback("auth", func(m *config.Map, node config.Node) error { return endp.saslAuth.AddProvider(m, node) }) + cfg.Bool("sasl_login", false, false, &endp.saslAuth.EnableLogin) cfg.Custom("storage", false, true, nil, modconfig.StorageDirective, &endp.Store) cfg.Custom("tls", true, true, nil, tls2.TLSDirective, &endp.tlsConfig) cfg.Custom("proxy_protocol", false, false, nil, proxy_protocol.ProxyProtocolDirective, &endp.proxyProtocol) @@ -144,7 +145,7 @@ func (endp *Endpoint) Init(cfg *config.Map) error { for _, mech := range endp.saslAuth.SASLMechanisms() { mech := mech endp.serv.EnableAuth(mech, func(c imapserver.Conn) sasl.Server { - return endp.saslAuth.CreateSASL(mech, c.Info().RemoteAddr, func(identity string) error { + return endp.saslAuth.CreateSASL(mech, c.Info().RemoteAddr, func(identity string, data auth.ContextData) error { return endp.openAccount(c, identity) }) }) diff --git a/internal/endpoint/smtp/session.go b/internal/endpoint/smtp/session.go index 6f2c68e5..5dfec134 100644 --- a/internal/endpoint/smtp/session.go +++ b/internal/endpoint/smtp/session.go @@ -31,6 +31,7 @@ import ( "sync" "github.com/emersion/go-message/textproto" + "github.com/emersion/go-sasl" "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/address" "github.com/foxcpp/maddy/framework/buffer" @@ -38,6 +39,7 @@ import ( "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/internal/auth" ) func limitReader(r io.Reader, n int64, err error) *limitedReader { @@ -96,6 +98,23 @@ type Session struct { log log.Logger } +func (s *Session) AuthMechanisms() []string { + return s.endp.saslAuth.SASLMechanisms() +} + +func (s *Session) Auth(mech string) (sasl.Server, error) { + // Executed before authentication and session initialization. + if err := s.endp.pipeline.RunEarlyChecks(s.sessionCtx, &s.connState); err != nil { + return nil, s.endp.wrapErr("", true, "AUTH", err) + } + + return s.endp.saslAuth.CreateSASL(mech, s.connState.RemoteAddr, func(identity string, data auth.ContextData) error { + s.connState.AuthUser = identity + s.connState.AuthPassword = data.Password + return nil + }), nil +} + func (s *Session) Reset() { s.msgLock.Lock() defer s.msgLock.Unlock() @@ -145,10 +164,6 @@ func (s *Session) cleanSession() { } func (s *Session) AuthPlain(username, password string) error { - if s.endp.serv.AuthDisabled { - return smtp.ErrAuthUnsupported - } - // Executed before authentication and session initialization. if err := s.endp.pipeline.RunEarlyChecks(context.TODO(), &s.connState); err != nil { return s.endp.wrapErr("", true, "AUTH", err) diff --git a/internal/endpoint/smtp/smtp.go b/internal/endpoint/smtp/smtp.go index abc01d94..69023cb5 100644 --- a/internal/endpoint/smtp/smtp.go +++ b/internal/endpoint/smtp/smtp.go @@ -33,7 +33,6 @@ import ( "sync/atomic" "time" - "github.com/emersion/go-sasl" "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" @@ -250,6 +249,7 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { cfg.Callback("auth", func(m *config.Map, node config.Node) error { return endp.saslAuth.AddProvider(m, node) }) + cfg.Bool("sasl_login", false, false, &endp.saslAuth.EnableLogin) cfg.String("hostname", true, true, "", &hostname) config.EnumMapped(cfg, "auth_map_normalize", true, false, authz.NormalizeFuncs, authz.NormalizeAuto, &endp.authNormalize) @@ -305,7 +305,6 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { endp.pipeline.Log = log.Logger{Name: "smtp/pipeline", Debug: endp.Log.Debug} endp.pipeline.FirstPipeline = true - endp.serv.AuthDisabled = len(endp.saslAuth.SASLMechanisms()) == 0 if endp.submission { endp.authAlwaysRequired = true if len(endp.saslAuth.SASLMechanisms()) == 0 { @@ -314,22 +313,6 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { } endp.saslAuth.AuthNormalize = endp.authNormalize endp.saslAuth.AuthMap = endp.authMap - for _, mech := range endp.saslAuth.SASLMechanisms() { - // The code below lacks handling to set AuthPassword. Don't - // override sasl.Plain handler so Login() will be called as usual. - if mech == sasl.Plain { - continue - } - - mech := mech - - endp.serv.EnableAuth(mech, func(c *smtp.Conn) sasl.Server { - return endp.saslAuth.CreateSASL(mech, c.Conn().RemoteAddr(), func(id string) error { - c.Session().(*Session).connState.AuthUser = id - return nil - }) - }) - } if ioDebug { endp.serv.Debug = endp.Log.DebugWriter() diff --git a/internal/smtpconn/smtpconn.go b/internal/smtpconn/smtpconn.go index d7b42459..0c73ed76 100644 --- a/internal/smtpconn/smtpconn.go +++ b/internal/smtpconn/smtpconn.go @@ -281,6 +281,18 @@ func (c *C) attemptConnect(ctx context.Context, lmtp bool, endp config.Endpoint, return false, nil, nil, TLSError{err} } + // Re-do HELO using our hostname instead of localhost. + if err := cl.Hello(c.Hostname); err != nil { + cl.Close() + + var tlsErr *tls.CertificateVerificationError + if errors.As(err, &tlsErr) { + return false, nil, nil, TLSError{Err: tlsErr} + } + + return false, nil, nil, err + } + return true, cl, conn, nil } diff --git a/internal/target/remote/connect.go b/internal/target/remote/connect.go index 2290d137..f9d317ef 100644 --- a/internal/target/remote/connect.go +++ b/internal/target/remote/connect.go @@ -21,7 +21,6 @@ package remote import ( "context" "crypto/tls" - "crypto/x509" "errors" "net" "runtime/trace" @@ -72,19 +71,8 @@ func (c *mxConn) Close() error { } func isVerifyError(err error) bool { - if errors.As(err, &x509.UnknownAuthorityError{}) { - return true - } - if errors.As(err, &x509.HostnameError{}) { - return true - } - if errors.As(err, &x509.ConstraintViolationError{}) { - return true - } - if errors.As(err, &x509.CertificateInvalidError{}) { - return true - } - return false + var e *tls.CertificateVerificationError + return errors.As(err, &e) } // connect attempts to connect to the MX, first trying STARTTLS with X.509 @@ -117,6 +105,16 @@ retry: starttlsOk, _ := conn.Client().Extension("STARTTLS") if starttlsOk && tlsCfg != nil { if err := conn.Client().StartTLS(tlsCfg); err != nil { + // Here we just issue STARTTLS command. If it fails for some + // reason - this is either a connection problem or server actively + // rejecting STARTTLS (despite advertising STARTTLS). + // We err on the caution side here and do not perform any fallbacks. + conn.DirectClose() + return module.TLSNone, nil, err + } + + // TLS handshake is deferred to here, this is where we check errors and allow fallback. + if err := conn.Client().Hello(rd.rt.hostname); err != nil { tlsErr = err // Attempt TLS without authentication. It is still better than diff --git a/internal/testutils/smtp_server.go b/internal/testutils/smtp_server.go index 6fb105a6..9af52c4f 100644 --- a/internal/testutils/smtp_server.go +++ b/internal/testutils/smtp_server.go @@ -21,6 +21,7 @@ package testutils import ( "crypto/tls" "crypto/x509" + "fmt" "io" "net" "reflect" @@ -29,6 +30,7 @@ import ( "testing" "time" + "github.com/emersion/go-sasl" "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/exterrors" ) @@ -107,6 +109,24 @@ type session struct { msg *SMTPMessage } +func (s *session) AuthMechanisms() []string { + return []string{sasl.Plain} +} + +func (s *session) Auth(mech string) (sasl.Server, error) { + if mech != sasl.Plain { + return nil, fmt.Errorf("mechanisms other than plain are unsupported") + } + return sasl.NewPlainServer(func(identity, username, password string) error { + if s.backend.AuthErr != nil { + return s.backend.AuthErr + } + s.user = username + s.password = password + return nil + }), nil +} + func (s *session) Reset() { s.msg = &SMTPMessage{} } @@ -116,15 +136,6 @@ func (s *session) Logout() error { return nil } -func (s *session) AuthPlain(username, password string) error { - if s.backend.AuthErr != nil { - return s.backend.AuthErr - } - s.user = username - s.password = password - return nil -} - func (s *session) Mail(from string, opts *smtp.MailOptions) error { s.backend.MailFromCounter++ @@ -188,10 +199,6 @@ func (s *session) LMTPData(r io.Reader, status smtp.StatusCollector) error { type SMTPServerConfigureFunc func(*smtp.Server) -var AuthDisabled = func(s *smtp.Server) { - s.AuthDisabled = true -} - func SMTPServer(t *testing.T, addr string, fn ...SMTPServerConfigureFunc) (*SMTPBackend, *smtp.Server) { t.Helper() diff --git a/internal/tls/acme/acme.go b/internal/tls/acme/acme.go index 39d6213d..a09c3e09 100644 --- a/internal/tls/acme/acme.go +++ b/internal/tls/acme/acme.go @@ -48,7 +48,7 @@ func (l *Loader) Init(cfg *config.Map) error { agreed bool challenge string overrideDomain string - provider certmagic.ACMEDNSProvider + provider certmagic.DNSProvider ) cfg.Bool("debug", true, false, &l.log.Debug) cfg.String("hostname", true, true, "", &hostname) @@ -69,7 +69,7 @@ func (l *Loader) Init(cfg *config.Map) error { cfg.Custom("dns", false, false, func() (interface{}, error) { return nil, nil }, func(m *config.Map, node config.Node) (interface{}, error) { - var p certmagic.ACMEDNSProvider + var p certmagic.DNSProvider err := modconfig.ModuleFromNode("libdns", node.Args, node, m.Globals, &p) return p, err }, &provider) @@ -108,8 +108,10 @@ func (l *Loader) Init(cfg *config.Map) error { return fmt.Errorf("tls.loader.acme: dns-01 challenge requires a configured DNS provider") } issuer.DNS01Solver = &certmagic.DNS01Solver{ - DNSProvider: provider, - OverrideDomain: overrideDomain, + DNSManager: certmagic.DNSManager{ + DNSProvider: provider, + OverrideDomain: overrideDomain, + }, } default: return fmt.Errorf("tls.loader.acme: challenge not supported") diff --git a/tests/basic_test.go b/tests/basic_test.go index ac7af2a2..80cf1aef 100644 --- a/tests/basic_test.go +++ b/tests/basic_test.go @@ -56,7 +56,8 @@ func TestBasic(tt *testing.T) { conn.ExpectPattern("250-ENHANCEDSTATUSCODES") conn.ExpectPattern("250-CHUNKING") conn.ExpectPattern("250-SMTPUTF8") - conn.ExpectPattern("250 SIZE *") + conn.ExpectPattern("250-SIZE *") + conn.ExpectPattern("250 LIMITS RCPTMAX=20000") conn.Writeln("QUIT") conn.ExpectPattern("221 *") } From 44a23f21cead3b513591ee98d22a27b1c4be4f77 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月24日 21:55:52 +0300 Subject: [PATCH 076/171] ci: Refactor CI workflows --- .github/workflows/{cicd.yml => release.yml} | 44 +-------------- .github/workflows/test.yml | 61 +++++++++++++++++++++ 2 files changed, 62 insertions(+), 43 deletions(-) rename .github/workflows/{cicd.yml => release.yml} (73%) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/cicd.yml b/.github/workflows/release.yml similarity index 73% rename from .github/workflows/cicd.yml rename to .github/workflows/release.yml index 738f672b..28ed9873 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/release.yml @@ -1,52 +1,10 @@ -name: "Testing and release preparation" +name: "Prepare release artifacts" on: push: - branches: [ master, dev ] tags: [ "v*" ] - pull_request: - branches: [ master, dev ] jobs: - build-and-test: - name: "Build and test" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: "Install libpam" - run: | - sudo apt-get update - sudo apt-get install -y libpam-dev - - uses: actions/cache@v2 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: ${{ runner.os }}-go- - - uses: actions/setup-go@v2 - with: - go-version: 1.19 - - name: "Verify build.sh" - run: | - ./build.sh - ./build.sh --destdir destdir/ install - find destdir/ - - name: "Unit & module tests" - run: | - go test ./... -coverprofile=coverage.out -covermode=atomic - - name: "Integration tests" - run: | - cd tests/ - ./run.sh - - uses: codecov/codecov-action@v2 - with: - files: ./coverage.out - flags: unit - - uses: codecov/codecov-action@v2 - with: - files: ./tests/coverage.out - flags: integration artifact-builder: name: "Prepare release artifacts" needs: build-and-test diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..7d6256d7 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,61 @@ +name: "Testing" + +on: + push: + branches: [ master, dev ] + tags: [ "v*" ] + pull_request: + branches: [ master, dev ] + +jobs: + golangci: + name: lint + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + - uses: golangci/golangci-lint-action@v6 + with: + version: v1.60 + buildsh: + name: "Verify build.sh" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + - name: "Install libpam" + run: | + sudo apt-get update + sudo apt-get install -y libpam-dev + - name: "Verify build.sh" + run: | + ./build.sh + ./build.sh --destdir destdir/ install + find destdir/ + test: + name: "Build and test" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + - name: "Unit & module tests" + run: | + go test ./... -coverprofile=coverage.out -covermode=atomic + - name: "Integration tests" + run: | + cd tests/ + ./run.sh + - uses: codecov/codecov-action@v2 + with: + files: ./coverage.out + flags: unit + - uses: codecov/codecov-action@v2 + with: + files: ./tests/coverage.out + flags: integration From c2cb732eef29e94b4e9d031c3a7600cb38910f62 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月24日 21:58:11 +0300 Subject: [PATCH 077/171] Fix-up --- .github/workflows/test.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7d6256d7..8f208b71 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,7 +9,7 @@ on: jobs: golangci: - name: lint + name: Lint runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 @@ -44,6 +44,10 @@ jobs: - uses: actions/setup-go@v5 with: go-version-file: 'go.mod' + - name: "Install libpam" + run: | + sudo apt-get update + sudo apt-get install -y libpam-dev - name: "Unit & module tests" run: | go test ./... -coverprofile=coverage.out -covermode=atomic From 4e1cb7c5c1b90e3178299c7bcbe98699fda29064 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月24日 22:09:17 +0300 Subject: [PATCH 078/171] build.sh: Fix-up config overwrite check to work when installing not to / --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 3dc76de2..f0a15922 100755 --- a/build.sh +++ b/build.sh @@ -3,7 +3,6 @@ destdir=/ builddir="$PWD/build" prefix=/usr/local -configdir="${destdir}etc/maddy" version= static=0 if [ "${GOFLAGS}" = "" ]; then @@ -77,6 +76,7 @@ while :; do shift done +configdir="${destdir}etc/maddy" if [ "$version" = "" ]; then version=unknown From 7ad6925c0d67e6a768fb52c9d8b7b483c7e3dcb7 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月24日 22:15:59 +0300 Subject: [PATCH 079/171] ci: Increase lint timeout --- .github/workflows/test.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8f208b71..6cc0f56f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,6 +7,11 @@ on: pull_request: branches: [ master, dev ] +permissions: + contents: read + pull-requests: read + checks: write + jobs: golangci: name: Lint @@ -19,6 +24,7 @@ jobs: - uses: golangci/golangci-lint-action@v6 with: version: v1.60 + args: "--timeout=30m" buildsh: name: "Verify build.sh" runs-on: ubuntu-latest From f9d49170af0b3b69bc02ba4d07d7a880317f3d23 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月24日 22:18:06 +0300 Subject: [PATCH 080/171] ci: Fixup --- .github/workflows/test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6cc0f56f..32c02e54 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,6 +21,10 @@ jobs: - uses: actions/setup-go@v5 with: go-version-file: 'go.mod' + - name: "Install libpam" + run: | + sudo apt-get update + sudo apt-get install -y libpam-dev - uses: golangci/golangci-lint-action@v6 with: version: v1.60 From 120c5c9ea2e836b2844e029b35cb32626917d4e1 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月24日 23:12:45 +0300 Subject: [PATCH 081/171] endpoint: Clean-up dead auth_map code --- .../endpoint/dovecot_sasld/dovecot_sasl.go | 9 ++---- internal/endpoint/imap/imap.go | 29 ++----------------- internal/endpoint/smtp/session.go | 2 +- internal/endpoint/smtp/smtp.go | 29 ++----------------- 4 files changed, 7 insertions(+), 62 deletions(-) diff --git a/internal/endpoint/dovecot_sasld/dovecot_sasl.go b/internal/endpoint/dovecot_sasld/dovecot_sasl.go index b4067bb5..f1184e86 100644 --- a/internal/endpoint/dovecot_sasld/dovecot_sasl.go +++ b/internal/endpoint/dovecot_sasld/dovecot_sasl.go @@ -44,9 +44,6 @@ type Endpoint struct { listenersWg sync.WaitGroup - authNormalize authz.NormalizeFunc - authMap module.Table - srv *dovecotsasl.Server } @@ -74,8 +71,8 @@ func (endp *Endpoint) Init(cfg *config.Map) error { }) cfg.Bool("sasl_login", false, false, &endp.saslAuth.EnableLogin) config.EnumMapped(cfg, "auth_map_normalize", true, false, authz.NormalizeFuncs, authz.NormalizeAuto, - &endp.authNormalize) - modconfig.Table(cfg, "auth_map", true, false, nil, &endp.authMap) + &endp.saslAuth.AuthNormalize) + modconfig.Table(cfg, "auth_map", true, false, nil, &endp.saslAuth.AuthMap) if _, err := cfg.Process(); err != nil { return err } @@ -83,8 +80,6 @@ func (endp *Endpoint) Init(cfg *config.Map) error { endp.srv = dovecotsasl.NewServer() endp.srv.Log = stdlog.New(endp.log, "", 0) - endp.saslAuth.AuthMap = endp.authMap - endp.saslAuth.AuthNormalize = endp.authNormalize for _, mech := range endp.saslAuth.SASLMechanisms() { mech := mech endp.srv.AddMechanism(mech, mechInfo[mech], func(req *dovecotsasl.AuthReq) sasl.Server { diff --git a/internal/endpoint/imap/imap.go b/internal/endpoint/imap/imap.go index 1be76ec8..3525b8ed 100644 --- a/internal/endpoint/imap/imap.go +++ b/internal/endpoint/imap/imap.go @@ -62,8 +62,6 @@ type Endpoint struct { storageNormalize authz.NormalizeFunc storageMap module.Table - authNormalize authz.NormalizeFunc - authMap module.Table Log log.Logger } @@ -102,8 +100,8 @@ func (endp *Endpoint) Init(cfg *config.Map) error { &endp.storageNormalize) modconfig.Table(cfg, "storage_map", false, false, nil, &endp.storageMap) config.EnumMapped(cfg, "auth_map_normalize", true, false, authz.NormalizeFuncs, authz.NormalizeAuto, - &endp.authNormalize) - modconfig.Table(cfg, "auth_map", true, false, nil, &endp.authMap) + &endp.saslAuth.AuthNormalize) + modconfig.Table(cfg, "auth_map", true, false, nil, &endp.saslAuth.AuthMap) if _, err := cfg.Process(); err != nil { return err } @@ -140,8 +138,6 @@ func (endp *Endpoint) Init(cfg *config.Map) error { return err } - endp.saslAuth.AuthNormalize = endp.authNormalize - endp.saslAuth.AuthMap = endp.authMap for _, mech := range endp.saslAuth.SASLMechanisms() { mech := mech endp.serv.EnableAuth(mech, func(c imapserver.Conn) sasl.Server { @@ -217,27 +213,6 @@ func (endp *Endpoint) Close() error { return nil } -func (endp *Endpoint) usernameForAuth(ctx context.Context, saslUsername string) (string, error) { - saslUsername, err := endp.authNormalize(saslUsername) - if err != nil { - return "", err - } - - if endp.authMap == nil { - return saslUsername, nil - } - - mapped, ok, err := endp.authMap.Lookup(ctx, saslUsername) - if err != nil { - return "", err - } - if !ok { - return "", imapbackend.ErrInvalidCredentials - } - - return mapped, nil -} - func (endp *Endpoint) usernameForStorage(ctx context.Context, saslUsername string) (string, error) { saslUsername, err := endp.storageNormalize(saslUsername) if err != nil { diff --git a/internal/endpoint/smtp/session.go b/internal/endpoint/smtp/session.go index 5dfec134..0eb6427f 100644 --- a/internal/endpoint/smtp/session.go +++ b/internal/endpoint/smtp/session.go @@ -435,7 +435,7 @@ func (s *Session) Logout() error { } func (s *Session) prepareBody(r io.Reader) (textproto.Header, buffer.Buffer, error) { - limitr := limitReader(r, int64(s.endp.maxHeaderBytes), &exterrors.SMTPError{ + limitr := limitReader(r, s.endp.maxHeaderBytes, &exterrors.SMTPError{ Code: 552, EnhancedCode: exterrors.EnhancedCode{5, 3, 4}, Message: "Message header size exceeds limit", diff --git a/internal/endpoint/smtp/smtp.go b/internal/endpoint/smtp/smtp.go index 69023cb5..28ba85e2 100644 --- a/internal/endpoint/smtp/smtp.go +++ b/internal/endpoint/smtp/smtp.go @@ -252,8 +252,8 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { cfg.Bool("sasl_login", false, false, &endp.saslAuth.EnableLogin) cfg.String("hostname", true, true, "", &hostname) config.EnumMapped(cfg, "auth_map_normalize", true, false, authz.NormalizeFuncs, authz.NormalizeAuto, - &endp.authNormalize) - modconfig.Table(cfg, "auth_map", true, false, nil, &endp.authMap) + &endp.saslAuth.AuthNormalize) + modconfig.Table(cfg, "auth_map", true, false, nil, &endp.saslAuth.AuthMap) cfg.Duration("write_timeout", false, false, 1*time.Minute, &endp.serv.WriteTimeout) cfg.Duration("read_timeout", false, false, 10*time.Minute, &endp.serv.ReadTimeout) cfg.DataSize("max_message_size", false, false, 32*1024*1024, &endp.serv.MaxMessageBytes) @@ -358,31 +358,6 @@ func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { return nil } -func (endp *Endpoint) usernameForAuth(ctx context.Context, saslUsername string) (string, error) { - saslUsername, err := endp.authNormalize(saslUsername) - if err != nil { - return "", err - } - - if endp.authMap == nil { - return saslUsername, nil - } - - mapped, ok, err := endp.authMap.Lookup(ctx, saslUsername) - if err != nil { - return "", err - } - if !ok { - return "", &smtp.SMTPError{ - Code: 535, - EnhancedCode: smtp.EnhancedCode{5, 7, 8}, - Message: "Invalid credentials", - } - } - - return mapped, nil -} - func (endp *Endpoint) NewSession(conn *smtp.Conn) (smtp.Session, error) { sess := endp.newSession(conn) From dbc030c267540c2b5d44e08c452c6bdb7dc8a300 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月24日 23:34:09 +0300 Subject: [PATCH 082/171] Clean-up lint warnings --- framework/buffer/file.go | 2 +- framework/cfgparser/imports.go | 2 +- framework/cfgparser/parse_test.go | 1 - framework/config/tls/server.go | 4 ---- framework/log/log.go | 7 +++++-- internal/check/dnsbl/dnsbl.go | 1 - internal/endpoint/dovecot_sasld/dovecot_sasl.go | 1 - internal/endpoint/imap/imap.go | 2 -- internal/endpoint/openmetrics/om.go | 1 - internal/endpoint/smtp/smtp.go | 4 ---- internal/endpoint/smtp/smtp_test.go | 1 - internal/msgpipeline/check_runner.go | 2 -- internal/msgpipeline/config_test.go | 1 - internal/smtpconn/smtpconn_test.go | 2 -- internal/target/remote/remote.go | 2 -- internal/updatepipe/unix_pipe.go | 3 ++- maddy.go | 1 - 17 files changed, 9 insertions(+), 28 deletions(-) diff --git a/framework/buffer/file.go b/framework/buffer/file.go index dc2b7305..00259849 100644 --- a/framework/buffer/file.go +++ b/framework/buffer/file.go @@ -19,10 +19,10 @@ along with this program. If not, see . package buffer import ( + "crypto/rand" "encoding/hex" "fmt" "io" - "math/rand" "os" "path/filepath" ) diff --git a/framework/cfgparser/imports.go b/framework/cfgparser/imports.go index d8276d22..8078dd8d 100644 --- a/framework/cfgparser/imports.go +++ b/framework/cfgparser/imports.go @@ -89,7 +89,7 @@ func (ctx *parseContext) resolveImport(node Node, name string, expansionDepth in src, err = os.Open(file + ".conf") if err != nil { if os.IsNotExist(err) { - return nil, NodeErr(node, "unknown import: "+name) + return nil, NodeErr(node, "unknown import: %s", name) } return nil, err } diff --git a/framework/cfgparser/parse_test.go b/framework/cfgparser/parse_test.go index 9488e580..32929b9f 100644 --- a/framework/cfgparser/parse_test.go +++ b/framework/cfgparser/parse_test.go @@ -583,7 +583,6 @@ func TestRead(t *testing.T) { os.Setenv("TESTING_VARIABLE2", "ABC2 DEF2") for _, case_ := range cases { - case_ := case_ t.Run(case_.name, func(t *testing.T) { tree, err := Read(strings.NewReader(case_.cfg), "test") if !case_.fail && err != nil { diff --git a/framework/config/tls/server.go b/framework/config/tls/server.go index c23fdc32..4fe8e8d3 100644 --- a/framework/config/tls/server.go +++ b/framework/config/tls/server.go @@ -113,10 +113,6 @@ func readTLSBlock(globals map[string]interface{}, blockNode config.Node) (*TLSCo return nil, err } - if len(baseCfg.CipherSuites) != 0 { - baseCfg.PreferServerCipherSuites = true - } - baseCfg.MinVersion = tlsVersions[0] baseCfg.MaxVersion = tlsVersions[1] log.Debugf("tls: min version: %x, max version: %x", tlsVersions[0], tlsVersions[1]) diff --git a/framework/log/log.go b/framework/log/log.go index 98fb3a81..f9092a44 100644 --- a/framework/log/log.go +++ b/framework/log/log.go @@ -80,7 +80,8 @@ func (l Logger) Println(val ...interface{}) { // Msg writes an event log message in a machine-readable format (currently // JSON). -// name: msg\t{"key":"value","key2":"value2"} +// +// name: msg\t{"key":"value","key2":"value2"} // // Key-value pairs are built from fields slice which should contain key strings // followed by corresponding values. That is, for example, []interface{"key", @@ -102,7 +103,9 @@ func (l Logger) Msg(msg string, fields ...interface{}) { // JSON) containing information about the error. If err does have a Fields // method that returns map[string]interface{}, its result will be added to the // message. -// name: msg\t{"key":"value","key2":"value2"} +// +// name: msg\t{"key":"value","key2":"value2"} +// // Additionally, values from fields will be added to it, as handled by // Logger.Msg. // diff --git a/internal/check/dnsbl/dnsbl.go b/internal/check/dnsbl/dnsbl.go index 76cc7e34..2c91c838 100644 --- a/internal/check/dnsbl/dnsbl.go +++ b/internal/check/dnsbl/dnsbl.go @@ -301,7 +301,6 @@ func (bl *DNSBL) checkLists(ctx context.Context, ip net.IP, ehlo, mailFrom strin ) for _, list := range bl.bls { - list := list eg.Go(func() error { err := bl.checkList(ctx, list, ip, ehlo, mailFrom) if err != nil { diff --git a/internal/endpoint/dovecot_sasld/dovecot_sasl.go b/internal/endpoint/dovecot_sasld/dovecot_sasl.go index f1184e86..77eedd0e 100644 --- a/internal/endpoint/dovecot_sasld/dovecot_sasl.go +++ b/internal/endpoint/dovecot_sasld/dovecot_sasl.go @@ -81,7 +81,6 @@ func (endp *Endpoint) Init(cfg *config.Map) error { endp.srv.Log = stdlog.New(endp.log, "", 0) for _, mech := range endp.saslAuth.SASLMechanisms() { - mech := mech endp.srv.AddMechanism(mech, mechInfo[mech], func(req *dovecotsasl.AuthReq) sasl.Server { var remoteAddr net.Addr if req.RemoteIP != nil && req.RemotePort != 0 { diff --git a/internal/endpoint/imap/imap.go b/internal/endpoint/imap/imap.go index 3525b8ed..191d93d2 100644 --- a/internal/endpoint/imap/imap.go +++ b/internal/endpoint/imap/imap.go @@ -139,7 +139,6 @@ func (endp *Endpoint) Init(cfg *config.Map) error { } for _, mech := range endp.saslAuth.SASLMechanisms() { - mech := mech endp.serv.EnableAuth(mech, func(c imapserver.Conn) sasl.Server { return endp.saslAuth.CreateSASL(mech, c.Info().RemoteAddr, func(identity string, data auth.ContextData) error { return endp.openAccount(c, identity) @@ -174,7 +173,6 @@ func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { endp.listeners = append(endp.listeners, l) endp.listenersWg.Add(1) - addr := addr go func() { if err := endp.serv.Serve(l); err != nil && !strings.HasSuffix(err.Error(), "use of closed network connection") { endp.Log.Printf("imap: failed to serve %s: %s", addr, err) diff --git a/internal/endpoint/openmetrics/om.go b/internal/endpoint/openmetrics/om.go index 3d362f97..874a333d 100644 --- a/internal/endpoint/openmetrics/om.go +++ b/internal/endpoint/openmetrics/om.go @@ -60,7 +60,6 @@ func (e *Endpoint) Init(cfg *config.Map) error { e.serv.Handler = e.mux for _, a := range e.addrs { - a := a endp, err := config.ParseEndpoint(a) if err != nil { return fmt.Errorf("%s: malformed endpoint: %v", modName, err) diff --git a/internal/endpoint/smtp/smtp.go b/internal/endpoint/smtp/smtp.go index 28ba85e2..004d92e5 100644 --- a/internal/endpoint/smtp/smtp.go +++ b/internal/endpoint/smtp/smtp.go @@ -24,7 +24,6 @@ import ( "crypto/tls" "fmt" "io" - "math/rand" "net" "os" "path/filepath" @@ -346,7 +345,6 @@ func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { endp.listeners = append(endp.listeners, l) endp.listenersWg.Add(1) - addr := addr go func() { if err := endp.serv.Serve(l); err != nil { endp.Log.Printf("failed to serve %s: %s", addr, err) @@ -431,6 +429,4 @@ func init() { module.RegisterEndpoint("smtp", New) module.RegisterEndpoint("submission", New) module.RegisterEndpoint("lmtp", New) - - rand.Seed(time.Now().UnixNano()) } diff --git a/internal/endpoint/smtp/smtp_test.go b/internal/endpoint/smtp/smtp_test.go index c295d7c8..fa46c279 100644 --- a/internal/endpoint/smtp/smtp_test.go +++ b/internal/endpoint/smtp/smtp_test.go @@ -583,7 +583,6 @@ func TestMain(m *testing.M) { flag.Parse() if *remoteSmtpPort == "random" { - rand.Seed(time.Now().UnixNano()) *remoteSmtpPort = strconv.Itoa(rand.Intn(65536-10000) + 10000) } diff --git a/internal/msgpipeline/check_runner.go b/internal/msgpipeline/check_runner.go index ba9c7823..f6df9525 100644 --- a/internal/msgpipeline/check_runner.go +++ b/internal/msgpipeline/check_runner.go @@ -125,7 +125,6 @@ func (cr *checkRunner) checkStates(ctx context.Context, checks []module.Check) ( if len(cr.checkedRcpts) != 0 { for _, rcpt := range cr.checkedRcpts { - rcpt := rcpt err := cr.runAndMergeResults(states, func(s module.CheckState) module.CheckResult { // Avoid calling CheckRcpt for the same recipient for the same check // multiple times, even if requested. @@ -176,7 +175,6 @@ func (cr *checkRunner) runAndMergeResults(states []module.CheckState, runner fun }{} for _, state := range states { - state := state data.wg.Add(1) go func() { defer func() { diff --git a/internal/msgpipeline/config_test.go b/internal/msgpipeline/config_test.go index b7ac666a..24d7e51f 100644 --- a/internal/msgpipeline/config_test.go +++ b/internal/msgpipeline/config_test.go @@ -224,7 +224,6 @@ func TestMsgPipelineCfg(t *testing.T) { } for _, case_ := range cases { - case_ := case_ t.Run(case_.name, func(t *testing.T) { cfg, _ := parser.Read(strings.NewReader(case_.str), "literal") parsed, err := parseMsgPipelineRootCfg(nil, cfg) diff --git a/internal/smtpconn/smtpconn_test.go b/internal/smtpconn/smtpconn_test.go index 6279add9..b8fd647e 100644 --- a/internal/smtpconn/smtpconn_test.go +++ b/internal/smtpconn/smtpconn_test.go @@ -24,7 +24,6 @@ import ( "os" "strconv" "testing" - "time" ) var testPort string @@ -34,7 +33,6 @@ func TestMain(m *testing.M) { flag.Parse() if *remoteSmtpPort == "random" { - rand.Seed(time.Now().UnixNano()) *remoteSmtpPort = strconv.Itoa(rand.Intn(65536-10000) + 10000) } diff --git a/internal/target/remote/remote.go b/internal/target/remote/remote.go index 3e661c4f..d4c42ed6 100644 --- a/internal/target/remote/remote.go +++ b/internal/target/remote/remote.go @@ -406,8 +406,6 @@ func (rd *remoteDelivery) BodyNonAtomic(ctx context.Context, c module.StatusColl var wg sync.WaitGroup for i, conn := range rd.connections { - i := i - conn := conn wg.Add(1) go func() { defer wg.Done() diff --git a/internal/updatepipe/unix_pipe.go b/internal/updatepipe/unix_pipe.go index d2e2ce80..a8249f90 100644 --- a/internal/updatepipe/unix_pipe.go +++ b/internal/updatepipe/unix_pipe.go @@ -34,7 +34,8 @@ import ( // Listen goroutine can be running. // // The socket is stream-oriented and consists of the following messages: -// SENDER_ID;JSON_SERIALIZED_INTERNAL_OBJECT\n +// +// SENDER_ID;JSON_SERIALIZED_INTERNAL_OBJECT\n // // And SENDER_ID is Process ID and UnixSockPipe address concated as a string. // It is used to deduplicate updates sent to Push and recevied via Listen. diff --git a/maddy.go b/maddy.go index 16aa0eb0..e6a05cf1 100644 --- a/maddy.go +++ b/maddy.go @@ -385,7 +385,6 @@ func RegisterModules(globals map[string]interface{}, nodes []config.Node) (endpo return nil, nil, err } - block := block module.RegisterInstance(inst, config.NewMap(globals, block)) for _, alias := range modAliases { if module.HasInstance(alias) { From d7dd6ef8451057db8ebc0fa504647a494ec0f8df Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月24日 23:44:50 +0300 Subject: [PATCH 083/171] Fix more linter warnings --- framework/cfgparser/parse.go | 15 +++++++------- framework/config/module/check_action.go | 21 +++++++++++--------- internal/auth/auth_test.go | 1 - internal/check/milter/milter_test.go | 2 +- internal/smtpconn/smtpconn.go | 1 - internal/target/remote/remote_test.go | 2 -- internal/target/smtp/smtp_downstream_test.go | 2 -- 7 files changed, 21 insertions(+), 23 deletions(-) diff --git a/framework/cfgparser/parse.go b/framework/cfgparser/parse.go index 5eabc3c0..aed01e3d 100644 --- a/framework/cfgparser/parse.go +++ b/framework/cfgparser/parse.go @@ -31,10 +31,10 @@ import ( // Node struct describes a parsed configurtion block or a simple directive. // -// name arg0 arg1 { -// children0 -// children1 -// } +// name arg0 arg1 { +// children0 +// children1 +// } type Node struct { // Name is the first string at node's line. Name string @@ -209,9 +209,10 @@ func (ctx *parseContext) parseAsMacro(node *Node) (macroName string, args []stri // // The lexer's cursor should point to the opening brace // name arg0 arg1 { #< this one -// c0 -// c1 -// } +// +// c0 +// c1 +// } // // To stay consistent with readNode after this function returns the lexer's cursor points // to the last token of the black (closing brace). diff --git a/framework/config/module/check_action.go b/framework/config/module/check_action.go index 5061674a..cac3278c 100644 --- a/framework/config/module/check_action.go +++ b/framework/config/module/check_action.go @@ -36,19 +36,22 @@ import ( // returns. It is intended to be used as follows: // // Add the configuration directive to allow user to specify the action: -// cfg.Custom("SOME_action", false, false, -// func() (interface{}, error) { -// return modconfig.FailAction{Quarantine: true}, nil -// }, modconfig.FailActionDirective, &yourModule.SOMEAction) +// +// cfg.Custom("SOME_action", false, false, +// func() (interface{}, error) { +// return modconfig.FailAction{Quarantine: true}, nil +// }, modconfig.FailActionDirective, &yourModule.SOMEAction) +// // return in func literal is the default value, you might want to adjust it. // // Call yourModule.SOMEAction.Apply on CheckResult containing only the // Reason field: -// func (yourModule YourModule) CheckConnection() module.CheckResult { -// return yourModule.SOMEAction.Apply(module.CheckResult{ -// Reason: ..., -// }) -// } +// +// func (yourModule YourModule) CheckConnection() module.CheckResult { +// return yourModule.SOMEAction.Apply(module.CheckResult{ +// Reason: ..., +// }) +// } type FailAction struct { Quarantine bool Reject bool diff --git a/internal/auth/auth_test.go b/internal/auth/auth_test.go index 61fcd364..65ffee4e 100644 --- a/internal/auth/auth_test.go +++ b/internal/auth/auth_test.go @@ -75,7 +75,6 @@ func TestCheckDomainAuth(t *testing.T) { } for _, case_ := range cases { - case_ := case_ t.Run(fmt.Sprintf("%+v", case_), func(t *testing.T) { loginName, allowed := CheckDomainAuth(case_.rawUsername, case_.perDomain, case_.allowedDomains) if case_.loginName != "" && !allowed { diff --git a/internal/check/milter/milter_test.go b/internal/check/milter/milter_test.go index d2ec19d3..97978517 100644 --- a/internal/check/milter/milter_test.go +++ b/internal/check/milter/milter_test.go @@ -58,4 +58,4 @@ func TestRejectInvalidEndpoints(t *testing.T) { return } } -} \ No newline at end of file +} diff --git a/internal/smtpconn/smtpconn.go b/internal/smtpconn/smtpconn.go index 0c73ed76..7f66bd23 100644 --- a/internal/smtpconn/smtpconn.go +++ b/internal/smtpconn/smtpconn.go @@ -530,7 +530,6 @@ func (c *C) Close() error { c.Log.DebugMsg("QUIT error", "reason", c.wrapClientErr(err, c.serverName)) } else if errors.As(err, &netErr) && (netErr.Timeout() || netErr.Err.Error() == "write: broken pipe" || netErr.Err.Error() == "read: connection reset") { - // The case for silently closed connections. c.Log.DebugMsg("QUIT error", "reason", c.wrapClientErr(err, c.serverName)) } else { diff --git a/internal/target/remote/remote_test.go b/internal/target/remote/remote_test.go index ae561582..4998e0c6 100644 --- a/internal/target/remote/remote_test.go +++ b/internal/target/remote/remote_test.go @@ -27,7 +27,6 @@ import ( "os" "strconv" "testing" - "time" "github.com/emersion/go-message/textproto" "github.com/emersion/go-smtp" @@ -1020,7 +1019,6 @@ func TestMain(m *testing.M) { flag.Parse() if *remoteSmtpPort == "random" { - rand.Seed(time.Now().UnixNano()) *remoteSmtpPort = strconv.Itoa(rand.Intn(65536-10000) + 10000) } diff --git a/internal/target/smtp/smtp_downstream_test.go b/internal/target/smtp/smtp_downstream_test.go index 93827e27..011ca04a 100644 --- a/internal/target/smtp/smtp_downstream_test.go +++ b/internal/target/smtp/smtp_downstream_test.go @@ -25,7 +25,6 @@ import ( "os" "strconv" "testing" - "time" "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/config" @@ -344,7 +343,6 @@ func TestMain(m *testing.M) { flag.Parse() if *remoteSmtpPort == "random" { - rand.Seed(time.Now().UnixNano()) *remoteSmtpPort = strconv.Itoa(rand.Intn(65536-10000) + 10000) } From 7f7903a645917df98890faecb7148f5751344a5f Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月24日 23:48:50 +0300 Subject: [PATCH 084/171] Fix more linter warnings --- internal/target/remote/dane_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/target/remote/dane_test.go b/internal/target/remote/dane_test.go index d8b6d5d2..470fbbbc 100644 --- a/internal/target/remote/dane_test.go +++ b/internal/target/remote/dane_test.go @@ -31,8 +31,9 @@ import ( ) // These certificates are related like this: -// Root A -> Intermediate A -> Leaf A -// Root B -> LeafB +// +// Root A -> Intermediate A -> Leaf A +// Root B -> LeafB var ( rootA = `-----BEGIN CERTIFICATE----- MIIBMDCB46ADAgECAhRDwag3n5CG90BEO87zEMAPejn6YTAFBgMrZXAwFjEUMBIG From 21329c8f6f406f0a00e0c0a5ff97546861f8dde0 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月25日 00:11:54 +0300 Subject: [PATCH 085/171] ci: Fix-up release pipeline --- .github/workflows/release.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 28ed9873..34fc3fe8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,6 @@ on: jobs: artifact-builder: name: "Prepare release artifacts" - needs: build-and-test if: github.ref_type == 'tag' runs-on: ubuntu-latest container: @@ -48,7 +47,6 @@ jobs: if-no-files-found: error docker-builder: name: "Build & push Docker image" - needs: build-and-test # Upload if: github.ref_type == 'tag' runs-on: ubuntu-latest steps: From aaa838dd79e74d8ac096161df5b18b98b1de52be Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月25日 00:49:15 +0300 Subject: [PATCH 086/171] ci: Upgrade release pipeline actions --- .github/workflows/release.yml | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 34fc3fe8..ca911b72 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,11 @@ on: push: tags: [ "v*" ] +permissions: + id-token: write + contents: read + attestations: write + jobs: artifact-builder: name: "Prepare release artifacts" @@ -45,33 +50,37 @@ jobs: name: maddy-binary.tar.zst path: '~/maddy-x86_64-linux-musl.tar.zst' if-no-files-found: error + - name: "Generate artifact attestation" + uses: actions/attest-build-provenance@v2 + with: + subject-path: '~/maddy-x86_64-linux-musl.tar.zst' docker-builder: name: "Build & push Docker image" if: github.ref_type == 'tag' runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: "Set up QEMU" uses: docker/setup-qemu-action@v1 with: platforms: arm64 - name: "Set up Docker Buildx" id: buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: "Login to Docker Hub" - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} - name: "Login to GitHub Container Registry" - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: registry: "ghcr.io" username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: "Generate container metadata" - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 id: meta with: images: | @@ -85,7 +94,8 @@ jobs: org.opencontainers.image.documentation=https://maddy.email/docker/ org.opencontainers.image.url=https://maddy.email - name: "Build and push" - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v6 + id: docker with: context: . platforms: linux/amd64,linux/arm64 @@ -93,3 +103,10 @@ jobs: push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + - name: "Generate container attestation" + uses: actions/attest-build-provenance@v2 + with: + subject-name: ghcr.io/foxcpp/maddy + subject-digest: ${{ steps.docker.outputs.digest }} + push-to-registry: true + From 96bd83316bb096788c154eb98d766b0e28737e1b Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月25日 00:51:05 +0300 Subject: [PATCH 087/171] ci: Fix-up release pipeline --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ca911b72..f908c63a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,13 +39,13 @@ jobs: tar c ./maddy-$ver-src | zstd> ~/maddy-src.tar.zst cd - - name: "Upload source tree" - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: maddy-src.tar.zst path: '~/maddy-src.tar.zst' if-no-files-found: error - name: "Upload binary tree" - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: maddy-binary.tar.zst path: '~/maddy-x86_64-linux-musl.tar.zst' From 3e5044ee8e0572acdd4147f84f788c22d2b06211 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月25日 01:07:46 +0300 Subject: [PATCH 088/171] build.sh: Remove -C from install Not available in Busybox when building under Alpine Linux --- build.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.sh b/build.sh index f0a15922..419dc1f6 100755 --- a/build.sh +++ b/build.sh @@ -163,14 +163,14 @@ install() { if command -v go>/dev/null 2>/dev/null; then set -e if [ "$(go env GOOS)" = "linux" ]; then - command install -C -m 0755 -d "${destdir}/${prefix}/lib/systemd/system/" - command install -C -m 0644 "${builddir}"/systemd/*.service "${destdir}/${prefix}/lib/systemd/system/" + command install -m 0755 -d "${destdir}/${prefix}/lib/systemd/system/" + command install -m 0644 "${builddir}"/systemd/*.service "${destdir}/${prefix}/lib/systemd/system/" fi else set -e if [ "$(uname -s)" = "Linux" ]; then - command install -C -m 0755 -d "${destdir}/${prefix}/lib/systemd/system/" - command install -C -m 0644 "${builddir}"/systemd/*.service "${destdir}/${prefix}/lib/systemd/system/" + command install -m 0755 -d "${destdir}/${prefix}/lib/systemd/system/" + command install -m 0644 "${builddir}"/systemd/*.service "${destdir}/${prefix}/lib/systemd/system/" fi fi From c5c8e4b5b5a11077a9df199500a4257c9feba483 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月25日 01:42:33 +0300 Subject: [PATCH 089/171] ci: Disable arm64 docker build because of SIGSEGV --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f908c63a..1063c356 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -98,7 +98,7 @@ jobs: id: docker with: context: . - platforms: linux/amd64,linux/arm64 + platforms: linux/amd64 #,linux/arm64 Temporary disabled due to SIGSEGV in gcc. file: Dockerfile push: true tags: ${{ steps.meta.outputs.tags }} From 5cd2356b28fdf7beedb5e0190bf48fc92a03784b Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月25日 01:54:31 +0300 Subject: [PATCH 090/171] ci: Try to fix ghcr.io image upload --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1063c356..bf95dbb1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -73,12 +73,14 @@ jobs: with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_PASSWORD }} + logout: false - name: "Login to GitHub Container Registry" uses: docker/login-action@v3 with: registry: "ghcr.io" username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} + logout: false # https://news.ycombinator.com/item?id=28607735 - name: "Generate container metadata" uses: docker/metadata-action@v5 id: meta From cff6cfaca676d5ee936d78aa8834ec001b3f0b2e Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月25日 02:01:52 +0300 Subject: [PATCH 091/171] ci: Another attempt to get ghcr.io upload working... --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bf95dbb1..0b5dfab0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,7 @@ permissions: id-token: write contents: read attestations: write + packages: write jobs: artifact-builder: From be0ec6b7cf14645b012e8151e8e5fb387e21df7e Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月25日 14:51:35 +0300 Subject: [PATCH 092/171] target/smtp: Check-in accidentally reverted attempt_starttls changes --- framework/config/map.go | 21 +++-- internal/smtpconn/smtpconn.go | 9 +- internal/target/smtp/smtp_downstream.go | 59 ++++++++----- internal/target/smtp/smtp_downstream_test.go | 93 ++------------------ internal/target/smtp/smtputf8_test.go | 4 + 5 files changed, 70 insertions(+), 116 deletions(-) diff --git a/framework/config/map.go b/framework/config/map.go index 10b17623..c85c19fe 100644 --- a/framework/config/map.go +++ b/framework/config/map.go @@ -20,6 +20,7 @@ package config import ( "errors" + "fmt" "reflect" "strconv" "strings" @@ -305,6 +306,16 @@ func (m *Map) DataSize(name string, inheritGlobal, required bool, defaultVal int }, store) } +func ParseBool(s string) (bool, error) { + switch strings.ToLower(s) { + case "1", "true", "on", "yes": + return true, nil + case "0", "false", "off", "no": + return false, nil + } + return false, fmt.Errorf("bool argument should be 'yes' or 'no'") +} + // Bool maps presence of some configuration directive to a boolean variable. // Additionally, 'name yes' and 'name no' are mapped to true and false // correspondingly. @@ -327,13 +338,11 @@ func (m *Map) Bool(name string, inheritGlobal, defaultVal bool, store *bool) { return nil, NodeErr(node, "expected exactly 1 argument") } - switch strings.ToLower(node.Args[0]) { - case "1", "true", "on", "yes": - return true, nil - case "0", "false", "off", "no": - return false, nil + b, err := ParseBool(node.Args[0]) + if err != nil { + return nil, NodeErr(node, "bool argument should be 'yes' or 'no'") } - return nil, NodeErr(node, "bool argument should be 'yes' or 'no'") + return b, nil }, store) } diff --git a/internal/smtpconn/smtpconn.go b/internal/smtpconn/smtpconn.go index 7f66bd23..ec42974d 100644 --- a/internal/smtpconn/smtpconn.go +++ b/internal/smtpconn/smtpconn.go @@ -259,12 +259,15 @@ func (c *C) attemptConnect(ctx context.Context, lmtp bool, endp config.Endpoint, return false, nil, nil, err } - if endp.IsTLS() || !starttls { - return endp.IsTLS(), cl, conn, nil + if !starttls { + return false, cl, conn, nil } if ok, _ := cl.Extension("STARTTLS"); !ok { - return false, cl, conn, nil + if err := cl.Quit(); err != nil { + cl.Close() + } + return false, nil, nil, fmt.Errorf("TLS required but unsupported by downstream") } cfg := tlsConfig.Clone() diff --git a/internal/target/smtp/smtp_downstream.go b/internal/target/smtp/smtp_downstream.go index f03fc827..8880f6cb 100644 --- a/internal/target/smtp/smtp_downstream.go +++ b/internal/target/smtp/smtp_downstream.go @@ -29,7 +29,6 @@ package smtp_downstream import ( "context" "crypto/tls" - "errors" "fmt" "net" "runtime/trace" @@ -54,12 +53,11 @@ type Downstream struct { lmtp bool targetsArg []string - requireTLS bool - attemptStartTLS bool - hostname string - endpoints []config.Endpoint - saslFactory saslClientFactory - tlsConfig tls.Config + starttls bool + hostname string + endpoints []config.Endpoint + saslFactory saslClientFactory + tlsConfig tls.Config connectTimeout time.Duration commandTimeout time.Duration @@ -89,10 +87,34 @@ func NewDownstream(modName, instName string, _, inlineArgs []string) (module.Mod } func (u *Downstream) Init(cfg *config.Map) error { + var attemptTLS *bool + var targetsArg []string cfg.Bool("debug", true, false, &u.log.Debug) - cfg.Bool("require_tls", false, false, &u.requireTLS) - cfg.Bool("attempt_starttls", false, !u.lmtp, &u.attemptStartTLS) + cfg.Callback("require_tls", func(m *config.Map, node config.Node) error { + u.log.Msg("require_tls directive is deprecated and ignored") + return nil + }) + cfg.Callback("attempt_starttls", func(m *config.Map, node config.Node) error { + u.log.Msg("attempt_starttls directive is deprecated and equivalent to starttls") + + if len(node.Args) == 0 { + trueVal := true + attemptTLS = &trueVal + return nil + } + if len(node.Args) != 1 { + return config.NodeErr(node, "expected exactly 1 argument") + } + + b, err := config.ParseBool(node.Args[0]) + if err != nil { + return err + } + attemptTLS = &b + return nil + }) + cfg.Bool("starttls", false, !u.lmtp, &u.starttls) cfg.String("hostname", true, true, "", &u.hostname) cfg.StringList("targets", false, false, nil, &targetsArg) cfg.Custom("auth", false, false, func() (interface{}, error) { @@ -109,6 +131,10 @@ func (u *Downstream) Init(cfg *config.Map) error { return err } + if attemptTLS != nil { + u.starttls = *attemptTLS + } + // INTERNATIONALIZATION: See RFC 6531 Section 3.7.1. var err error u.hostname, err = idna.ToASCII(u.hostname) @@ -201,14 +227,11 @@ func (d *delivery) connect(ctx context.Context) error { } for _, endp := range d.u.endpoints { - var ( - didTLS bool - err error - ) + var err error if d.u.lmtp { - didTLS, err = conn.ConnectLMTP(ctx, endp, d.u.attemptStartTLS, &d.u.tlsConfig) + _, err = conn.ConnectLMTP(ctx, endp, d.u.starttls, &d.u.tlsConfig) } else { - didTLS, err = conn.Connect(ctx, endp, d.u.attemptStartTLS, &d.u.tlsConfig) + _, err = conn.Connect(ctx, endp, d.u.starttls, &d.u.tlsConfig) } if err != nil { if len(d.u.endpoints) != 1 { @@ -220,12 +243,6 @@ func (d *delivery) connect(ctx context.Context) error { d.log.DebugMsg("connected", "downstream_server", conn.ServerName()) - if !didTLS && d.u.requireTLS { - conn.Close() - lastErr = errors.New("TLS is required, but unsupported by downstream") - continue - } - lastErr = nil break } diff --git a/internal/target/smtp/smtp_downstream_test.go b/internal/target/smtp/smtp_downstream_test.go index 011ca04a..31ef2954 100644 --- a/internal/target/smtp/smtp_downstream_test.go +++ b/internal/target/smtp/smtp_downstream_test.go @@ -207,7 +207,7 @@ func TestDownstreamDelivery_MAILErr(t *testing.T) { testutils.CheckSMTPErr(t, err, 550, exterrors.EnhancedCode{5, 1, 2}, "Hey") } -func TestDownstreamDelivery_AttemptTLS(t *testing.T) { +func TestDownstreamDelivery_StartTLS(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+testPort) defer srv.Close() defer testutils.CheckSMTPConnLeak(t, srv) @@ -221,9 +221,9 @@ func TestDownstreamDelivery_AttemptTLS(t *testing.T) { Port: testPort, }, }, - tlsConfig: *clientCfg.Clone(), - attemptStartTLS: true, - log: testutils.Logger(t, "target.smtp"), + tlsConfig: *clientCfg.Clone(), + starttls: true, + log: testutils.Logger(t, "target.smtp"), } testutils.DoTestDelivery(t, mod, "test@example.invalid", []string{"rcpt@example.invalid"}) @@ -235,85 +235,7 @@ func TestDownstreamDelivery_AttemptTLS(t *testing.T) { } } -func TestDownstreamDelivery_AttemptTLS_Fallback(t *testing.T) { - be, srv := testutils.SMTPServer(t, "127.0.0.1:"+testPort) - defer srv.Close() - defer testutils.CheckSMTPConnLeak(t, srv) - - mod := &Downstream{ - hostname: "mx.example.invalid", - endpoints: []config.Endpoint{ - { - Scheme: "tcp", - Host: "127.0.0.1", - Port: testPort, - }, - }, - attemptStartTLS: true, - log: testutils.Logger(t, "target.smtp"), - } - - testutils.DoTestDelivery(t, mod, "test@example.invalid", []string{"rcpt@example.invalid"}) - be.CheckMsg(t, 0, "test@example.invalid", []string{"rcpt@example.invalid"}) -} - -func TestDownstreamDelivery_RequireTLS(t *testing.T) { - clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+testPort) - defer srv.Close() - defer testutils.CheckSMTPConnLeak(t, srv) - - mod := &Downstream{ - hostname: "mx.example.invalid", - endpoints: []config.Endpoint{ - { - Scheme: "tcp", - Host: "127.0.0.1", - Port: testPort, - }, - }, - tlsConfig: *clientCfg.Clone(), - attemptStartTLS: true, - requireTLS: true, - log: testutils.Logger(t, "target.smtp"), - } - - testutils.DoTestDelivery(t, mod, "test@example.invalid", []string{"rcpt@example.invalid"}) - be.CheckMsg(t, 0, "test@example.invalid", []string{"rcpt@example.invalid"}) - tlsState, ok := be.Messages[0].Conn.TLSConnectionState() - if !ok || !tlsState.HandshakeComplete { - t.Fatal("Message was not delivered over TLS") - } -} - -func TestDownstreamDelivery_RequireTLS_Implicit(t *testing.T) { - clientCfg, be, srv := testutils.SMTPServerTLS(t, "127.0.0.1:"+testPort) - defer srv.Close() - defer testutils.CheckSMTPConnLeak(t, srv) - - mod := &Downstream{ - hostname: "mx.example.invalid", - endpoints: []config.Endpoint{ - { - Scheme: "tls", - Host: "127.0.0.1", - Port: testPort, - }, - }, - tlsConfig: *clientCfg.Clone(), - attemptStartTLS: true, - requireTLS: true, - log: testutils.Logger(t, "target.smtp"), - } - - testutils.DoTestDelivery(t, mod, "test@example.invalid", []string{"rcpt@example.invalid"}) - be.CheckMsg(t, 0, "test@example.invalid", []string{"rcpt@example.invalid"}) - tlsState, ok := be.Messages[0].Conn.TLSConnectionState() - if !ok || !tlsState.HandshakeComplete { - t.Fatal("Message was not delivered over TLS") - } -} - -func TestDownstreamDelivery_RequireTLS_Fail(t *testing.T) { +func TestDownstreamDelivery_StartTLS_NoFallback(t *testing.T) { _, srv := testutils.SMTPServer(t, "127.0.0.1:"+testPort) defer srv.Close() defer testutils.CheckSMTPConnLeak(t, srv) @@ -327,9 +249,8 @@ func TestDownstreamDelivery_RequireTLS_Fail(t *testing.T) { Port: testPort, }, }, - attemptStartTLS: true, - requireTLS: true, - log: testutils.Logger(t, "target.smtp"), + starttls: true, + log: testutils.Logger(t, "target.smtp"), } _, err := testutils.DoTestDeliveryErr(t, mod, "test@example.invalid", []string{"rcpt@example.invalid"}) diff --git a/internal/target/smtp/smtputf8_test.go b/internal/target/smtp/smtputf8_test.go index d99eaf19..74aae232 100644 --- a/internal/target/smtp/smtputf8_test.go +++ b/internal/target/smtp/smtputf8_test.go @@ -40,6 +40,10 @@ func TestDownstreamDelivery_EHLO_ALabel(t *testing.T) { Name: "hostname", Args: []string{"тест.invalid"}, }, + { + Name: "starttls", + Args: []string{"no"}, + }, }, })); err != nil { t.Fatal(err) From 21485e99d2da2b663f040eadd6c4f9ae57a7f914 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月25日 14:52:02 +0300 Subject: [PATCH 093/171] maddy 0.8.1 --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index a3df0a69..6f4eebdf 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.8.0 +0.8.1 From 69b434f3417ec47f2741240fa85bf17bb9a543a0 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月28日 23:33:37 +0300 Subject: [PATCH 094/171] tests: Allow to run maddyctl commands in integration tests --- internal/cli/app.go | 30 ++++---- internal/cli/extflag.go | 60 +++++++++++++++ maddy.go | 2 +- tests/cover_test.go | 20 ++--- tests/t.go | 157 ++++++++++++++++++++++++++++------------ 5 files changed, 198 insertions(+), 71 deletions(-) create mode 100644 internal/cli/extflag.go diff --git a/internal/cli/app.go b/internal/cli/app.go index fc4273d7..59108965 100644 --- a/internal/cli/app.go +++ b/internal/cli/app.go @@ -1,7 +1,6 @@ package maddycli import ( - "flag" "fmt" "os" "strings" @@ -30,10 +29,6 @@ databases used by it (all other subcommands). } app.ExitErrHandler = func(c *cli.Context, err error) { cli.HandleExitCoder(err) - if err != nil { - log.Println(err) - cli.OsExiter(1) - } } app.EnableBashCompletion = true app.Commands = []*cli.Command{ @@ -66,9 +61,6 @@ databases used by it (all other subcommands). func AddGlobalFlag(f cli.Flag) { app.Flags = append(app.Flags, f) - if err := f.Apply(flag.CommandLine); err != nil { - log.Println("GlobalFlag", f, "could not be mapped to stdlib flag:", err) - } } func AddSubcommand(cmd *cli.Command) { @@ -83,15 +75,27 @@ func AddSubcommand(cmd *cli.Command) { return cmd.Action(c) } app.Flags = append(app.Flags, cmd.Flags...) - for _, f := range cmd.Flags { - if err := f.Apply(flag.CommandLine); err != nil { - log.Println("GlobalFlag", f, "could not be mapped to stdlib flag:", err) - } - } } } +// RunWithoutExit is like Run but returns exit code instead of calling os.Exit +// To be used in maddy.cover. +func RunWithoutExit() int { + code := 0 + + cli.OsExiter = func(c int) { code = c } + defer func() { + cli.OsExiter = os.Exit + }() + + Run() + + return code +} + func Run() { + mapStdlibFlags(app) + // Actual entry point is registered in maddy.go. // Print help when called via maddyctl executable. To be removed diff --git a/internal/cli/extflag.go b/internal/cli/extflag.go new file mode 100644 index 00000000..8cfc27c3 --- /dev/null +++ b/internal/cli/extflag.go @@ -0,0 +1,60 @@ +package maddycli + +import ( + "flag" + + "github.com/urfave/cli/v2" +) + +// extFlag implements cli.Flag via standard flag.Flag. +type extFlag struct { + f *flag.Flag +} + +func (e *extFlag) Apply(fs *flag.FlagSet) error { + fs.Var(e.f.Value, e.f.Name, e.f.Usage) + return nil +} + +func (e *extFlag) Names() []string { + return []string{e.f.Name} +} + +func (e *extFlag) IsSet() bool { + return false +} + +func (e *extFlag) String() string { + return cli.FlagStringer(e) +} + +func (e *extFlag) IsVisible() bool { + return true +} + +func (e *extFlag) TakesValue() bool { + return false +} + +func (e *extFlag) GetUsage() string { + return e.f.Usage +} + +func (e *extFlag) GetValue() string { + return e.f.Value.String() +} + +func (e *extFlag) GetDefaultText() string { + return e.f.DefValue +} + +func (e *extFlag) GetEnvVars() []string { + return nil +} + +func mapStdlibFlags(app *cli.App) { + // Modified AllowExtFlags from cli lib with -test.* exception removed. + flag.VisitAll(func(f *flag.Flag) { + app.Flags = append(app.Flags, &extFlag{f}) + }) +} diff --git a/maddy.go b/maddy.go index e6a05cf1..f838e96e 100644 --- a/maddy.go +++ b/maddy.go @@ -284,7 +284,7 @@ func ensureDirectoryWritable(path string) error { return err } testFile.Close() - return os.Remove(testFile.Name()) + return os.RemoveAll(testFile.Name()) } func ReadGlobals(cfg []config.Node) (map[string]interface{}, []config.Node, error) { diff --git a/tests/cover_test.go b/tests/cover_test.go index d47c8840..298e3e9f 100644 --- a/tests/cover_test.go +++ b/tests/cover_test.go @@ -42,8 +42,10 @@ import ( "os" "testing" - "github.com/foxcpp/maddy" - "github.com/urfave/cli/v2" + _ "github.com/foxcpp/maddy" // To register run command + _ "github.com/foxcpp/maddy/internal/cli/ctl" // To register other CLI commands. + + maddycli "github.com/foxcpp/maddy/internal/cli" ) func TestMain(m *testing.M) { @@ -56,16 +58,14 @@ func TestMain(m *testing.M) { panic(err) } + // Skip flag parsing and make flag.Parse no-op so when + // m.Run calls it it will not error out on maddy flags. + args := os.Args + os.Args = []string{"command"} flag.Parse() + os.Args = args - app := cli.NewApp() - // maddycli wrapper registers all necessary flags with flag.CommandLine by default - ctx := cli.NewContext(app, flag.CommandLine, nil) - err = maddy.Run(ctx) - code := 0 - if ec, ok := err.(cli.ExitCoder); ok { - code = ec.ExitCode() - } + code := maddycli.RunWithoutExit() if err := os.Chdir(wd); err != nil { panic(err) diff --git a/tests/t.go b/tests/t.go index 2243662a..3ae27c65 100644 --- a/tests/t.go +++ b/tests/t.go @@ -25,6 +25,7 @@ package tests import ( "bufio" + "bytes" "flag" "fmt" "math/rand" @@ -34,6 +35,7 @@ import ( "path/filepath" "strconv" "strings" + "sync" "testing" "time" @@ -129,14 +131,7 @@ func (t *T) Env(kv string) { t.env = append(t.env, kv) } -// Run completes the configuration of test environment and starts the test server. -// -// T.Close should be called by the end of test to release any resources and -// shutdown the server. -// -// The parameter waitListeners specifies the amount of listeners the server is -// supposed to configure. Run() will block before all of them are up. -func (t *T) Run(waitListeners int) { +func (t *T) ensureCanRun() { if t.cfg == "" { panic("tests: Run called without configuration set") } @@ -146,66 +141,75 @@ func (t *T) Run(waitListeners int) { // any DNS queries to the real world. t.Log("NOTE: Explicit DNS(nil) is recommended.") t.DNS(nil) - } - // Setup file system, create statedir, runtimedir, write out config. - testDir, err := os.MkdirTemp("", "maddy-tests-") - if err != nil { - t.Fatal("Test configuration failed:", err) + t.Cleanup(func() { + // Shutdown the DNS server after maddy to make sure it will not spend time + // timing out queries. + if err := t.dnsServ.Close(); err != nil { + t.Log("Unable to stop the DNS server:", err) + } + t.dnsServ = nil + }) } - t.testDir = testDir - - t.Log("Using", t.testDir) - defer func() { - if !t.Failed() { - return + // Setup file system, create statedir, runtimedir, write out config. + if t.testDir == "" { + testDir, err := os.MkdirTemp("", "maddy-tests-") + if err != nil { + t.Fatal("Test configuration failed:", err) } + t.testDir = testDir + t.Log("using", t.testDir) - // Clean-up on test failure (if Run failed somewhere) - - t.dnsServ.Close() - t.dnsServ = nil + if err := os.MkdirAll(filepath.Join(t.testDir, "statedir"), os.ModePerm); err != nil { + t.Fatal("Test configuration failed:", err) + } + if err := os.MkdirAll(filepath.Join(t.testDir, "runtimedir"), os.ModePerm); err != nil { + t.Fatal("Test configuration failed:", err) + } - os.RemoveAll(t.testDir) - t.testDir = "" - }() + t.Cleanup(func() { + if !t.Failed() { + return + } - if err := os.MkdirAll(filepath.Join(t.testDir, "statedir"), os.ModePerm); err != nil { - t.Fatal("Test configuration failed:", err) - } - if err := os.MkdirAll(filepath.Join(t.testDir, "runtimedir"), os.ModePerm); err != nil { - t.Fatal("Test configuration failed:", err) + t.Log("removing", t.testDir) + os.RemoveAll(t.testDir) + t.testDir = "" + }) } configPreable := "state_dir " + filepath.Join(t.testDir, "statedir") + "\n" + - "runtime_dir " + filepath.Join(t.testDir, "runtime") + "\n\n" + "runtime_dir " + filepath.Join(t.testDir, "runtimedir") + "\n\n" - err = os.WriteFile(filepath.Join(t.testDir, "maddy.conf"), []byte(configPreable+t.cfg), os.ModePerm) + err := os.WriteFile(filepath.Join(t.testDir, "maddy.conf"), []byte(configPreable+t.cfg), os.ModePerm) if err != nil { t.Fatal("Test configuration failed:", err) } +} +func (t *T) buildCmd(additionalArgs ...string) *exec.Cmd { // Assigning 0 by default will make outbound SMTP unusable. remoteSmtp := "0" if port := t.ports["remote_smtp"]; port != 0 { remoteSmtp = strconv.Itoa(int(port)) } - cmd := exec.Command(TestBinary, - "-config", filepath.Join(t.testDir, "maddy.conf"), + args := []string{"-config", filepath.Join(t.testDir, "maddy.conf"), "-debug.smtpport", remoteSmtp, "-debug.dnsoverride", t.dnsServ.LocalAddr().String(), - "-log", "stderr") + "-log", "/tmp/test.log"} if CoverageOut != "" { - cmd.Args = append(cmd.Args, "-test.coverprofile", CoverageOut+"."+strconv.FormatInt(time.Now().UnixNano(), 16)) + args = append(args, "-test.coverprofile", CoverageOut+"."+strconv.FormatInt(time.Now().UnixNano(), 16)) } if DebugLog { - cmd.Args = append(cmd.Args, "-debug") + args = append(args, "-debug") } - t.Logf("launching %v", cmd.Args) + args = append(args, additionalArgs...) + + cmd := exec.Command(TestBinary, args...) pwd, err := os.Getwd() if err != nil { @@ -217,19 +221,79 @@ func (t *T) Run(waitListeners int) { cmd.Env = append(cmd.Env, "TEST_PWD="+pwd, "TEST_STATE_DIR="+filepath.Join(t.testDir, "statedir"), - "TEST_RUNTIME_DIR="+filepath.Join(t.testDir, "statedir"), + "TEST_RUNTIME_DIR="+filepath.Join(t.testDir, "runtimedir"), ) for name, port := range t.ports { cmd.Env = append(cmd.Env, fmt.Sprintf("TEST_PORT_%s=%d", name, port)) } cmd.Env = append(cmd.Env, t.env...) + return cmd +} + +func (t *T) MustRunCLIGroup(args ...[]string) { + t.ensureCanRun() + + wg := sync.WaitGroup{} + for _, arg := range args { + wg.Add(1) + go func() { + defer wg.Done() + + _, err := t.RunCLI(arg...) + if err != nil { + t.Fatalf("maddy %v: %v", arg, err) + } + }() + } + wg.Wait() +} + +func (t *T) MustRunCLI(args ...string) string { + s, err := t.RunCLI(args...) + if err != nil { + t.Fatalf("maddy %v: %v", args, err) + } + return s +} + +func (t *T) RunCLI(args ...string) (string, error) { + t.ensureCanRun() + cmd := t.buildCmd(args...) + + var stderr, stdout bytes.Buffer + cmd.Stderr = &stderr + cmd.Stdout = &stdout + + t.Log("launching maddy", cmd.Args) + if err := cmd.Run(); err != nil { + t.Log("Stderr:", stderr.String()) + t.Fatal("Test configuration failed:", err) + } + + t.Log("Stderr:", stderr.String()) + + return stdout.String(), nil +} + +// Run completes the configuration of test environment and starts the test server. +// +// T.Close should be called by the end of test to release any resources and +// shutdown the server. +// +// The parameter waitListeners specifies the amount of listeners the server is +// supposed to configure. Run() will block before all of them are up. +func (t *T) Run(waitListeners int) { + t.ensureCanRun() + cmd := t.buildCmd("run") + // Capture maddy log and redirect it. logOut, err := cmd.StderrPipe() if err != nil { t.Fatal("Test configuration failed:", err) } + t.Log("launching maddy", cmd.Args) if err := cmd.Start(); err != nil { t.Fatal("Test configuration failed:", err) } @@ -264,6 +328,8 @@ func (t *T) Run(waitListeners int) { } t.servProc = cmd + + t.Cleanup(t.killServer) } func (t *T) StateDir() string { @@ -274,7 +340,7 @@ func (t *T) RuntimeDir() string { return filepath.Join(t.testDir, "statedir") } -func (t *T) Close() { +func (t *T) killServer() { if err := t.servProc.Process.Signal(os.Interrupt); err != nil { t.Log("Unable to kill the server process:", err) os.RemoveAll(t.testDir) @@ -299,13 +365,10 @@ func (t *T) Close() { t.Log("Failed to remove test directory:", err) } t.testDir = "" +} - // Shutdown the DNS server after maddy to make sure it will not spend time - // timing out queries. - if err := t.dnsServ.Close(); err != nil { - t.Log("Unable to stop the DNS server:", err) - } - t.dnsServ = nil +func (t *T) Close() { + t.Log("close is no-op") } // Printf implements Logger interfaces used by some libraries. From 06fd5249d2230245d0cf080ae0fc6cc7e0d86319 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月28日 23:34:03 +0300 Subject: [PATCH 095/171] cli/ctl: Add --no-specialuse flag for imap-acct create --- internal/cli/ctl/imapacct.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/internal/cli/ctl/imapacct.go b/internal/cli/ctl/imapacct.go index ee7d19c2..2541a228 100644 --- a/internal/cli/ctl/imapacct.go +++ b/internal/cli/ctl/imapacct.go @@ -81,6 +81,11 @@ creates a set of default folder (mailboxes) with special-use attribute set.`, EnvVars: []string{"MADDY_CFGBLOCK"}, Value: "local_mailboxes", }, + &cli.BoolFlag{ + Name: "no-specialuse", + Usage: "Do not create special-use folders", + Value: false, + }, &cli.StringFlag{ Name: "sent-name", Usage: "Name of special mailbox for sent messages, use empty string to not create any", @@ -235,6 +240,10 @@ func imapAcctCreate(be module.Storage, ctx *cli.Context) error { fmt.Fprintf(os.Stderr, "Note: Storage backend does not support SPECIAL-USE IMAP extension") } + if ctx.Bool("no-specialuse") { + return nil + } + createMbox := func(name, specialUseAttr string) error { if suu == nil { return act.CreateMailbox(name) From c48332a9401151a93df75fae55dd34bea2cafc56 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月28日 23:34:52 +0300 Subject: [PATCH 096/171] auth/sasl: Add missing usernameForAuth call --- internal/auth/sasl.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/internal/auth/sasl.go b/internal/auth/sasl.go index 00d25ffa..5968c3c0 100644 --- a/internal/auth/sasl.go +++ b/internal/auth/sasl.go @@ -139,7 +139,12 @@ func (s *SASLAuth) CreateSASL(mech string, remoteAddr net.Addr, successCb func(i return ErrInvalidAuthCred } - err := s.AuthPlain(username, password) + username, err := s.usernameForAuth(context.Background(), username) + if err != nil { + return err + } + + err = s.AuthPlain(username, password) if err != nil { s.Log.Error("authentication failed", err, "username", username, "src_ip", remoteAddr) return ErrInvalidAuthCred @@ -156,6 +161,11 @@ func (s *SASLAuth) CreateSASL(mech string, remoteAddr net.Addr, successCb func(i } return sasllogin.NewLoginServer(func(username, password string) error { + username, err := s.usernameForAuth(context.Background(), username) + if err != nil { + return err + } + err := s.AuthPlain(username, password) if err != nil { s.Log.Error("authentication failed", err, "username", username, "src_ip", remoteAddr) From b3ebe4b81e3ca1293f4da0505bb8811e8263b051 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月28日 23:35:08 +0300 Subject: [PATCH 097/171] endpoint/smtp: Drop duplicate RunEarlyChecks call --- internal/endpoint/smtp/session.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/internal/endpoint/smtp/session.go b/internal/endpoint/smtp/session.go index 0eb6427f..cff1c01e 100644 --- a/internal/endpoint/smtp/session.go +++ b/internal/endpoint/smtp/session.go @@ -103,11 +103,6 @@ func (s *Session) AuthMechanisms() []string { } func (s *Session) Auth(mech string) (sasl.Server, error) { - // Executed before authentication and session initialization. - if err := s.endp.pipeline.RunEarlyChecks(s.sessionCtx, &s.connState); err != nil { - return nil, s.endp.wrapErr("", true, "AUTH", err) - } - return s.endp.saslAuth.CreateSASL(mech, s.connState.RemoteAddr, func(identity string, data auth.ContextData) error { s.connState.AuthUser = identity s.connState.AuthPassword = data.Password From 41b49bd44fbdef06e7d8e98facea54f73b997bf3 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月28日 23:35:23 +0300 Subject: [PATCH 098/171] tests: Add integration test for multidomain setups with shared namespaces --- tests/multiple_domains_test.go | 271 +++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 tests/multiple_domains_test.go diff --git a/tests/multiple_domains_test.go b/tests/multiple_domains_test.go new file mode 100644 index 00000000..971ca91b --- /dev/null +++ b/tests/multiple_domains_test.go @@ -0,0 +1,271 @@ +//go:build integration + +/* +Maddy Mail Server - Composable all-in-one email server. +Copyright © 2019-2025 Max Mazurov , Maddy Mail Server contributors + +This program 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. + +This program 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 . +*/ + +package tests_test + +import ( + "testing" + + "github.com/foxcpp/maddy/tests" +) + +// Test cases based on https://maddy.email/multiple-domains/ + +func TestMultipleDomains_SeparateNamespace(tt *testing.T) { + tt.Parallel() + t := tests.NewT(tt) + t.DNS(nil) + t.Port("submission") + t.Port("imap") + t.Config(` + tls off + hostname test.maddy.email + + auth.pass_table local_authdb { + table sql_table { + driver sqlite3 + dsn credentials.db + table_name passwords + } + } + storage.imapsql local_mailboxes { + driver sqlite3 + dsn imapsql.db + } + + submission tcp://0.0.0.0:{env:TEST_PORT_submission} { + auth &local_authdb + reject + } + imap tcp://127.0.0.1:{env:TEST_PORT_imap} { + auth &local_authdb + storage &local_mailboxes + } + `) + + t.MustRunCLIGroup( + []string{"creds", "create", "-p", "user1", "user1@test1.maddy.email"}, + []string{"creds", "create", "-p", "user2", "user2@test1.maddy.email"}, + []string{"creds", "create", "-p", "user3", "user1@test2.maddy.email"}, + []string{"imap-acct", "create", "--no-specialuse", "user1@test1.maddy.email"}, + []string{"imap-acct", "create", "--no-specialuse", "user2@test1.maddy.email"}, + []string{"imap-acct", "create", "--no-specialuse", "user1@test2.maddy.email"}, + ) + t.Run(2) + + user1 := t.Conn("imap") + user1.ExpectPattern(`\* OK *`) + user1.Writeln(`. LOGIN user1@test1.maddy.email user1`) + user1.ExpectPattern(`. OK *`) + user1.Writeln(`. CREATE user1`) + user1.ExpectPattern(`. OK *`) + + user2 := t.Conn("imap") + user2.ExpectPattern(`\* OK *`) + user2.Writeln(`. LOGIN user2@test1.maddy.email user2`) + user2.ExpectPattern(`. OK *`) + user2.Writeln(`. CREATE user2`) + user2.ExpectPattern(`. OK *`) + + user3 := t.Conn("imap") + user3.ExpectPattern(`\* OK *`) + user3.Writeln(`. LOGIN user1@test2.maddy.email user3`) + user3.ExpectPattern(`. OK *`) + user3.Writeln(`. CREATE user3`) + user3.ExpectPattern(`. OK *`) + + user1.Writeln(`. LIST "" "*"`) + user1.Expect(`* LIST (\HasNoChildren) "." INBOX`) + user1.Expect(`* LIST (\HasNoChildren) "." "user1"`) + user1.ExpectPattern(". OK *") + + user2.Writeln(`. LIST "" "*"`) + user2.Expect(`* LIST (\HasNoChildren) "." INBOX`) + user2.Expect(`* LIST (\HasNoChildren) "." "user2"`) + user2.ExpectPattern(". OK *") + + user3.Writeln(`. LIST "" "*"`) + user3.Expect(`* LIST (\HasNoChildren) "." INBOX`) + user3.Expect(`* LIST (\HasNoChildren) "." "user3"`) + user3.ExpectPattern(". OK *") +} + +func TestMultipleDomains_SharedCredentials_DistinctMailboxes(tt *testing.T) { + tt.Parallel() + t := tests.NewT(tt) + t.DNS(nil) + t.Port("submission") + t.Port("imap") + t.Config(` + tls off + hostname test.maddy.email + auth_map email_localpart + + auth.pass_table local_authdb { + table sql_table { + driver sqlite3 + dsn credentials.db + table_name passwords + } + } + storage.imapsql local_mailboxes { + driver sqlite3 + dsn imapsql.db + } + + submission tcp://0.0.0.0:{env:TEST_PORT_submission} { + auth &local_authdb + reject + } + imap tcp://127.0.0.1:{env:TEST_PORT_imap} { + auth &local_authdb + storage &local_mailboxes + } + `) + + t.MustRunCLIGroup( + []string{"creds", "create", "-p", "user1", "user1"}, + []string{"creds", "create", "-p", "user2", "user2"}, + []string{"imap-acct", "create", "--no-specialuse", "user1@test1.maddy.email"}, + []string{"imap-acct", "create", "--no-specialuse", "user2@test1.maddy.email"}, + []string{"imap-acct", "create", "--no-specialuse", "user1@test2.maddy.email"}, + ) + t.Run(2) + + user1 := t.Conn("imap") + user1.ExpectPattern(`\* OK *`) + user1.Writeln(`. LOGIN user1@test1.maddy.email user1`) + user1.ExpectPattern(`. OK *`) + user1.Writeln(`. CREATE user1`) + user1.ExpectPattern(`. OK *`) + + user2 := t.Conn("imap") + user2.ExpectPattern(`\* OK *`) + user2.Writeln(`. LOGIN user2@test1.maddy.email user2`) + user2.ExpectPattern(`. OK *`) + user2.Writeln(`. CREATE user2`) + user2.ExpectPattern(`. OK *`) + + user3 := t.Conn("imap") + user3.ExpectPattern(`\* OK *`) + user3.Writeln(`. LOGIN user1@test2.maddy.email user1`) + user3.ExpectPattern(`. OK *`) + user3.Writeln(`. CREATE user3`) + user3.ExpectPattern(`. OK *`) + + user1.Writeln(`. LIST "" "*"`) + user1.Expect(`* LIST (\HasNoChildren) "." INBOX`) + user1.Expect(`* LIST (\HasNoChildren) "." "user1"`) + user1.ExpectPattern(". OK *") + + user2.Writeln(`. LIST "" "*"`) + user2.Expect(`* LIST (\HasNoChildren) "." INBOX`) + user2.Expect(`* LIST (\HasNoChildren) "." "user2"`) + user2.ExpectPattern(". OK *") + + user3.Writeln(`. LIST "" "*"`) + user3.Expect(`* LIST (\HasNoChildren) "." INBOX`) + user3.Expect(`* LIST (\HasNoChildren) "." "user3"`) + user3.ExpectPattern(". OK *") +} + +func TestMultipleDomains_SharedCredentials_SharedMailboxes(tt *testing.T) { + tt.Parallel() + t := tests.NewT(tt) + t.DNS(nil) + t.Port("submission") + t.Port("imap") + t.Config(` + tls off + hostname test.maddy.email + auth_map email_localpart_optional + + auth.pass_table local_authdb { + table sql_table { + driver sqlite3 + dsn credentials.db + table_name passwords + } + } + storage.imapsql local_mailboxes { + driver sqlite3 + dsn imapsql.db + + delivery_map email_localpart_optional + } + + submission tcp://0.0.0.0:{env:TEST_PORT_submission} { + auth &local_authdb + reject + } + imap tcp://127.0.0.1:{env:TEST_PORT_imap} { + auth &local_authdb + storage &local_mailboxes + + storage_map email_localpart_optional + } + `) + + t.MustRunCLIGroup( + []string{"creds", "create", "-p", "user1", "user1"}, + []string{"creds", "create", "-p", "user2", "user2"}, + []string{"imap-acct", "create", "--no-specialuse", "user1"}, + []string{"imap-acct", "create", "--no-specialuse", "user2"}, + ) + t.Run(2) + + user1 := t.Conn("imap") + user1.ExpectPattern(`\* OK *`) + user1.Writeln(`. LOGIN user1 user1`) + user1.ExpectPattern(`. OK *`) + user1.Writeln(`. CREATE user1`) + user1.ExpectPattern(`. OK *`) + + user2 := t.Conn("imap") + user2.ExpectPattern(`\* OK *`) + user2.Writeln(`. LOGIN user2@test1.maddy.email user2`) + user2.ExpectPattern(`. OK *`) + user2.Writeln(`. CREATE user2`) + user2.ExpectPattern(`. OK *`) + + user12 := t.Conn("imap") + user12.ExpectPattern(`\* OK *`) + user12.Writeln(`. LOGIN user1@test2.maddy.email user1`) + user12.ExpectPattern(`. OK *`) + user12.Writeln(`. CREATE user12`) + user12.ExpectPattern(`. OK *`) + + user1.Writeln(`. LIST "" "*"`) + user1.Expect(`* LIST (\HasNoChildren) "." INBOX`) + user1.Expect(`* LIST (\HasNoChildren) "." "user1"`) + user1.Expect(`* LIST (\HasNoChildren) "." "user12"`) + user1.ExpectPattern(". OK *") + + user2.Writeln(`. LIST "" "*"`) + user2.Expect(`* LIST (\HasNoChildren) "." INBOX`) + user2.Expect(`* LIST (\HasNoChildren) "." "user2"`) + user2.ExpectPattern(". OK *") + + user12.Writeln(`. LIST "" "*"`) + user12.Expect(`* LIST (\HasNoChildren) "." INBOX`) + user12.Expect(`* LIST (\HasNoChildren) "." "user1"`) + user12.Expect(`* LIST (\HasNoChildren) "." "user12"`) + user12.ExpectPattern(". OK *") +} From 503c9f8849af59fe0490e78c71feb618da9a4150 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月29日 21:40:15 +0300 Subject: [PATCH 099/171] Implement Configure-Start-Stop module lifetime and non-global Registry object container.Global is temporarily added until we start passing container during module initialization. --- framework/config/module/modconfig.go | 30 +-- framework/container/container.go | 70 ++++++ framework/log/log.go | 11 + framework/module/delivery_target.go | 6 +- framework/module/dummy.go | 6 +- framework/module/instances.go | 105 -------- framework/module/lifetime.go | 121 +++++++++ framework/module/module.go | 26 +- framework/module/modules.go | 96 ++++++++ framework/module/mxauth.go | 2 +- framework/module/partial_delivery.go | 2 +- framework/module/registry.go | 162 ++++++++----- internal/auth/dovecot_sasl/dovecot_sasl.go | 20 +- internal/auth/external/externalauth.go | 12 +- internal/auth/ldap/ldap.go | 32 ++- internal/auth/netauth/netauth.go | 10 +- internal/auth/pam/module.go | 11 +- internal/auth/pass_table/table.go | 18 +- internal/auth/pass_table/table_test.go | 4 +- .../auth/plain_separate/plain_separate.go | 12 +- internal/auth/shadow/module.go | 11 +- .../authorize_sender/authorize_sender.go | 9 +- internal/check/command/command.go | 18 +- internal/check/dkim/dkim.go | 11 +- internal/check/dkim/dkim_test.go | 4 +- internal/check/dnsbl/dnsbl.go | 12 +- internal/check/milter/milter.go | 20 +- internal/check/milter/milter_test.go | 4 +- internal/check/rspamd/rspamd.go | 22 +- internal/check/spf/spf.go | 4 +- internal/check/stateless_check.go | 11 +- internal/cli/ctl/moduleinit.go | 58 ++--- .../endpoint/dovecot_sasld/dovecot_sasl.go | 24 +- internal/endpoint/imap/imap.go | 56 +++-- internal/endpoint/openmetrics/om.go | 38 +-- internal/endpoint/smtp/session.go | 2 +- internal/endpoint/smtp/smtp.go | 23 +- internal/endpoint/smtp/smtp_test.go | 30 ++- internal/endpoint/smtp/smtputf8_test.go | 20 +- internal/endpoint/smtp/submission_test.go | 2 +- internal/imap_filter/command/command.go | 18 +- internal/imap_filter/group.go | 4 +- internal/libdns/acmedns.go | 2 +- internal/libdns/alidns.go | 2 +- internal/libdns/cloudflare.go | 2 +- internal/libdns/digitalocean.go | 2 +- internal/libdns/gandi.go | 2 +- internal/libdns/googleclouddns.go | 2 +- internal/libdns/hetzner.go | 2 +- internal/libdns/leaseweb.go | 2 +- internal/libdns/metaname.go | 2 +- internal/libdns/namecheap.go | 2 +- internal/libdns/namedotcom.go | 2 +- internal/libdns/provider_module.go | 2 +- internal/libdns/rfc2136.go | 2 +- internal/libdns/route53.go | 2 +- internal/libdns/vultr.go | 2 +- internal/limits/limits.go | 4 +- internal/modify/dkim/dkim.go | 23 +- internal/modify/dkim/dkim_test.go | 4 +- internal/modify/group.go | 6 +- internal/modify/replace_addr.go | 28 +-- internal/modify/replace_addr_test.go | 4 +- internal/msgpipeline/check_group.go | 8 +- internal/msgpipeline/dmarc_test.go | 2 +- internal/msgpipeline/module.go | 4 +- internal/msgpipeline/msgpipeline.go | 10 +- internal/msgpipeline/msgpipeline_test.go | 14 +- internal/smtpconn/smtpconn.go | 2 +- internal/storage/blob/fs/fs.go | 27 ++- internal/storage/blob/s3/s3.go | 12 +- internal/storage/blob/s3/s3_test.go | 2 +- internal/storage/imapsql/delivery.go | 4 +- internal/storage/imapsql/imapsql.go | 52 ++-- internal/table/chain.go | 4 +- internal/table/email_localpart.go | 4 +- internal/table/email_with_domain.go | 7 +- internal/table/file.go | 44 ++-- internal/table/file_test.go | 27 ++- internal/table/identity.go | 4 +- internal/table/regexp.go | 20 +- internal/table/sql_query.go | 67 ++--- internal/table/sql_query_test.go | 7 +- internal/table/sql_table.go | 12 +- internal/table/static.go | 4 +- internal/target/queue/queue.go | 34 +-- internal/target/queue/queue_test.go | 38 +-- internal/target/remote/mxauth_test.go | 28 +-- internal/target/remote/policy_group.go | 8 +- internal/target/remote/remote.go | 21 +- internal/target/remote/remote_test.go | 90 +++---- internal/target/remote/security.go | 79 +++--- internal/target/smtp/smtp_downstream.go | 28 +-- internal/target/smtp/smtputf8_test.go | 4 +- internal/testutils/bench_delivery.go | 2 +- internal/testutils/check.go | 5 +- internal/testutils/modifier.go | 5 +- internal/testutils/target.go | 16 +- internal/tls/acme/acme.go | 35 +-- internal/tls/file.go | 45 ++-- internal/tls/self_signed.go | 8 +- maddy.go | 229 +++++++++++------- signal_nonposix.go | 2 +- 103 files changed, 1305 insertions(+), 965 deletions(-) create mode 100644 framework/container/container.go delete mode 100644 framework/module/instances.go create mode 100644 framework/module/lifetime.go create mode 100644 framework/module/modules.go diff --git a/framework/config/module/modconfig.go b/framework/config/module/modconfig.go index 3183bb93..443bfebd 100644 --- a/framework/config/module/modconfig.go +++ b/framework/config/module/modconfig.go @@ -28,19 +28,18 @@ package modconfig import ( "fmt" - "io" "reflect" "strings" parser "github.com/foxcpp/maddy/framework/cfgparser" "github.com/foxcpp/maddy/framework/config" - "github.com/foxcpp/maddy/framework/hooks" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" ) // createInlineModule is a helper function for config matchers that can create inline modules. -func createInlineModule(preferredNamespace, modName string, args []string) (module.Module, error) { +func createInlineModule(preferredNamespace, modName string) (module.Module, error) { var newMod module.FuncNewModule originalModName := modName @@ -61,26 +60,21 @@ func createInlineModule(preferredNamespace, modName string, args []string) (modu return nil, fmt.Errorf("unknown module: %s (namespace: %s)", originalModName, preferredNamespace) } - return newMod(modName, "", nil, args) + return newMod(modName, "") } -// initInlineModule constructs "faked" config tree and passes it to module +// configureInlineModule constructs "faked" config tree and passes it to module // Init function to make it look like it is defined at top-level. // -// args must contain at least one argument, otherwise initInlineModule panics. -func initInlineModule(modObj module.Module, globals map[string]interface{}, block config.Node) error { - err := modObj.Init(config.NewMap(globals, block)) +// args must contain at least one argument, otherwise configureInlineModule panics. +func configureInlineModule(modObj module.Module, args []string, globals map[string]interface{}, block config.Node) error { + err := modObj.Configure(args, config.NewMap(globals, block)) if err != nil { return err } - if closer, ok := modObj.(io.Closer); ok { - hooks.AddHook(hooks.EventShutdown, func() { - log.Debugf("close %s (%s)", modObj.Name(), modObj.InstanceName()) - if err := closer.Close(); err != nil { - log.Printf("module %s (%s) close failed: %v", modObj.Name(), modObj.InstanceName(), err) - } - }) + if li, ok := modObj.(module.LifetimeModule); ok { + container.Global.Lifetime.Add(li) } return nil @@ -117,11 +111,11 @@ func ModuleFromNode(preferredNamespace string, args []string, inlineCfg config.N if len(args) != 1 || inlineCfg.Children != nil { return parser.NodeErr(inlineCfg, "exactly one argument is required to use existing config block") } - modObj, err = module.GetInstance(args[0][1:]) + modObj, err = container.Global.Modules.Get(args[0][1:]) log.Debugf("%s:%d: reference %s", inlineCfg.File, inlineCfg.Line, args[0]) } else { log.Debugf("%s:%d: new module %s %v", inlineCfg.File, inlineCfg.Line, args[0], args[1:]) - modObj, err = createInlineModule(preferredNamespace, args[0], args[1:]) + modObj, err = createInlineModule(preferredNamespace, args[0]) } if err != nil { return err @@ -144,7 +138,7 @@ func ModuleFromNode(preferredNamespace string, args []string, inlineCfg config.N reflect.ValueOf(moduleIface).Elem().Set(reflect.ValueOf(modObj)) if !referenceExisting { - if err := initInlineModule(modObj, globals, inlineCfg); err != nil { + if err := configureInlineModule(modObj, args[1:], globals, inlineCfg); err != nil { return err } } diff --git a/framework/container/container.go b/framework/container/container.go new file mode 100644 index 00000000..dc1e0124 --- /dev/null +++ b/framework/container/container.go @@ -0,0 +1,70 @@ +/* +Maddy Mail Server - Composable all-in-one email server. +Copyright © 2019-2020 Max Mazurov , Maddy Mail Server contributors + +This program 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. + +This program 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 . +*/ + +package container + +import ( + "github.com/foxcpp/maddy/framework/log" + "github.com/foxcpp/maddy/framework/module" +) + +type GlobalConfig struct { + // StateDirectory contains the path to the directory that + // should be used to store any data that should be + // preserved between sessions. + // + // Value of this variable must not change after initialization + // in cmd/maddy/main.go. + StateDirectory string + + // RuntimeDirectory contains the path to the directory that + // should be used to store any temporary data. + // + // It should be preferred over os.TempDir, which is + // global and world-readable on most systems, while + // RuntimeDirectory can be dedicated for maddy. + // + // Value of this variable must not change after initialization + // in cmd/maddy/main.go. + RuntimeDirectory string + + // LibexecDirectory contains the path to the directory + // where helper binaries should be searched. + // + // Value of this variable must not change after initialization + // in cmd/maddy/main.go. + LibexecDirectory string +} + +type C struct { + Config GlobalConfig + DefaultLogger log.Logger + Modules *module.Registry + Lifetime *module.LifetimeTracker +} + +func New() *C { + return &C{ + DefaultLogger: log.DefaultLogger, + Modules: module.NewRegistry(log.DefaultLogger.Sublogger("registry")), + Lifetime: module.NewLifetime(log.DefaultLogger.Sublogger("lifetime")), + } +} + +// Global is the default instance while refactoring is in progress. +var Global *C diff --git a/framework/log/log.go b/framework/log/log.go index f9092a44..da8647f6 100644 --- a/framework/log/log.go +++ b/framework/log/log.go @@ -224,6 +224,17 @@ func (l Logger) log(debug bool, s string) { // Logging is disabled - do nothing. } +func (l Logger) Sublogger(name string) Logger { + if l.Name != "" { + name = l.Name + "/" + name + } + return Logger{ + Out: l.Out, + Name: name, + Debug: l.Debug, + } +} + // DefaultLogger is the global Logger object that is used by // package-level logging functions. // diff --git a/framework/module/delivery_target.go b/framework/module/delivery_target.go index 9a7c1e09..40e5881a 100644 --- a/framework/module/delivery_target.go +++ b/framework/module/delivery_target.go @@ -33,12 +33,12 @@ import ( // Modules implementing this interface should be registered with "target." // prefix in name. type DeliveryTarget interface { - // Start starts the delivery of a new message. + // StartDelivery starts the delivery of a new message. // // The domain part of the MAIL FROM address is assumed to be U-labels with // NFC normalization and case-folding applied. The message source should // ensure that by calling address.CleanDomain if necessary. - Start(ctx context.Context, msgMeta *MsgMetadata, mailFrom string) (Delivery, error) + StartDelivery(ctx context.Context, msgMeta *MsgMetadata, mailFrom string) (Delivery, error) } type Delivery interface { @@ -54,7 +54,7 @@ type Delivery interface { // however. They should be silently ignored. // // Implementation should do as much checks as possible here and reject - // recipients that can't be used. Note: MsgMetadata object passed to Start + // recipients that can't be used. Note: MsgMetadata object passed to StartDelivery // contains BodyLength field. If it is non-zero, it can be used to check // storage quota for the user before Body. AddRcpt(ctx context.Context, rcptTo string, opts smtp.RcptOptions) error diff --git a/framework/module/dummy.go b/framework/module/dummy.go index 0930d4db..0722fb26 100644 --- a/framework/module/dummy.go +++ b/framework/module/dummy.go @@ -54,11 +54,11 @@ func (d *Dummy) InstanceName() string { return d.instName } -func (d *Dummy) Init(_ *config.Map) error { +func (d *Dummy) Configure(_ []string, _ *config.Map) error { return nil } -func (d *Dummy) Start(ctx context.Context, msgMeta *MsgMetadata, mailFrom string) (Delivery, error) { +func (d *Dummy) StartDelivery(ctx context.Context, msgMeta *MsgMetadata, mailFrom string) (Delivery, error) { return dummyDelivery{}, nil } @@ -81,7 +81,7 @@ func (dd dummyDelivery) Commit(ctx context.Context) error { } func init() { - Register("dummy", func(_, instName string, _, _ []string) (Module, error) { + Register("dummy", func(_, instName string) (Module, error) { return &Dummy{instName: instName}, nil }) } diff --git a/framework/module/instances.go b/framework/module/instances.go deleted file mode 100644 index aa6f1489..00000000 --- a/framework/module/instances.go +++ /dev/null @@ -1,105 +0,0 @@ -/* -Maddy Mail Server - Composable all-in-one email server. -Copyright © 2019-2020 Max Mazurov , Maddy Mail Server contributors - -This program 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. - -This program 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 . -*/ - -package module - -import ( - "fmt" - "io" - - "github.com/foxcpp/maddy/framework/config" - "github.com/foxcpp/maddy/framework/hooks" - "github.com/foxcpp/maddy/framework/log" -) - -var ( - instances = make(map[string]struct { - mod Module - cfg *config.Map - }) - aliases = make(map[string]string) - - Initialized = make(map[string]bool) -) - -// RegisterInstance adds module instance to the global registry. -// -// Instance name must be unique. Second RegisterInstance with same instance -// name will replace previous. -func RegisterInstance(inst Module, cfg *config.Map) { - instances[inst.InstanceName()] = struct { - mod Module - cfg *config.Map - }{inst, cfg} -} - -// RegisterAlias creates an association between a certain name and instance name. -// -// After RegisterAlias, module.GetInstance(aliasName) will return the same -// result as module.GetInstance(instName). -func RegisterAlias(aliasName, instName string) { - aliases[aliasName] = instName -} - -func HasInstance(name string) bool { - aliasedName := aliases[name] - if aliasedName != "" { - name = aliasedName - } - - _, ok := instances[name] - return ok -} - -// GetInstance returns module instance from global registry, initializing it if -// necessary. -// -// Error is returned if module initialization fails or module instance does not -// exists. -func GetInstance(name string) (Module, error) { - aliasedName := aliases[name] - if aliasedName != "" { - name = aliasedName - } - - mod, ok := instances[name] - if !ok { - return nil, fmt.Errorf("unknown config block: %s", name) - } - - // Break circular dependencies. - if Initialized[name] { - return mod.mod, nil - } - - Initialized[name] = true - if err := mod.mod.Init(mod.cfg); err != nil { - return mod.mod, err - } - - if closer, ok := mod.mod.(io.Closer); ok { - hooks.AddHook(hooks.EventShutdown, func() { - log.Debugf("close %s (%s)", mod.mod.Name(), mod.mod.InstanceName()) - if err := closer.Close(); err != nil { - log.Printf("module %s (%s) close failed: %v", mod.mod.Name(), mod.mod.InstanceName(), err) - } - }) - } - - return mod.mod, nil -} diff --git a/framework/module/lifetime.go b/framework/module/lifetime.go new file mode 100644 index 00000000..1b0339da --- /dev/null +++ b/framework/module/lifetime.go @@ -0,0 +1,121 @@ +/* +Maddy Mail Server - Composable all-in-one email server. +Copyright © 2019-2025 Max Mazurov , Maddy Mail Server contributors + +This program 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. + +This program 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 . +*/ + +package module + +import ( + "fmt" + + "github.com/foxcpp/maddy/framework/log" +) + +// LifetimeModule is a stateful module that needs to have post-configuration +// startup and graceful shutdown functionality. +type LifetimeModule interface { + Module + Start() error + Stop() error +} + +type ReloadModule interface { + Module + Reload() error +} + +type LifetimeTracker struct { + logger log.Logger + instances []*struct { + mod LifetimeModule + started bool + } +} + +func (lt *LifetimeTracker) Add(mod LifetimeModule) { + lt.instances = append(lt.instances, &struct { + mod LifetimeModule + started bool + }{mod: mod, started: false}) +} + +// StartAll calls Start for all registered LifetimeModule instances. +func (lt *LifetimeTracker) StartAll() error { + for _, entry := range lt.instances { + if entry.started { + continue + } + + if err := entry.mod.Start(); err != nil { + lt.StopAll() + return fmt.Errorf("failed to start module %v: %w", + entry.mod.InstanceName(), err) + } + lt.logger.DebugMsg("module started", + "mod_name", entry.mod.Name(), "inst_name", entry.mod.InstanceName()) + entry.started = true + } + return nil +} + +func (lt *LifetimeTracker) ReloadAll() error { + for _, entry := range lt.instances { + if !entry.started { + continue + } + + rm, ok := entry.mod.(ReloadModule) + if !ok { + continue + } + + if err := rm.Reload(); err != nil { + lt.logger.Error("module reload failed", err, + "mod_name", entry.mod.Name(), "inst_name", entry.mod.InstanceName()) + continue + } + + lt.logger.DebugMsg("module reloaded", + "mod_name", entry.mod.Name(), "inst_name", entry.mod.InstanceName()) + } + return nil +} + +// StopAll calls Stop for all registered LifetimeModule instances. +func (lt *LifetimeTracker) StopAll() error { + for _, entry := range lt.instances { + if !entry.started { + continue + } + + if err := entry.mod.Stop(); err != nil { + lt.logger.Error("module stop failed", err, + "mod_name", entry.mod.Name(), "inst_name", entry.mod.InstanceName()) + continue + } + lt.logger.DebugMsg("module stopped", + "mod_name", entry.mod.Name(), "inst_name", entry.mod.InstanceName()) + + entry.started = false + } + return nil +} + +func NewLifetime(log log.Logger) *LifetimeTracker { + return &LifetimeTracker{ + logger: log, + } +} diff --git a/framework/module/module.go b/framework/module/module.go index 2dbb45e9..2f949c14 100644 --- a/framework/module/module.go +++ b/framework/module/module.go @@ -38,22 +38,8 @@ import ( ) // Module is the interface implemented by all maddy module instances. -// -// It defines basic methods used to identify instances. -// -// Additionally, module can implement io.Closer if it needs to perform clean-up -// on shutdown. If module starts long-lived goroutines - they should be stopped -// *before* Close method returns to ensure graceful shutdown. type Module interface { - // Init performs actual initialization of the module. - // - // It is not done in FuncNewModule so all module instances are - // registered at time of initialization, thus initialization does not - // depends on ordering of configuration blocks and modules can reference - // each other without any problems. - // - // Module can use passed config.Map to read its configuration variables. - Init(*config.Map) error + Configure(inlineArgs []string, config *config.Map) error // Name method reports module name. // @@ -68,12 +54,10 @@ type Module interface { // FuncNewModule is function that creates new instance of module with specified name. // // Module.InstanceName() of the returned module object should return instName. -// aliases slice contains other names that can be used to reference created -// module instance. +// If module is defined inline, instName will be empty. // -// If module is defined inline, instName will be empty and all values -// specified after module name in configuration will be in inlineArgs. -type FuncNewModule func(modName, instName string, aliases, inlineArgs []string) (Module, error) +// Returned Module may additionally implement LifetimeModule. +type FuncNewModule func(modName, instName string) (Module, error) // FuncNewEndpoint is a function that creates new instance of endpoint // module. @@ -87,4 +71,4 @@ type FuncNewModule func(modName, instName string, aliases, inlineArgs []string) // // As a consequence of having no per-instance name, InstanceName of the module // object always returns the same value as Name. -type FuncNewEndpoint func(modName string, addrs []string) (Module, error) +type FuncNewEndpoint func(modName string, addrs []string) (LifetimeModule, error) diff --git a/framework/module/modules.go b/framework/module/modules.go new file mode 100644 index 00000000..d7669808 --- /dev/null +++ b/framework/module/modules.go @@ -0,0 +1,96 @@ +/* +Maddy Mail Server - Composable all-in-one email server. +Copyright © 2019-2020 Max Mazurov , Maddy Mail Server contributors + +This program 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. + +This program 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 . +*/ + +package module + +import ( + "sync" + + "github.com/foxcpp/maddy/framework/log" +) + +var ( + modules = make(map[string]FuncNewModule) + endpoints = make(map[string]FuncNewEndpoint) + modulesLock sync.RWMutex +) + +// Register adds module factory function to global registry. +// +// name must be unique. Register will panic if module with specified name +// already exists in registry. +// +// You probably want to call this function from func init() of module package. +func Register(name string, factory FuncNewModule) { + modulesLock.Lock() + defer modulesLock.Unlock() + + if _, ok := modules[name]; ok { + panic("Register: module with specified name is already registered: " + name) + } + + modules[name] = factory +} + +// RegisterDeprecated adds module factory function to global registry. +// +// It prints warning to the log about name being deprecated and suggests using +// a new name. +func RegisterDeprecated(name, newName string, factory FuncNewModule) { + Register(name, func(modName, instName string) (Module, error) { + log.Printf("module initialized via deprecated name %s, %s should be used instead; deprecated name may be removed in the next version", name, newName) + return factory(modName, instName) + }) +} + +// Get returns module from global registry. +// +// This function does not return endpoint-type modules, use GetEndpoint for +// that. +// Nil is returned if no module with specified name is registered. +func Get(name string) FuncNewModule { + modulesLock.RLock() + defer modulesLock.RUnlock() + + return modules[name] +} + +// GetEndpoint returns an endpoint module from global registry. +// +// Nil is returned if no module with specified name is registered. +func GetEndpoint(name string) FuncNewEndpoint { + modulesLock.RLock() + defer modulesLock.RUnlock() + + return endpoints[name] +} + +// RegisterEndpoint registers an endpoint module. +// +// See FuncNewEndpoint for information about +// differences of endpoint modules from regular modules. +func RegisterEndpoint(name string, factory FuncNewEndpoint) { + modulesLock.Lock() + defer modulesLock.Unlock() + + if _, ok := endpoints[name]; ok { + panic("Register: module with specified name is already registered: " + name) + } + + endpoints[name] = factory +} diff --git a/framework/module/mxauth.go b/framework/module/mxauth.go index 5226fb02..ac2167ca 100644 --- a/framework/module/mxauth.go +++ b/framework/module/mxauth.go @@ -96,7 +96,7 @@ type ( // Modules implementing this interface should be registered with "mx_auth." // prefix in name. MXAuthPolicy interface { - Start(*MsgMetadata) DeliveryMXAuthPolicy + StartDelivery(*MsgMetadata) DeliveryMXAuthPolicy // Weight is an integer in range 0-1000 that represents relative // ordering of policy application. diff --git a/framework/module/partial_delivery.go b/framework/module/partial_delivery.go index beeb46e8..fa1f7471 100644 --- a/framework/module/partial_delivery.go +++ b/framework/module/partial_delivery.go @@ -45,7 +45,7 @@ type StatusCollector interface { } // PartialDelivery is an optional interface that may be implemented -// by the object returned by DeliveryTarget.Start. See PartialDelivery.BodyNonAtomic +// by the object returned by DeliveryTarget.StartDelivery. See PartialDelivery.BodyNonAtomic // documentation for details. type PartialDelivery interface { // BodyNonAtomic is similar to Body method of the regular Delivery interface diff --git a/framework/module/registry.go b/framework/module/registry.go index c52210f8..ec62edf7 100644 --- a/framework/module/registry.go +++ b/framework/module/registry.go @@ -19,85 +19,129 @@ along with this program. If not, see . package module import ( - "sync" + "errors" "github.com/foxcpp/maddy/framework/log" ) var ( - // NoRun makes sure modules do not start any bacground tests. - // - // If it set - modules should not perform any actual work and should stop - // once the configuration is read and verified to be correct. - // TODO: Replace it with separation of Init and Run at interface level. - NoRun = false - - modules = make(map[string]FuncNewModule) - endpoints = make(map[string]FuncNewEndpoint) - modulesLock sync.RWMutex + ErrInstanceNameDuplicate = errors.New("instance name already registered") + ErrInstanceUnknown = errors.New("no such instance registered") ) -// Register adds module factory function to global registry. -// -// name must be unique. Register will panic if module with specified name -// already exists in registry. -// -// You probably want to call this function from func init() of module package. -func Register(name string, factory FuncNewModule) { - modulesLock.Lock() - defer modulesLock.Unlock() +type registryEntry struct { + Mod Module + LazyInit func() error +} - if _, ok := modules[name]; ok { - panic("Register: module with specified name is already registered: " + name) - } +type Registry struct { + logger log.Logger + instances map[string]registryEntry + initialized map[string]struct{} + started map[string]struct{} + aliases map[string]string +} - modules[name] = factory +func NewRegistry(log log.Logger) *Registry { + return &Registry{ + logger: log, + instances: make(map[string]registryEntry), + initialized: make(map[string]struct{}), + started: make(map[string]struct{}), + aliases: make(map[string]string), + } } -// RegisterDeprecated adds module factory function to global registry. +// Register adds not-initialized (configured) module into registry. // -// It prints warning to the log about name being deprecated and suggests using -// a new name. -func RegisterDeprecated(name, newName string, factory FuncNewModule) { - Register(name, func(modName, instName string, aliases, inlineArgs []string) (Module, error) { - log.Printf("module initialized via deprecated name %s, %s should be used instead; deprecated name may be removed in the next version", name, newName) - return factory(modName, instName, aliases, inlineArgs) - }) +// lazyInit function will be called on first request to get the module from +// registry. +func (r *Registry) Register(mod Module, lazyInit func() error) error { + instName := mod.InstanceName() + if instName == "" { + panic("module with empty instance name cannot be added to the registry") + } + + _, ok := r.instances[instName] + if ok { + return ErrInstanceNameDuplicate + } + + r.instances[instName] = registryEntry{ + Mod: mod, + LazyInit: lazyInit, + } + return nil } -// Get returns module from global registry. -// -// This function does not return endpoint-type modules, use GetEndpoint for -// that. -// Nil is returned if no module with specified name is registered. -func Get(name string) FuncNewModule { - modulesLock.RLock() - defer modulesLock.RUnlock() - - return modules[name] +func (r *Registry) AddAlias(instanceName string, alias string) error { + if instanceName == "" { + panic("cannot add an alias for empty instance name") + } + if alias == "" { + panic("cannot add an empty alias") + } + _, ok := r.aliases[alias] + if ok { + return ErrInstanceNameDuplicate + } + _, ok = r.instances[instanceName] + if ok { + return ErrInstanceNameDuplicate + } + + r.aliases[alias] = instanceName + return nil } -// GetEndpoints returns an endpoint module from global registry. -// -// Nil is returned if no module with specified name is registered. -func GetEndpoint(name string) FuncNewEndpoint { - modulesLock.RLock() - defer modulesLock.RUnlock() +func (r *Registry) ensureInitialized(name string, entry *registryEntry) error { + _, ok := r.initialized[name] + if ok { + return nil + } + if entry.LazyInit == nil { + return nil + } + + r.logger.DebugMsg("module configure", + "mod_name", entry.Mod.Name(), "inst_name", entry.Mod.InstanceName()) + err := entry.LazyInit() + if err != nil { + return err + } + r.initialized[name] = struct{}{} - return endpoints[name] + return nil } -// RegisterEndpoint registers an endpoint module. -// -// See FuncNewEndpoint for information about -// differences of endpoint modules from regular modules. -func RegisterEndpoint(name string, factory FuncNewEndpoint) { - modulesLock.Lock() - defer modulesLock.Unlock() +func (r *Registry) Get(name string) (Module, error) { + if name == "" { + panic("cannot get module with empty name") + } + aliasedName := r.aliases[name] + if aliasedName != "" { + name = aliasedName + } + + mod, ok := r.instances[name] + if !ok { + return nil, ErrInstanceUnknown + } - if _, ok := endpoints[name]; ok { - panic("Register: module with specified name is already registered: " + name) + if err := r.ensureInitialized(name, &mod); err != nil { + return nil, err } - endpoints[name] = factory + return mod.Mod, nil +} + +func (r *Registry) NotInitialized() []Module { + notinit := make([]Module, 0, len(r.instances)-len(r.initialized)) + for name, mod := range r.instances { + if _, ok := r.initialized[name]; ok { + continue + } + notinit = append(notinit, mod.Mod) + } + return notinit } diff --git a/internal/auth/dovecot_sasl/dovecot_sasl.go b/internal/auth/dovecot_sasl/dovecot_sasl.go index c7dd6cc9..cc5dd370 100644 --- a/internal/auth/dovecot_sasl/dovecot_sasl.go +++ b/internal/auth/dovecot_sasl/dovecot_sasl.go @@ -44,20 +44,12 @@ type Auth struct { const modName = "dovecot_sasl" -func New(_, instName string, _, inlineArgs []string) (module.Module, error) { +func New(_, instName string) (module.Module, error) { a := &Auth{ instName: instName, log: log.Logger{Name: modName, Debug: log.DefaultLogger.Debug}, } - switch len(inlineArgs) { - case 0: - case 1: - a.serverEndpoint = inlineArgs[0] - default: - return nil, fmt.Errorf("%s: one or none arguments needed", modName) - } - return a, nil } @@ -88,7 +80,15 @@ func (a *Auth) returnConn(cl *dovecotsasl.Client) { cl.Close() } -func (a *Auth) Init(cfg *config.Map) error { +func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { + switch len(inlineArgs) { + case 0: + case 1: + a.serverEndpoint = inlineArgs[0] + default: + return fmt.Errorf("%s: one or none arguments needed", modName) + } + cfg.String("endpoint", false, false, a.serverEndpoint, &a.serverEndpoint) if _, err := cfg.Process(); err != nil { return err diff --git a/internal/auth/external/externalauth.go b/internal/auth/external/externalauth.go index 59d71fb3..144864da 100644 --- a/internal/auth/external/externalauth.go +++ b/internal/auth/external/externalauth.go @@ -41,17 +41,13 @@ type ExternalAuth struct { Log log.Logger } -func NewExternalAuth(modName, instName string, _, inlineArgs []string) (module.Module, error) { +func NewExternalAuth(modName, instName string) (module.Module, error) { ea := &ExternalAuth{ modName: modName, instName: instName, Log: log.Logger{Name: modName}, } - if len(inlineArgs) != 0 { - return nil, errors.New("external: inline arguments are not used") - } - return ea, nil } @@ -63,7 +59,11 @@ func (ea *ExternalAuth) InstanceName() string { return ea.instName } -func (ea *ExternalAuth) Init(cfg *config.Map) error { +func (ea *ExternalAuth) Configure(inlineArgs []string, cfg *config.Map) error { + if len(inlineArgs) != 0 { + return errors.New("external: inline arguments are not used") + } + cfg.Bool("debug", false, false, &ea.Log.Debug) cfg.Bool("perdomain", false, false, &ea.perDomain) cfg.StringList("domains", false, false, nil, &ea.domains) diff --git a/internal/auth/ldap/ldap.go b/internal/auth/ldap/ldap.go index 04cfe9f9..a2392d56 100644 --- a/internal/auth/ldap/ldap.go +++ b/internal/auth/ldap/ldap.go @@ -40,15 +40,16 @@ type Auth struct { log log.Logger } -func New(modName, instName string, _, inlineArgs []string) (module.Module, error) { +func New(modName, instName string) (module.Module, error) { return &Auth{ instName: instName, log: log.Logger{Name: modName}, - urls: inlineArgs, }, nil } -func (a *Auth) Init(cfg *config.Map) error { +func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { + a.urls = inlineArgs + a.dialer = &net.Dialer{} cfg.Bool("debug", true, false, &a.log.Debug) @@ -87,15 +88,6 @@ func (a *Auth) Init(cfg *config.Map) error { } } - if module.NoRun { - return nil - } - - var err error - a.conn, err = a.newConn() - if err != nil { - return fmt.Errorf("auth.ldap: %w", err) - } return nil } @@ -281,6 +273,22 @@ func (a *Auth) AuthPlain(username, password string) error { return nil } +func (a *Auth) Start() error { + var err error + a.conn, err = a.newConn() + if err != nil { + return fmt.Errorf("auth.ldap: %w", err) + } + return nil +} + +func (a *Auth) Stop() error { + a.connLock.Lock() + defer a.connLock.Unlock() + a.conn.Close() + return nil +} + func init() { var _ module.PlainAuth = &Auth{} var _ module.Table = &Auth{} diff --git a/internal/auth/netauth/netauth.go b/internal/auth/netauth/netauth.go index 3348eefe..62d6c6cc 100644 --- a/internal/auth/netauth/netauth.go +++ b/internal/auth/netauth/netauth.go @@ -31,15 +31,17 @@ type Auth struct { } // New creates a new instance of the NetAuth module. -func New(modName, instName string, _, inlineArgs []string) (module.Module, error) { +func New(modName, instName string) (module.Module, error) { return &Auth{ instName: instName, log: log.Logger{Name: modName}, }, nil } +func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { + if len(inlineArgs)> 0 { + return fmt.Errorf("%s: inline arguments are not used", modName) + } -// Init performs deferred initialization actions. -func (a *Auth) Init(cfg *config.Map) error { l := hclog.New(&hclog.LoggerOptions{Output: a.log}) n, err := netauth.NewWithLog(l) if err != nil { @@ -53,8 +55,6 @@ func (a *Auth) Init(cfg *config.Map) error { return err } - a.log.Debugln("Debug logging enabled") - a.log.Debugf("mustGroups status: %s", a.mustGroup) return nil } diff --git a/internal/auth/pam/module.go b/internal/auth/pam/module.go index c93269d3..da8e59f9 100644 --- a/internal/auth/pam/module.go +++ b/internal/auth/pam/module.go @@ -38,10 +38,7 @@ type Auth struct { Log log.Logger } -func New(modName, instName string, _, inlineArgs []string) (module.Module, error) { - if len(inlineArgs) != 0 { - return nil, errors.New("pam: inline arguments are not used") - } +func New(modName, instName string) (module.Module, error) { return &Auth{ instName: instName, Log: log.Logger{Name: modName}, @@ -56,7 +53,11 @@ func (a *Auth) InstanceName() string { return a.instName } -func (a *Auth) Init(cfg *config.Map) error { +func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { + if len(inlineArgs) != 0 { + return errors.New("pam: inline arguments are not used") + } + cfg.Bool("debug", true, false, &a.Log.Debug) cfg.Bool("use_helper", false, false, &a.useHelper) if _, err := cfg.Process(); err != nil { diff --git a/internal/auth/pass_table/table.go b/internal/auth/pass_table/table.go index 4d0e3137..626913b4 100644 --- a/internal/auth/pass_table/table.go +++ b/internal/auth/pass_table/table.go @@ -31,24 +31,22 @@ import ( ) type Auth struct { - modName string - instName string - inlineArgs []string + modName string + instName string table module.Table } -func New(modName, instName string, _, inlineArgs []string) (module.Module, error) { +func New(modName, instName string) (module.Module, error) { return &Auth{ - modName: modName, - instName: instName, - inlineArgs: inlineArgs, + modName: modName, + instName: instName, }, nil } -func (a *Auth) Init(cfg *config.Map) error { - if len(a.inlineArgs) != 0 { - return modconfig.ModuleFromNode("table", a.inlineArgs, cfg.Block, cfg.Globals, &a.table) +func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { + if len(inlineArgs) != 0 { + return modconfig.ModuleFromNode("table", inlineArgs, cfg.Block, cfg.Globals, &a.table) } cfg.Custom("table", false, true, nil, modconfig.TableDirective, &a.table) diff --git a/internal/auth/pass_table/table_test.go b/internal/auth/pass_table/table_test.go index 666e2b60..7f688cac 100644 --- a/internal/auth/pass_table/table_test.go +++ b/internal/auth/pass_table/table_test.go @@ -28,11 +28,11 @@ import ( func TestAuth_AuthPlain(t *testing.T) { addSHA256() - mod, err := New("pass_table", "", nil, []string{"dummy"}) + mod, err := New("pass_table", "") if err != nil { t.Fatal(err) } - err = mod.Init(config.NewMap(nil, config.Node{ + err = mod.Configure([]string{"dummy"}, config.NewMap(nil, config.Node{ Children: []config.Node{}, })) if err != nil { diff --git a/internal/auth/plain_separate/plain_separate.go b/internal/auth/plain_separate/plain_separate.go index 893d1f01..b3e06016 100644 --- a/internal/auth/plain_separate/plain_separate.go +++ b/internal/auth/plain_separate/plain_separate.go @@ -41,7 +41,7 @@ type Auth struct { Log log.Logger } -func NewAuth(modName, instName string, _, inlinargs []string) (module.Module, error) { +func NewAuth(modName, instName string) (module.Module, error) { a := &Auth{ modName: modName, instName: instName, @@ -49,10 +49,6 @@ func NewAuth(modName, instName string, _, inlinargs []string) (module.Module, er Log: log.Logger{Name: modName}, } - if len(inlinargs) != 0 { - return nil, errors.New("plain_separate: inline arguments are not used") - } - return a, nil } @@ -64,7 +60,11 @@ func (a *Auth) InstanceName() string { return a.instName } -func (a *Auth) Init(cfg *config.Map) error { +func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { + if len(inlineArgs) != 0 { + return errors.New("plain_separate: inline arguments are not used") + } + cfg.Bool("debug", false, false, &a.Log.Debug) cfg.Callback("user", func(m *config.Map, node config.Node) error { var tbl module.Table diff --git a/internal/auth/shadow/module.go b/internal/auth/shadow/module.go index 92307bfc..290d826e 100644 --- a/internal/auth/shadow/module.go +++ b/internal/auth/shadow/module.go @@ -41,10 +41,7 @@ type Auth struct { Log log.Logger } -func New(modName, instName string, _, inlineArgs []string) (module.Module, error) { - if len(inlineArgs) != 0 { - return nil, errors.New("shadow: inline arguments are not used") - } +func New(modName, instName string) (module.Module, error) { return &Auth{ instName: instName, Log: log.Logger{Name: modName}, @@ -59,7 +56,11 @@ func (a *Auth) InstanceName() string { return a.instName } -func (a *Auth) Init(cfg *config.Map) error { +func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { + if len(inlineArgs) != 0 { + return errors.New("shadow: inline arguments are not used") + } + cfg.Bool("debug", true, false, &a.Log.Debug) cfg.Bool("use_helper", false, false, &a.useHelper) if _, err := cfg.Process(); err != nil { diff --git a/internal/check/authorize_sender/authorize_sender.go b/internal/check/authorize_sender/authorize_sender.go index 6827add2..ab091c56 100644 --- a/internal/check/authorize_sender/authorize_sender.go +++ b/internal/check/authorize_sender/authorize_sender.go @@ -20,6 +20,7 @@ package authorize_sender import ( "context" + "fmt" "net/mail" "github.com/emersion/go-message/textproto" @@ -52,7 +53,7 @@ type Check struct { authNorm authz.NormalizeFunc } -func New(_, instName string, _, inlineArgs []string) (module.Module, error) { +func New(_, instName string) (module.Module, error) { return &Check{ instName: instName, }, nil @@ -66,7 +67,11 @@ func (c *Check) InstanceName() string { return c.instName } -func (c *Check) Init(cfg *config.Map) error { +func (c *Check) Configure(inlineArgs []string, cfg *config.Map) error { + if len(inlineArgs) != 0 { + return fmt.Errorf("%s: inline arguments are not used", modName) + } + cfg.Bool("debug", true, false, &c.log.Debug) cfg.Bool("check_header", false, true, &c.checkHeader) diff --git a/internal/check/command/command.go b/internal/check/command/command.go index 60937607..512f147a 100644 --- a/internal/check/command/command.go +++ b/internal/check/command/command.go @@ -66,7 +66,7 @@ type Check struct { cmdArgs []string } -func New(modName, instName string, aliases, inlineArgs []string) (module.Module, error) { +func New(modName, instName string) (module.Module, error) { c := &Check{ instName: instName, actions: map[int]modconfig.FailAction{ @@ -79,13 +79,6 @@ func New(modName, instName string, aliases, inlineArgs []string) (module.Module, }, } - if len(inlineArgs) == 0 { - return nil, errors.New("command: at least one argument is required (command name)") - } - - c.cmd = inlineArgs[0] - c.cmdArgs = inlineArgs[1:] - return c, nil } @@ -97,7 +90,14 @@ func (c *Check) InstanceName() string { return c.instName } -func (c *Check) Init(cfg *config.Map) error { +func (c *Check) Configure(inlineArgs []string, cfg *config.Map) error { + if len(inlineArgs) == 0 { + return errors.New("command: at least one argument is required (command name)") + } + + c.cmd = inlineArgs[0] + c.cmdArgs = inlineArgs[1:] + // Check whether the inline argument command is usable. if _, err := exec.LookPath(c.cmd); err != nil { return fmt.Errorf("command: %w", err) diff --git a/internal/check/dkim/dkim.go b/internal/check/dkim/dkim.go index 563fc5b1..b6921d40 100644 --- a/internal/check/dkim/dkim.go +++ b/internal/check/dkim/dkim.go @@ -52,10 +52,7 @@ type Check struct { resolver dns.Resolver } -func New(_, instName string, _, inlineArgs []string) (module.Module, error) { - if len(inlineArgs) != 0 { - return nil, errors.New("check.dkim: inline arguments are not used") - } +func New(_, instName string) (module.Module, error) { return &Check{ instName: instName, log: log.Logger{Name: "check.dkim"}, @@ -63,7 +60,11 @@ func New(_, instName string, _, inlineArgs []string) (module.Module, error) { }, nil } -func (c *Check) Init(cfg *config.Map) error { +func (c *Check) Configure(inlineArgs []string, cfg *config.Map) error { + if len(inlineArgs) != 0 { + return errors.New("check.dkim: inline arguments are not used") + } + var requiredFields []string cfg.Bool("debug", true, false, &c.log.Debug) diff --git a/internal/check/dkim/dkim_test.go b/internal/check/dkim/dkim_test.go index 020d50f5..b4054124 100644 --- a/internal/check/dkim/dkim_test.go +++ b/internal/check/dkim/dkim_test.go @@ -84,7 +84,7 @@ Joe. func testCheck(t *testing.T, zones map[string]mockdns.Zone, cfg []config.Node) *Check { t.Helper() - mod, err := New("check.dkim", "", nil, nil) + mod, err := New("check.dkim", "") if err != nil { t.Fatal(err) } @@ -92,7 +92,7 @@ func testCheck(t *testing.T, zones map[string]mockdns.Zone, cfg []config.Node) * check.resolver = &mockdns.Resolver{Zones: zones} check.log = testutils.Logger(t, mod.Name()) - if err := check.Init(config.NewMap(nil, config.Node{Children: cfg})); err != nil { + if err := check.Configure(nil, config.NewMap(nil, config.Node{Children: cfg})); err != nil { t.Fatal(err) } diff --git a/internal/check/dnsbl/dnsbl.go b/internal/check/dnsbl/dnsbl.go index 2c91c838..79738720 100644 --- a/internal/check/dnsbl/dnsbl.go +++ b/internal/check/dnsbl/dnsbl.go @@ -58,7 +58,6 @@ var defaultBL = List{ type DNSBL struct { instName string checkEarly bool - inlineBls []string bls []List quarantineThres int @@ -68,10 +67,9 @@ type DNSBL struct { log log.Logger } -func NewDNSBL(_, instName string, _, inlineArgs []string) (module.Module, error) { +func New(_, instName string) (module.Module, error) { return &DNSBL{ - instName: instName, - inlineBls: inlineArgs, + instName: instName, resolver: dns.DefaultResolver(), log: log.Logger{Name: "dnsbl"}, @@ -86,7 +84,7 @@ func (bl *DNSBL) InstanceName() string { return bl.instName } -func (bl *DNSBL) Init(cfg *config.Map) error { +func (bl *DNSBL) Configure(inlineArgs []string, cfg *config.Map) error { cfg.Bool("debug", false, false, &bl.log.Debug) cfg.Bool("check_early", false, false, &bl.checkEarly) cfg.Int("quarantine_threshold", false, false, 1, &bl.quarantineThres) @@ -97,7 +95,7 @@ func (bl *DNSBL) Init(cfg *config.Map) error { return err } - for _, inlineBl := range bl.inlineBls { + for _, inlineBl := range inlineArgs { cfg := defaultBL cfg.Zone = inlineBl go bl.testList(cfg) @@ -431,5 +429,5 @@ func (*state) Close() error { } func init() { - module.Register("check.dnsbl", NewDNSBL) + module.Register("check.dnsbl", New) } diff --git a/internal/check/milter/milter.go b/internal/check/milter/milter.go index 37704d43..38f81930 100644 --- a/internal/check/milter/milter.go +++ b/internal/check/milter/milter.go @@ -46,18 +46,12 @@ type Check struct { log log.Logger } -func New(_, instName string, _, inlineArgs []string) (module.Module, error) { +func New(_, instName string) (module.Module, error) { c := &Check{ instName: instName, log: log.Logger{Name: modName, Debug: log.DefaultLogger.Debug}, } - switch len(inlineArgs) { - case 1: - c.milterUrl = inlineArgs[0] - case 0: - default: - return nil, fmt.Errorf("%s: unexpected amount of arguments, want 1 or 0", modName) - } + return c, nil } @@ -69,7 +63,15 @@ func (c *Check) InstanceName() string { return c.instName } -func (c *Check) Init(cfg *config.Map) error { +func (c *Check) Configure(inlineArgs []string, cfg *config.Map) error { + switch len(inlineArgs) { + case 1: + c.milterUrl = inlineArgs[0] + case 0: + default: + return fmt.Errorf("%s: unexpected amount of arguments, want 1 or 0", modName) + } + cfg.String("endpoint", false, false, c.milterUrl, &c.milterUrl) cfg.Bool("fail_open", false, false, &c.failOpen) if _, err := cfg.Process(); err != nil { diff --git a/internal/check/milter/milter_test.go b/internal/check/milter/milter_test.go index 97978517..50e3954e 100644 --- a/internal/check/milter/milter_test.go +++ b/internal/check/milter/milter_test.go @@ -38,7 +38,7 @@ func TestAcceptValidEndpoints(t *testing.T) { } { c := &Check{milterUrl: endpoint} - err := c.Init(&config.Map{}) + err := c.Configure(nil, &config.Map{}) if err != nil { t.Errorf("Unexpected failure for %s: %v", endpoint, err) return @@ -52,7 +52,7 @@ func TestRejectInvalidEndpoints(t *testing.T) { "tls:0.0.0.0:10025", } { c := &Check{milterUrl: endpoint} - err := c.Init(&config.Map{}) + err := c.Configure(nil, &config.Map{}) if err == nil { t.Errorf("Accepted invalid endpoint: %s", endpoint) return diff --git a/internal/check/rspamd/rspamd.go b/internal/check/rspamd/rspamd.go index e6afad68..559c94b6 100644 --- a/internal/check/rspamd/rspamd.go +++ b/internal/check/rspamd/rspamd.go @@ -61,22 +61,13 @@ type Check struct { client *http.Client } -func New(modName, instName string, _, inlineArgs []string) (module.Module, error) { +func New(modName, instName string) (module.Module, error) { c := &Check{ instName: instName, client: http.DefaultClient, log: log.Logger{Name: modName, Debug: log.DefaultLogger.Debug}, } - switch len(inlineArgs) { - case 1: - c.apiPath = inlineArgs[0] - case 0: - c.apiPath = "http://127.0.0.1:11333" - default: - return nil, fmt.Errorf("%s: unexpected amount of inline arguments", modName) - } - return c, nil } @@ -88,7 +79,16 @@ func (c *Check) InstanceName() string { return c.instName } -func (c *Check) Init(cfg *config.Map) error { +func (c *Check) Configure(inlineArgs []string, cfg *config.Map) error { + switch len(inlineArgs) { + case 1: + c.apiPath = inlineArgs[0] + case 0: + c.apiPath = "http://127.0.0.1:11333" + default: + return fmt.Errorf("%s: unexpected amount of inline arguments", modName) + } + var ( tlsConfig tls.Config flags []string diff --git a/internal/check/spf/spf.go b/internal/check/spf/spf.go index c94dd915..a799cd85 100644 --- a/internal/check/spf/spf.go +++ b/internal/check/spf/spf.go @@ -60,7 +60,7 @@ type Check struct { resolver dns.Resolver } -func New(_, instName string, _, _ []string) (module.Module, error) { +func New(_, instName string) (module.Module, error) { return &Check{ instName: instName, log: log.Logger{Name: modName}, @@ -76,7 +76,7 @@ func (c *Check) InstanceName() string { return c.instName } -func (c *Check) Init(cfg *config.Map) error { +func (c *Check) Configure(inlineArgs []string, cfg *config.Map) error { cfg.Bool("debug", true, false, &c.log.Debug) cfg.Bool("enforce_early", true, false, &c.enforceEarly) cfg.Custom("none_action", false, false, diff --git a/internal/check/stateless_check.go b/internal/check/stateless_check.go index 4c5d2d56..729c106b 100644 --- a/internal/check/stateless_check.go +++ b/internal/check/stateless_check.go @@ -152,7 +152,11 @@ func (c *statelessCheck) CheckStateForMsg(ctx context.Context, msgMeta *module.M }, nil } -func (c *statelessCheck) Init(cfg *config.Map) error { +func (c *statelessCheck) Configure(inlineArgs []string, cfg *config.Map) error { + if len(inlineArgs) != 0 { + return fmt.Errorf("%s: inline arguments are not used", c.modName) + } + cfg.Bool("debug", true, false, &c.logger.Debug) cfg.Custom("fail_action", false, false, func() (interface{}, error) { @@ -181,10 +185,7 @@ func (c *statelessCheck) InstanceName() string { // code doesn't need to know about it. It should assume that it is always "Reject" and hence it should // populate Reason field of the result object with the relevant error description. func RegisterStatelessCheck(name string, defaultFailAction modconfig.FailAction, connCheck FuncConnCheck, senderCheck FuncSenderCheck, rcptCheck FuncRcptCheck, bodyCheck FuncBodyCheck) { - module.Register(name, func(modName, instName string, aliases, inlineArgs []string) (module.Module, error) { - if len(inlineArgs) != 0 { - return nil, fmt.Errorf("%s: inline arguments are not used", modName) - } + module.Register(name, func(modName, instName string) (module.Module, error) { return &statelessCheck{ modName: modName, instName: instName, diff --git a/internal/cli/ctl/moduleinit.go b/internal/cli/ctl/moduleinit.go index 23e79e5c..ef58d7b4 100644 --- a/internal/cli/ctl/moduleinit.go +++ b/internal/cli/ctl/moduleinit.go @@ -25,9 +25,7 @@ import ( "os" "github.com/foxcpp/maddy" - parser "github.com/foxcpp/maddy/framework/cfgparser" - "github.com/foxcpp/maddy/framework/config" - "github.com/foxcpp/maddy/framework/hooks" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/updatepipe" "github.com/urfave/cli/v2" @@ -39,71 +37,61 @@ func closeIfNeeded(i interface{}) { } } -func getCfgBlockModule(ctx *cli.Context) (map[string]interface{}, *maddy.ModInfo, error) { +func getCfgBlockModule(ctx *cli.Context) (*container.C, module.Module, error) { cfgPath := ctx.String("config") if cfgPath == "" { return nil, nil, cli.Exit("Error: config is required", 2) } - cfgFile, err := os.Open(cfgPath) + + c := container.New() + + cfg, err := maddy.ReadConfig(cfgPath) if err != nil { return nil, nil, cli.Exit(fmt.Sprintf("Error: failed to open config: %v", err), 2) } - defer cfgFile.Close() - cfgNodes, err := parser.Read(cfgFile, cfgFile.Name()) - if err != nil { - return nil, nil, cli.Exit(fmt.Sprintf("Error: failed to parse config: %v", err), 2) - } - globals, cfgNodes, err := maddy.ReadGlobals(cfgNodes) + globals, cfgNodes, err := maddy.ReadGlobals(c, cfg) if err != nil { return nil, nil, err } - if err := maddy.InitDirs(); err != nil { + if err := maddy.InitDirs(c); err != nil { return nil, nil, err } - module.NoRun = true - _, mods, err := maddy.RegisterModules(globals, cfgNodes) + err = maddy.RegisterModules(c, globals, cfgNodes) if err != nil { return nil, nil, err } - defer hooks.RunHooks(hooks.EventShutdown) cfgBlock := ctx.String("cfg-block") if cfgBlock == "" { return nil, nil, cli.Exit("Error: cfg-block is required", 2) } - var mod maddy.ModInfo - for _, m := range mods { - if m.Instance.InstanceName() == cfgBlock { - mod = m - break + + mod, err := c.Modules.Get(cfgBlock) + if err != nil { + if errors.Is(err, module.ErrInstanceUnknown) { + return nil, nil, cli.Exit(fmt.Sprintf("Error: unknown configuration block: %s", cfgBlock), 2) } - } - if mod.Instance == nil { - return nil, nil, cli.Exit(fmt.Sprintf("Error: unknown configuration block: %s", cfgBlock), 2) + return nil, nil, err } - return globals, &mod, nil + return c, mod, nil } func openStorage(ctx *cli.Context) (module.Storage, error) { - globals, mod, err := getCfgBlockModule(ctx) + _, mod, err := getCfgBlockModule(ctx) if err != nil { return nil, err } - storage, ok := mod.Instance.(module.Storage) + storage, ok := mod.(module.Storage) if !ok { return nil, cli.Exit(fmt.Sprintf("Error: configuration block %s is not an IMAP storage", ctx.String("cfg-block")), 2) } - if err := mod.Instance.Init(config.NewMap(globals, mod.Cfg)); err != nil { - return nil, fmt.Errorf("Error: module initialization failed: %w", err) - } - - if updStore, ok := mod.Instance.(updatepipe.Backend); ok { + if updStore, ok := mod.(updatepipe.Backend); ok { if err := updStore.EnableUpdatePipe(updatepipe.ModePush); err != nil && !errors.Is(err, os.ErrNotExist) { fmt.Fprintf(os.Stderr, "Failed to initialize update pipe, do not remove messages from mailboxes open by clients: %v\n", err) } @@ -115,19 +103,15 @@ func openStorage(ctx *cli.Context) (module.Storage, error) { } func openUserDB(ctx *cli.Context) (module.PlainUserDB, error) { - globals, mod, err := getCfgBlockModule(ctx) + _, mod, err := getCfgBlockModule(ctx) if err != nil { return nil, err } - userDB, ok := mod.Instance.(module.PlainUserDB) + userDB, ok := mod.(module.PlainUserDB) if !ok { return nil, cli.Exit(fmt.Sprintf("Error: configuration block %s is not a local credentials store", ctx.String("cfg-block")), 2) } - if err := mod.Instance.Init(config.NewMap(globals, mod.Cfg)); err != nil { - return nil, fmt.Errorf("Error: module initialization failed: %w", err) - } - return userDB, nil } diff --git a/internal/endpoint/dovecot_sasld/dovecot_sasl.go b/internal/endpoint/dovecot_sasld/dovecot_sasl.go index 77eedd0e..b1bcd159 100644 --- a/internal/endpoint/dovecot_sasld/dovecot_sasl.go +++ b/internal/endpoint/dovecot_sasld/dovecot_sasl.go @@ -42,12 +42,13 @@ type Endpoint struct { log log.Logger saslAuth auth.SASLAuth + endpoints []config.Endpoint listenersWg sync.WaitGroup srv *dovecotsasl.Server } -func New(_ string, addrs []string) (module.Module, error) { +func New(_ string, addrs []string) (module.LifetimeModule, error) { return &Endpoint{ addrs: addrs, saslAuth: auth.SASLAuth{ @@ -65,7 +66,7 @@ func (endp *Endpoint) InstanceName() string { return modName } -func (endp *Endpoint) Init(cfg *config.Map) error { +func (endp *Endpoint) Configure(_ []string, cfg *config.Map) error { cfg.Callback("auth", func(m *config.Map, node config.Node) error { return endp.saslAuth.AddProvider(m, node) }) @@ -97,12 +98,20 @@ func (endp *Endpoint) Init(cfg *config.Map) error { return fmt.Errorf("%s: %v", modName, err) } - l, err := net.Listen(parsed.Network(), parsed.Address()) + endp.endpoints = append(endp.endpoints, parsed) + } + + return nil +} + +func (endp *Endpoint) Start() error { + for _, addr := range endp.endpoints { + l, err := net.Listen(addr.Network(), addr.Address()) if err != nil { return fmt.Errorf("%s: %v", modName, err) } - endp.log.Printf("listening on %v", l.Addr()) + endp.log.Printf("listening on %v", l.Addr()) endp.listenersWg.Add(1) go func() { defer endp.listenersWg.Done() @@ -113,12 +122,13 @@ func (endp *Endpoint) Init(cfg *config.Map) error { } }() } - return nil } -func (endp *Endpoint) Close() error { - return endp.srv.Close() +func (endp *Endpoint) Stop() error { + endp.srv.Close() + endp.listenersWg.Wait() + return nil } func init() { diff --git a/internal/endpoint/imap/imap.go b/internal/endpoint/imap/imap.go index 191d93d2..765c429f 100644 --- a/internal/endpoint/imap/imap.go +++ b/internal/endpoint/imap/imap.go @@ -51,11 +51,12 @@ import ( type Endpoint struct { addrs []string serv *imapserver.Server - listeners []net.Listener proxyProtocol *proxy_protocol.ProxyProtocol Store module.Storage + tlsConfig *tls.Config - tlsConfig *tls.Config + endpoints []config.Endpoint + listeners []net.Listener listenersWg sync.WaitGroup saslAuth auth.SASLAuth @@ -66,7 +67,7 @@ type Endpoint struct { Log log.Logger } -func New(modName string, addrs []string) (module.Module, error) { +func New(modName string, addrs []string) (module.LifetimeModule, error) { endp := &Endpoint{ addrs: addrs, Log: log.Logger{Name: modName}, @@ -78,7 +79,7 @@ func New(modName string, addrs []string) (module.Module, error) { return endp, nil } -func (endp *Endpoint) Init(cfg *config.Map) error { +func (endp *Endpoint) Configure(_ []string, cfg *config.Map) error { var ( insecureAuth bool ioDebug bool @@ -106,20 +107,18 @@ func (endp *Endpoint) Init(cfg *config.Map) error { return err } - if updBe, ok := endp.Store.(updatepipe.Backend); ok { - if err := updBe.EnableUpdatePipe(updatepipe.ModeReplicate); err != nil { - endp.Log.Error("failed to initialize updates pipe", err) - } - } - addresses := make([]config.Endpoint, 0, len(endp.addrs)) for _, addr := range endp.addrs { saddr, err := config.ParseEndpoint(addr) if err != nil { return fmt.Errorf("imap: invalid address: %s", addr) } + if saddr.IsTLS() && endp.tlsConfig == nil { + return errors.New("imap: can't bind on IMAPS endpoint without TLS configuration") + } addresses = append(addresses, saddr) } + endp.endpoints = addresses endp.serv = imapserver.New(endp) endp.serv.AllowInsecureAuth = insecureAuth @@ -146,7 +145,29 @@ func (endp *Endpoint) Init(cfg *config.Map) error { }) } - return endp.setupListeners(addresses) + if endp.serv.AllowInsecureAuth { + endp.Log.Println("authentication over unencrypted connections is allowed, this is insecure configuration and should be used only for testing!") + } + if endp.serv.TLSConfig == nil { + endp.Log.Println("TLS is disabled, this is insecure configuration and should be used only for testing!") + endp.serv.AllowInsecureAuth = true + } + + return nil +} + +func (endp *Endpoint) Start() error { + if updBe, ok := endp.Store.(updatepipe.Backend); ok { + if err := updBe.EnableUpdatePipe(updatepipe.ModeReplicate); err != nil { + endp.Log.Error("failed to initialize updates pipe", err) + } + } + + if err := endp.setupListeners(endp.endpoints); err != nil { + endp.Stop() + return err + } + return nil } func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { @@ -171,24 +192,15 @@ func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { } endp.listeners = append(endp.listeners, l) - endp.listenersWg.Add(1) go func() { + defer endp.listenersWg.Done() if err := endp.serv.Serve(l); err != nil && !strings.HasSuffix(err.Error(), "use of closed network connection") { endp.Log.Printf("imap: failed to serve %s: %s", addr, err) } - endp.listenersWg.Done() }() } - if endp.serv.AllowInsecureAuth { - endp.Log.Println("authentication over unencrypted connections is allowed, this is insecure configuration and should be used only for testing!") - } - if endp.serv.TLSConfig == nil { - endp.Log.Println("TLS is disabled, this is insecure configuration and should be used only for testing!") - endp.serv.AllowInsecureAuth = true - } - return nil } @@ -200,7 +212,7 @@ func (endp *Endpoint) InstanceName() string { return "imap" } -func (endp *Endpoint) Close() error { +func (endp *Endpoint) Stop() error { for _, l := range endp.listeners { l.Close() } diff --git a/internal/endpoint/openmetrics/om.go b/internal/endpoint/openmetrics/om.go index 874a333d..136d4af5 100644 --- a/internal/endpoint/openmetrics/om.go +++ b/internal/endpoint/openmetrics/om.go @@ -34,22 +34,23 @@ import ( const modName = "openmetrics" type Endpoint struct { - addrs []string - logger log.Logger + addrs []string + endpoints []config.Endpoint + logger log.Logger listenersWg sync.WaitGroup serv http.Server mux *http.ServeMux } -func New(_ string, args []string) (module.Module, error) { +func New(_ string, args []string) (module.LifetimeModule, error) { return &Endpoint{ addrs: args, logger: log.Logger{Name: modName, Debug: log.DefaultLogger.Debug}, }, nil } -func (e *Endpoint) Init(cfg *config.Map) error { +func (e *Endpoint) Configure(inlineArgs []string, cfg *config.Map) error { cfg.Bool("debug", false, false, &e.logger.Debug) if _, err := cfg.Process(); err != nil { return err @@ -67,8 +68,24 @@ func (e *Endpoint) Init(cfg *config.Map) error { if endp.IsTLS() { return fmt.Errorf("%s: TLS is not supported yet", modName) } + } + + return nil +} + +func (e *Endpoint) Name() string { + return modName +} + +func (e *Endpoint) InstanceName() string { + return "" +} + +func (e *Endpoint) Start() error { + for _, endp := range e.endpoints { l, err := net.Listen(endp.Network(), endp.Address()) if err != nil { + e.Stop() return fmt.Errorf("%s: %v", modName, err) } @@ -77,24 +94,15 @@ func (e *Endpoint) Init(cfg *config.Map) error { e.logger.Println("listening on", endp.String()) err := e.serv.Serve(l) if err != nil && !errors.Is(err, http.ErrServerClosed) { - e.logger.Error("serve failed", err, "endpoint", a) + e.logger.Error("serve failed", err, "endpoint", endp) } e.listenersWg.Done() }() } - return nil } -func (e *Endpoint) Name() string { - return modName -} - -func (e *Endpoint) InstanceName() string { - return "" -} - -func (e *Endpoint) Close() error { +func (e *Endpoint) Stop() error { if err := e.serv.Close(); err != nil { return err } diff --git a/internal/endpoint/smtp/session.go b/internal/endpoint/smtp/session.go index 0eb6427f..ed842f98 100644 --- a/internal/endpoint/smtp/session.go +++ b/internal/endpoint/smtp/session.go @@ -274,7 +274,7 @@ func (s *Session) startDelivery(ctx context.Context, from string, opts smtp.Mail mailCtx, mailTask := trace.NewTask(s.msgCtx, "MAIL FROM") defer mailTask.End() - delivery, err := s.endp.pipeline.Start(mailCtx, msgMeta, cleanFrom) + delivery, err := s.endp.pipeline.StartDelivery(mailCtx, msgMeta, cleanFrom) if err != nil { s.msgCtx = nil s.msgTask.End() diff --git a/internal/endpoint/smtp/smtp.go b/internal/endpoint/smtp/smtp.go index 004d92e5..7e5f3ab0 100644 --- a/internal/endpoint/smtp/smtp.go +++ b/internal/endpoint/smtp/smtp.go @@ -54,6 +54,7 @@ type Endpoint struct { serv *smtp.Server name string addrs []string + endpoints []config.Endpoint listeners []net.Listener proxyProtocol *proxy_protocol.ProxyProtocol pipeline *msgpipeline.MsgPipeline @@ -88,7 +89,7 @@ func (endp *Endpoint) InstanceName() string { return endp.name } -func New(modName string, addrs []string) (module.Module, error) { +func New(modName string, addrs []string) (module.LifetimeModule, error) { endp := &Endpoint{ name: modName, addrs: addrs, @@ -104,7 +105,7 @@ func New(modName string, addrs []string) (module.Module, error) { return endp, nil } -func (endp *Endpoint) Init(cfg *config.Map) error { +func (endp *Endpoint) Configure(_ []string, cfg *config.Map) error { endp.serv = smtp.NewServer(endp) endp.serv.ErrorLog = endp.Log endp.serv.LMTP = endp.lmtp @@ -123,13 +124,7 @@ func (endp *Endpoint) Init(cfg *config.Map) error { addresses = append(addresses, saddr) } - - if err := endp.setupListeners(addresses); err != nil { - for _, l := range endp.listeners { - l.Close() - } - return err - } + endp.endpoints = addresses allLocal := true for _, addr := range addresses { @@ -321,6 +316,14 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { return nil } +func (endp *Endpoint) Start() error { + if err := endp.setupListeners(endp.endpoints); err != nil { + endp.Stop() + return err + } + return nil +} + func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { for _, addr := range addresses { var l net.Listener @@ -419,7 +422,7 @@ func (endp *Endpoint) ConnectionCount() int { return int(endp.sessionCnt.Load()) } -func (endp *Endpoint) Close() error { +func (endp *Endpoint) Stop() error { endp.serv.Close() endp.listenersWg.Wait() return nil diff --git a/internal/endpoint/smtp/smtp_test.go b/internal/endpoint/smtp/smtp_test.go index fa46c279..d6de22dc 100644 --- a/internal/endpoint/smtp/smtp_test.go +++ b/internal/endpoint/smtp/smtp_test.go @@ -89,7 +89,7 @@ func testEndpoint(t *testing.T, modName string, authMod module.PlainAuth, tgt mo }) } - err = endp.Init(config.NewMap(nil, config.Node{ + err = endp.Configure(nil, config.NewMap(nil, config.Node{ Children: cfg, })) if err != nil { @@ -107,6 +107,10 @@ func testEndpoint(t *testing.T, modName string, authMod module.PlainAuth, tgt mo endp.pipeline.FirstPipeline = true endp.pipeline.Log = testutils.Logger(t, "smtp/pipeline") + if err := endp.Start(); err != nil { + t.Fatal(err) + } + return endp } @@ -142,7 +146,7 @@ func submitMsgOpts(t *testing.T, cl *smtp.Client, from string, rcpts []string, o func TestSMTPDelivery(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Close() + defer endp.Stop() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { @@ -180,7 +184,7 @@ func TestSMTPDelivery(t *testing.T) { func TestSMTPDelivery_rDNSError(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Close() + defer endp.Stop() endp.resolver.(*mockdns.Resolver).Zones["1.0.0.127.in-addr.arpa."] = mockdns.Zone{ Err: &net.DNSError{ @@ -224,7 +228,7 @@ func TestSMTPDelivery_EarlyCheck_Fail(t *testing.T) { }, }, }, nil) - defer endp.Close() + defer endp.Stop() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { @@ -264,7 +268,7 @@ func TestSMTPDeliver_CheckError(t *testing.T) { }, }, nil) endp.deferServerReject = false - defer endp.Close() + defer endp.Stop() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { @@ -303,7 +307,7 @@ func TestSMTPDeliver_CheckError_Deferred(t *testing.T) { }, }, nil) endp.deferServerReject = true - defer endp.Close() + defer endp.Stop() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { @@ -342,7 +346,7 @@ func TestSMTPDeliver_CheckError_Deferred(t *testing.T) { func TestSMTPDelivery_Multi(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Close() + defer endp.Stop() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { @@ -380,7 +384,7 @@ func TestSMTPDelivery_Multi(t *testing.T) { func TestSMTPDelivery_AbortData(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Close() + defer endp.Stop() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { @@ -418,7 +422,7 @@ func TestSMTPDelivery_AbortData(t *testing.T) { func TestSMTPDelivery_EmptyMessage(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Close() + defer endp.Stop() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { @@ -457,7 +461,7 @@ func TestSMTPDelivery_EmptyMessage(t *testing.T) { func TestSMTPDelivery_AbortLogout(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Close() + defer endp.Stop() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { @@ -488,7 +492,7 @@ func TestSMTPDelivery_AbortLogout(t *testing.T) { func TestSMTPDelivery_Reset(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Close() + defer endp.Stop() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { @@ -523,7 +527,7 @@ func TestSMTPDelivery_Reset(t *testing.T) { func TestSMTPDelivery_SubmissionAuthRequire(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "submission", &module.Dummy{}, &tgt, nil, nil) - defer endp.Close() + defer endp.Stop() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { @@ -539,7 +543,7 @@ func TestSMTPDelivery_SubmissionAuthRequire(t *testing.T) { func TestSMTPDelivery_SubmissionAuthOK(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "submission", &module.Dummy{}, &tgt, nil, nil) - defer endp.Close() + defer endp.Stop() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { diff --git a/internal/endpoint/smtp/smtputf8_test.go b/internal/endpoint/smtp/smtputf8_test.go index b3f57015..2f157aba 100644 --- a/internal/endpoint/smtp/smtputf8_test.go +++ b/internal/endpoint/smtp/smtputf8_test.go @@ -43,7 +43,7 @@ func TestSMTPUTF8_MangleStatusMessage(t *testing.T) { }, }, nil) endp.deferServerReject = false - defer endp.Close() + defer endp.Stop() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) @@ -73,7 +73,7 @@ func TestSMTP_RejectNonASCIIFrom(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) endp.deferServerReject = false - defer endp.Close() + defer endp.Stop() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) @@ -100,7 +100,7 @@ func TestSMTPUTF8_NormalizeCaseFoldFrom(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) endp.deferServerReject = false - defer endp.Close() + defer endp.Stop() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) @@ -127,7 +127,7 @@ func TestSMTP_RejectNonASCIIRcpt(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) endp.deferServerReject = false - defer endp.Close() + defer endp.Stop() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) @@ -154,7 +154,7 @@ func TestSMTPUTF8_NormalizeCaseFoldRcpt(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) endp.deferServerReject = false - defer endp.Close() + defer endp.Stop() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) @@ -191,7 +191,7 @@ func TestSMTPUTF8_NoMangleStatusMessage(t *testing.T) { }, }, nil) endp.deferServerReject = false - defer endp.Close() + defer endp.Stop() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) @@ -222,7 +222,7 @@ func TestSMTPUTF8_NoMangleStatusMessage(t *testing.T) { func TestSMTPUTF8_Received_EHLO_ALabel(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Close() + defer endp.Stop() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) @@ -256,7 +256,7 @@ func TestSMTPUTF8_Received_EHLO_ALabel(t *testing.T) { func TestSMTPUTF8_Received_rDNS_ALabel(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Close() + defer endp.Stop() defer testutils.WaitForConnsClose(t, endp.serv) endp.resolver.(*mockdns.Resolver).Zones["1.0.0.127.in-addr.arpa."] = mockdns.Zone{ @@ -290,7 +290,7 @@ func TestSMTPUTF8_Received_rDNS_ALabel(t *testing.T) { func TestSMTPUTF8_Received_rDNS_ULabel(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Close() + defer endp.Stop() defer testutils.WaitForConnsClose(t, endp.serv) endp.resolver.(*mockdns.Resolver).Zones["1.0.0.127.in-addr.arpa."] = mockdns.Zone{ @@ -326,7 +326,7 @@ func TestSMTPUTF8_Received_rDNS_ULabel(t *testing.T) { func TestSMTPUTF8_Received_EHLO_ULabel(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Close() + defer endp.Stop() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) diff --git a/internal/endpoint/smtp/submission_test.go b/internal/endpoint/smtp/submission_test.go index f257364c..beb4cf5e 100644 --- a/internal/endpoint/smtp/submission_test.go +++ b/internal/endpoint/smtp/submission_test.go @@ -56,7 +56,7 @@ func TestSubmissionPrepare(t *testing.T) { cl, _ := smtp.Dial("127.0.0.1:" + testPort) cl.Close() - endp.Close() + endp.Stop() }() session, err := endp.NewSession(nil) diff --git a/internal/imap_filter/command/command.go b/internal/imap_filter/command/command.go index 58146dca..32f05c03 100644 --- a/internal/imap_filter/command/command.go +++ b/internal/imap_filter/command/command.go @@ -61,19 +61,12 @@ func (c *Check) IMAPFilter(accountName string, rcptTo string, msgMeta *module.Ms return c.run(cmd, args, io.MultiReader(bytes.NewReader(buf.Bytes()), bR)) } -func New(_, instName string, _, inlineArgs []string) (module.Module, error) { +func New(_, instName string) (module.Module, error) { c := &Check{ instName: instName, log: log.Logger{Name: modName, Debug: log.DefaultLogger.Debug}, } - if len(inlineArgs) == 0 { - return nil, errors.New("command: at least one argument is required (command name)") - } - - c.cmd = inlineArgs[0] - c.cmdArgs = inlineArgs[1:] - return c, nil } @@ -85,7 +78,14 @@ func (c *Check) InstanceName() string { return c.instName } -func (c *Check) Init(cfg *config.Map) error { +func (c *Check) Configure(inlineArgs []string, cfg *config.Map) error { + if len(inlineArgs) == 0 { + return errors.New("command: at least one argument is required (command name)") + } + + c.cmd = inlineArgs[0] + c.cmdArgs = inlineArgs[1:] + // Check whether the inline argument command is usable. if _, err := exec.LookPath(c.cmd); err != nil { return fmt.Errorf("command: %w", err) diff --git a/internal/imap_filter/group.go b/internal/imap_filter/group.go index c1c5ecc8..f86ea866 100644 --- a/internal/imap_filter/group.go +++ b/internal/imap_filter/group.go @@ -37,7 +37,7 @@ type Group struct { log log.Logger } -func NewGroup(_, instName string, _, _ []string) (module.Module, error) { +func NewGroup(_, instName string) (module.Module, error) { return &Group{ instName: instName, log: log.Logger{Name: "imap_filters", Debug: log.DefaultLogger.Debug}, @@ -66,7 +66,7 @@ func (g *Group) IMAPFilter(accountName string, rcptTo string, meta *module.MsgMe return finalFolder, finalFlags, nil } -func (g *Group) Init(cfg *config.Map) error { +func (g *Group) Configure(inlineArgs []string, cfg *config.Map) error { for _, node := range cfg.Block.Children { mod, err := modconfig.IMAPFilter(cfg.Globals, append([]string{node.Name}, node.Args...), node) if err != nil { diff --git a/internal/libdns/acmedns.go b/internal/libdns/acmedns.go index cb657ee2..d088811d 100644 --- a/internal/libdns/acmedns.go +++ b/internal/libdns/acmedns.go @@ -10,7 +10,7 @@ import ( ) func init() { - module.Register("libdns.acmedns", func(modName, instName string, _, _ []string) (module.Module, error) { + module.Register("libdns.acmedns", func(modName, instName string) (module.Module, error) { p := acmedns.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/alidns.go b/internal/libdns/alidns.go index cb980beb..9e32399c 100644 --- a/internal/libdns/alidns.go +++ b/internal/libdns/alidns.go @@ -10,7 +10,7 @@ import ( ) func init() { - module.Register("libdns.alidns", func(modName, instName string, _, _ []string) (module.Module, error) { + module.Register("libdns.alidns", func(modName, instName string) (module.Module, error) { p := alidns.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/cloudflare.go b/internal/libdns/cloudflare.go index 30319538..b69ec75d 100644 --- a/internal/libdns/cloudflare.go +++ b/internal/libdns/cloudflare.go @@ -10,7 +10,7 @@ import ( ) func init() { - module.Register("libdns.cloudflare", func(modName, instName string, _, _ []string) (module.Module, error) { + module.Register("libdns.cloudflare", func(modName, instName string) (module.Module, error) { p := cloudflare.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/digitalocean.go b/internal/libdns/digitalocean.go index 98b77b07..20369c9a 100644 --- a/internal/libdns/digitalocean.go +++ b/internal/libdns/digitalocean.go @@ -10,7 +10,7 @@ import ( ) func init() { - module.Register("libdns.digitalocean", func(modName, instName string, _, _ []string) (module.Module, error) { + module.Register("libdns.digitalocean", func(modName, instName string) (module.Module, error) { p := digitalocean.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/gandi.go b/internal/libdns/gandi.go index 62c7c2d0..59a493d5 100644 --- a/internal/libdns/gandi.go +++ b/internal/libdns/gandi.go @@ -13,7 +13,7 @@ import ( ) func init() { - module.Register("libdns.gandi", func(modName, instName string, _, _ []string) (module.Module, error) { + module.Register("libdns.gandi", func(modName, instName string) (module.Module, error) { p := gandi.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/googleclouddns.go b/internal/libdns/googleclouddns.go index b59c0562..a9bc493e 100644 --- a/internal/libdns/googleclouddns.go +++ b/internal/libdns/googleclouddns.go @@ -10,7 +10,7 @@ import ( ) func init() { - module.Register("libdns.googleclouddns", func(modName, instName string, _, _ []string) (module.Module, error) { + module.Register("libdns.googleclouddns", func(modName, instName string) (module.Module, error) { p := googleclouddns.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/hetzner.go b/internal/libdns/hetzner.go index b360641f..06620c46 100644 --- a/internal/libdns/hetzner.go +++ b/internal/libdns/hetzner.go @@ -10,7 +10,7 @@ import ( ) func init() { - module.Register("libdns.hetzner", func(modName, instName string, _, _ []string) (module.Module, error) { + module.Register("libdns.hetzner", func(modName, instName string) (module.Module, error) { p := hetzner.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/leaseweb.go b/internal/libdns/leaseweb.go index 23af12d7..888d0870 100644 --- a/internal/libdns/leaseweb.go +++ b/internal/libdns/leaseweb.go @@ -10,7 +10,7 @@ import ( ) func init() { - module.Register("libdns.leaseweb", func(modName, instName string, _, _ []string) (module.Module, error) { + module.Register("libdns.leaseweb", func(modName, instName string) (module.Module, error) { p := leaseweb.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/metaname.go b/internal/libdns/metaname.go index 2e37ddd3..2180509b 100644 --- a/internal/libdns/metaname.go +++ b/internal/libdns/metaname.go @@ -10,7 +10,7 @@ import ( ) func init() { - module.Register("libdns.metaname", func(modName, instName string, _, _ []string) (module.Module, error) { + module.Register("libdns.metaname", func(modName, instName string) (module.Module, error) { p := metaname.Provider{ Endpoint: "https://metaname.net/api/1.1", } diff --git a/internal/libdns/namecheap.go b/internal/libdns/namecheap.go index 656ebe51..cb38461e 100644 --- a/internal/libdns/namecheap.go +++ b/internal/libdns/namecheap.go @@ -10,7 +10,7 @@ import ( ) func init() { - module.Register("libdns.namecheap", func(modName, instName string, _, _ []string) (module.Module, error) { + module.Register("libdns.namecheap", func(modName, instName string) (module.Module, error) { p := namecheap.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/namedotcom.go b/internal/libdns/namedotcom.go index 3ce3c51c..0a5c9934 100644 --- a/internal/libdns/namedotcom.go +++ b/internal/libdns/namedotcom.go @@ -10,7 +10,7 @@ import ( ) func init() { - module.Register("libdns.namedotcom", func(modName, instName string, _, _ []string) (module.Module, error) { + module.Register("libdns.namedotcom", func(modName, instName string) (module.Module, error) { p := namedotcom.Provider{ Server: "https://api.name.com", } diff --git a/internal/libdns/provider_module.go b/internal/libdns/provider_module.go index 75561501..6df107ff 100644 --- a/internal/libdns/provider_module.go +++ b/internal/libdns/provider_module.go @@ -15,7 +15,7 @@ type ProviderModule struct { modName string } -func (p *ProviderModule) Init(cfg *config.Map) error { +func (p *ProviderModule) Configure(inlineArgs []string, cfg *config.Map) error { p.setConfig(cfg) _, err := cfg.Process() if p.afterConfig != nil { diff --git a/internal/libdns/rfc2136.go b/internal/libdns/rfc2136.go index 19751f6f..90eca724 100644 --- a/internal/libdns/rfc2136.go +++ b/internal/libdns/rfc2136.go @@ -10,7 +10,7 @@ import ( ) func init() { - module.Register("libdns.rfc2136", func(modName, instName string, _, _ []string) (module.Module, error) { + module.Register("libdns.rfc2136", func(modName, instName string) (module.Module, error) { p := rfc2136.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/route53.go b/internal/libdns/route53.go index dd507249..fbb5edf7 100644 --- a/internal/libdns/route53.go +++ b/internal/libdns/route53.go @@ -10,7 +10,7 @@ import ( ) func init() { - module.Register("libdns.route53", func(modName, instName string, _, _ []string) (module.Module, error) { + module.Register("libdns.route53", func(modName, instName string) (module.Module, error) { p := route53.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/vultr.go b/internal/libdns/vultr.go index 9157258e..e94a2869 100644 --- a/internal/libdns/vultr.go +++ b/internal/libdns/vultr.go @@ -10,7 +10,7 @@ import ( ) func init() { - module.Register("libdns.vultr", func(modName, instName string, _, _ []string) (module.Module, error) { + module.Register("libdns.vultr", func(modName, instName string) (module.Module, error) { p := vultr.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/limits/limits.go b/internal/limits/limits.go index 95d98a2f..7c1a39b0 100644 --- a/internal/limits/limits.go +++ b/internal/limits/limits.go @@ -46,13 +46,13 @@ type Group struct { dest *limiters.BucketSet // BucketSet of MultiLimit } -func New(_, instName string, _, _ []string) (module.Module, error) { +func New(_, instName string) (module.Module, error) { return &Group{ instName: instName, }, nil } -func (g *Group) Init(cfg *config.Map) error { +func (g *Group) Configure(inlineArgs []string, cfg *config.Map) error { var ( globalL []limiters.L ipL []func() limiters.L diff --git a/internal/modify/dkim/dkim.go b/internal/modify/dkim/dkim.go index ffeed4a2..f0894931 100644 --- a/internal/modify/dkim/dkim.go +++ b/internal/modify/dkim/dkim.go @@ -111,23 +111,13 @@ type Modifier struct { log log.Logger } -func New(_, instName string, _, inlineArgs []string) (module.Module, error) { +func New(_, instName string) (module.Module, error) { m := &Modifier{ instName: instName, signers: map[string]crypto.Signer{}, log: log.Logger{Name: "modify.dkim"}, } - if len(inlineArgs) == 0 { - return m, nil - } - if len(inlineArgs) == 1 { - return nil, errors.New("modify.dkim: at least two arguments required") - } - - m.domains = inlineArgs[0 : len(inlineArgs)-1] - m.selector = inlineArgs[len(inlineArgs)-1] - return m, nil } @@ -139,7 +129,16 @@ func (m *Modifier) InstanceName() string { return m.instName } -func (m *Modifier) Init(cfg *config.Map) error { +func (m *Modifier) Configure(inlineArgs []string, cfg *config.Map) error { + if len(inlineArgs) != 0 { + if len(inlineArgs) == 1 { + return errors.New("modify.dkim: at least two arguments required") + } + + m.domains = inlineArgs[0 : len(inlineArgs)-1] + m.selector = inlineArgs[len(inlineArgs)-1] + } + var ( hashName string keyPathTemplate string diff --git a/internal/modify/dkim/dkim_test.go b/internal/modify/dkim/dkim_test.go index d4a9b5ad..172f0f34 100644 --- a/internal/modify/dkim/dkim_test.go +++ b/internal/modify/dkim/dkim_test.go @@ -37,14 +37,14 @@ import ( ) func newTestModifier(t *testing.T, dir, keyAlgo string, domains []string) *Modifier { - mod, err := New("", "test", nil, nil) + mod, err := New("", "test") if err != nil { t.Fatal(err) } m := mod.(*Modifier) m.log = testutils.Logger(t, m.Name()) - err = m.Init(config.NewMap(nil, config.Node{ + err = m.Configure(nil, config.NewMap(nil, config.Node{ Children: []config.Node{ { Name: "domains", diff --git a/internal/modify/group.go b/internal/modify/group.go index 3278b0ff..00fbe124 100644 --- a/internal/modify/group.go +++ b/internal/modify/group.go @@ -43,7 +43,7 @@ type ( } ) -func (g *Group) Init(cfg *config.Map) error { +func (g *Group) Configure(inlineArgs []string, cfg *config.Map) error { for _, node := range cfg.Block.Children { mod, err := modconfig.MsgModifier(cfg.Globals, append([]string{node.Name}, node.Args...), node) if err != nil { @@ -64,7 +64,7 @@ func (g *Group) InstanceName() string { return g.instName } -func (g Group) ModStateForMsg(ctx context.Context, msgMeta *module.MsgMetadata) (module.ModifierState, error) { +func (g *Group) ModStateForMsg(ctx context.Context, msgMeta *module.MsgMetadata) (module.ModifierState, error) { gs := groupState{} for _, modifier := range g.Modifiers { state, err := modifier.ModStateForMsg(ctx, msgMeta) @@ -132,7 +132,7 @@ func (gs groupState) Close() error { } func init() { - module.Register("modifiers", func(_, instName string, _, _ []string) (module.Module, error) { + module.Register("modifiers", func(_, instName string) (module.Module, error) { return &Group{ instName: instName, }, nil diff --git a/internal/modify/replace_addr.go b/internal/modify/replace_addr.go index 50dd5dff..f305ac95 100644 --- a/internal/modify/replace_addr.go +++ b/internal/modify/replace_addr.go @@ -37,20 +37,18 @@ import ( // If created with modName = "modify.replace_sender", it will change sender address. // If created with modName = "modify.replace_rcpt", it will change recipient addresses. type replaceAddr struct { - modName string - instName string - inlineArgs []string + modName string + instName string replaceSender bool replaceRcpt bool table module.MultiTable } -func NewReplaceAddr(modName, instName string, _, inlineArgs []string) (module.Module, error) { +func NewReplaceAddr(modName, instName string) (module.Module, error) { r := replaceAddr{ modName: modName, instName: instName, - inlineArgs: inlineArgs, replaceSender: modName == "modify.replace_sender", replaceRcpt: modName == "modify.replace_rcpt", } @@ -58,23 +56,23 @@ func NewReplaceAddr(modName, instName string, _, inlineArgs []string) (module.Mo return &r, nil } -func (r *replaceAddr) Init(cfg *config.Map) error { - return modconfig.ModuleFromNode("table", r.inlineArgs, cfg.Block, cfg.Globals, &r.table) +func (r *replaceAddr) Configure(inlineArgs []string, cfg *config.Map) error { + return modconfig.ModuleFromNode("table", inlineArgs, cfg.Block, cfg.Globals, &r.table) } -func (r replaceAddr) Name() string { +func (r *replaceAddr) Name() string { return r.modName } -func (r replaceAddr) InstanceName() string { +func (r *replaceAddr) InstanceName() string { return r.instName } -func (r replaceAddr) ModStateForMsg(ctx context.Context, msgMeta *module.MsgMetadata) (module.ModifierState, error) { +func (r *replaceAddr) ModStateForMsg(ctx context.Context, msgMeta *module.MsgMetadata) (module.ModifierState, error) { return r, nil } -func (r replaceAddr) RewriteSender(ctx context.Context, mailFrom string) (string, error) { +func (r *replaceAddr) RewriteSender(ctx context.Context, mailFrom string) (string, error) { if r.replaceSender { results, err := r.rewrite(ctx, mailFrom) if err != nil { @@ -85,22 +83,22 @@ func (r replaceAddr) RewriteSender(ctx context.Context, mailFrom string) (string return mailFrom, nil } -func (r replaceAddr) RewriteRcpt(ctx context.Context, rcptTo string) ([]string, error) { +func (r *replaceAddr) RewriteRcpt(ctx context.Context, rcptTo string) ([]string, error) { if r.replaceRcpt { return r.rewrite(ctx, rcptTo) } return []string{rcptTo}, nil } -func (r replaceAddr) RewriteBody(ctx context.Context, h *textproto.Header, body buffer.Buffer) error { +func (r *replaceAddr) RewriteBody(ctx context.Context, h *textproto.Header, body buffer.Buffer) error { return nil } -func (r replaceAddr) Close() error { +func (r *replaceAddr) Close() error { return nil } -func (r replaceAddr) rewrite(ctx context.Context, val string) ([]string, error) { +func (r *replaceAddr) rewrite(ctx context.Context, val string) ([]string, error) { normAddr, err := address.ForLookup(val) if err != nil { return []string{val}, fmt.Errorf("malformed address: %v", err) diff --git a/internal/modify/replace_addr_test.go b/internal/modify/replace_addr_test.go index e16cfd34..9c3b0b75 100644 --- a/internal/modify/replace_addr_test.go +++ b/internal/modify/replace_addr_test.go @@ -31,12 +31,12 @@ func testReplaceAddr(t *testing.T, modName string) { test := func(addr string, expectedMulti []string, aliases map[string][]string) { t.Helper() - mod, err := NewReplaceAddr(modName, "", nil, []string{"dummy"}) + mod, err := NewReplaceAddr(modName, "") if err != nil { t.Fatal(err) } m := mod.(*replaceAddr) - if err := m.Init(config.NewMap(nil, config.Node{})); err != nil { + if err := m.Configure([]string{"dummy"}, config.NewMap(nil, config.Node{})); err != nil { t.Fatal(err) } m.table = testutils.MultiTable{M: aliases} diff --git a/internal/msgpipeline/check_group.go b/internal/msgpipeline/check_group.go index 1fd6b245..27bcdb6c 100644 --- a/internal/msgpipeline/check_group.go +++ b/internal/msgpipeline/check_group.go @@ -37,7 +37,7 @@ type CheckGroup struct { L []module.Check } -func (cg *CheckGroup) Init(cfg *config.Map) error { +func (cg *CheckGroup) Configure(inlineArgs []string, cfg *config.Map) error { for _, node := range cfg.Block.Children { chk, err := modconfig.MessageCheck(cfg.Globals, append([]string{node.Name}, node.Args...), node) if err != nil { @@ -50,16 +50,16 @@ func (cg *CheckGroup) Init(cfg *config.Map) error { return nil } -func (CheckGroup) Name() string { +func (*CheckGroup) Name() string { return "checks" } -func (cg CheckGroup) InstanceName() string { +func (cg *CheckGroup) InstanceName() string { return cg.instName } func init() { - module.Register("checks", func(_, instName string, _, _ []string) (module.Module, error) { + module.Register("checks", func(_, instName string) (module.Module, error) { return &CheckGroup{ instName: instName, }, nil diff --git a/internal/msgpipeline/dmarc_test.go b/internal/msgpipeline/dmarc_test.go index f942baf8..e1c5656a 100644 --- a/internal/msgpipeline/dmarc_test.go +++ b/internal/msgpipeline/dmarc_test.go @@ -55,7 +55,7 @@ func doTestDelivery(t *testing.T, tgt module.DeliveryTarget, from string, to []s panic(err) } - delivery, err := tgt.Start(context.Background(), &ctx, from) + delivery, err := tgt.StartDelivery(context.Background(), &ctx, from) if err != nil { return encodedID, err } diff --git a/internal/msgpipeline/module.go b/internal/msgpipeline/module.go index cf30d221..a951ec88 100644 --- a/internal/msgpipeline/module.go +++ b/internal/msgpipeline/module.go @@ -30,14 +30,14 @@ type Module struct { *MsgPipeline } -func NewModule(modName, instName string, aliases, inlineArgs []string) (module.Module, error) { +func NewModule(modName, instName string) (module.Module, error) { return &Module{ log: log.Logger{Name: "msgpipeline"}, instName: instName, }, nil } -func (m *Module) Init(cfg *config.Map) error { +func (m *Module) Configure(inlineArgs []string, cfg *config.Map) error { var hostname string cfg.String("hostname", true, true, "", &hostname) cfg.Bool("debug", true, false, &m.log.Debug) diff --git a/internal/msgpipeline/msgpipeline.go b/internal/msgpipeline/msgpipeline.go index 388caab1..5caa0899 100644 --- a/internal/msgpipeline/msgpipeline.go +++ b/internal/msgpipeline/msgpipeline.go @@ -111,13 +111,13 @@ func (d *MsgPipeline) RunEarlyChecks(ctx context.Context, state *module.ConnStat return eg.Wait() } -// Start starts new message delivery, runs connection and sender checks, sender modifiers +// StartDelivery starts new message delivery, runs connection and sender checks, sender modifiers // and selects source block from config to use for handling. // // Returned module.Delivery implements PartialDelivery. If underlying target doesn't // support it, msgpipeline will copy the returned error for all recipients handled // by target. -func (d *MsgPipeline) Start(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { +func (d *MsgPipeline) StartDelivery(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { dd := msgpipelineDelivery{ d: d, rcptModifiersState: make(map[*rcptBlock]module.ModifierState), @@ -622,14 +622,14 @@ func (dd *msgpipelineDelivery) getDelivery(ctx context.Context, tgt module.Deliv return delivery_, nil } - deliveryObj, err := tgt.Start(ctx, dd.msgMeta, dd.sourceAddr) + deliveryObj, err := tgt.StartDelivery(ctx, dd.msgMeta, dd.sourceAddr) if err != nil { - dd.log.Debugf("tgt.Start(%s) failure, target = %s: %v", dd.sourceAddr, objectName(tgt), err) + dd.log.Debugf("tgt.StartDelivery(%s) failure, target = %s: %v", dd.sourceAddr, objectName(tgt), err) return nil, err } delivery_ = &delivery{Delivery: deliveryObj} - dd.log.Debugf("tgt.Start(%s) ok, target = %s", dd.sourceAddr, objectName(tgt)) + dd.log.Debugf("tgt.StartDelivery(%s) ok, target = %s", dd.sourceAddr, objectName(tgt)) dd.deliveries[tgt] = delivery_ return delivery_, nil diff --git a/internal/msgpipeline/msgpipeline_test.go b/internal/msgpipeline/msgpipeline_test.go index 9899eb03..6d09da00 100644 --- a/internal/msgpipeline/msgpipeline_test.go +++ b/internal/msgpipeline/msgpipeline_test.go @@ -380,14 +380,14 @@ func TestMsgPipeline_PerSourceReject(t *testing.T) { testutils.DoTestDelivery(t, &d, "sender1@example.com", []string{"rcpt@example.com"}) - _, err := d.Start(context.Background(), &module.MsgMetadata{ID: "testing"}, "sender2@example.com") + _, err := d.StartDelivery(context.Background(), &module.MsgMetadata{ID: "testing"}, "sender2@example.com") if err == nil { - t.Error("expected error for delivery.Start, got nil") + t.Error("expected error for delivery.StartDelivery, got nil") } - _, err = d.Start(context.Background(), &module.MsgMetadata{ID: "testing"}, "sender2@example.org") + _, err = d.StartDelivery(context.Background(), &module.MsgMetadata{ID: "testing"}, "sender2@example.org") if err == nil { - t.Error("expected error for delivery.Start, got nil") + t.Error("expected error for delivery.StartDelivery, got nil") } } @@ -413,9 +413,9 @@ func TestMsgPipeline_PerRcptReject(t *testing.T) { Log: testutils.Logger(t, "msgpipeline"), } - delivery, err := d.Start(context.Background(), &module.MsgMetadata{ID: "testing"}, "sender@example.com") + delivery, err := d.StartDelivery(context.Background(), &module.MsgMetadata{ID: "testing"}, "sender@example.com") if err != nil { - t.Fatalf("unexpected Start err: %v", err) + t.Fatalf("unexpected StartDelivery err: %v", err) } defer func() { if err := delivery.Abort(context.Background()); err != nil { @@ -628,7 +628,7 @@ func TestMsgPipeline_MalformedSource(t *testing.T) { // Simple checks for violations that can make msgpipeline misbehave. for _, addr := range []string{"not_postmaster_but_no_at_sign", "@no_mailbox", "no_domain@"} { - _, err := d.Start(context.Background(), &module.MsgMetadata{ID: "testing"}, addr) + _, err := d.StartDelivery(context.Background(), &module.MsgMetadata{ID: "testing"}, addr) if err == nil { t.Errorf("%s is accepted as valid address", addr) } diff --git a/internal/smtpconn/smtpconn.go b/internal/smtpconn/smtpconn.go index ec42974d..3c4b4224 100644 --- a/internal/smtpconn/smtpconn.go +++ b/internal/smtpconn/smtpconn.go @@ -376,7 +376,7 @@ func (c *C) Rcpt(ctx context.Context, to string, opts smtp.RcptOptions) error { // TODO: DSN support } - // If necessary, the extension flag is enabled in Start. + // If necessary, the extension flag is enabled in StartDelivery. if ok, _ := c.cl.Extension("SMTPUTF8"); !address.IsASCII(to) && !ok { var err error to, err = address.ToASCII(to) diff --git a/internal/storage/blob/fs/fs.go b/internal/storage/blob/fs/fs.go index e8c9b389..48e5f21a 100644 --- a/internal/storage/blob/fs/fs.go +++ b/internal/storage/blob/fs/fs.go @@ -17,26 +17,27 @@ type FSStore struct { root string } -func New(_, instName string, _, inlineArgs []string) (module.Module, error) { - switch len(inlineArgs) { - case 0: - return &FSStore{instName: instName}, nil - case 1: - return &FSStore{instName: instName, root: inlineArgs[0]}, nil - default: - return nil, fmt.Errorf("storage.blob.fs: 1 or 0 arguments expected") - } +func New(_, instName string) (module.Module, error) { + return &FSStore{instName: instName}, nil } -func (s FSStore) Name() string { +func (s *FSStore) Name() string { return "storage.blob.fs" } -func (s FSStore) InstanceName() string { +func (s *FSStore) InstanceName() string { return s.instName } -func (s *FSStore) Init(cfg *config.Map) error { +func (s *FSStore) Configure(inlineArgs []string, cfg *config.Map) error { + switch len(inlineArgs) { + case 0: + case 1: + s.root = inlineArgs[0] + default: + return fmt.Errorf("storage.blob.fs: 1 or 0 arguments expected") + } + cfg.String("root", false, false, s.root, &s.root) if _, err := cfg.Process(); err != nil { return err @@ -91,5 +92,5 @@ func (s *FSStore) Delete(_ context.Context, keys []string) error { func init() { var _ module.BlobStore = &FSStore{} - module.Register(FSStore{}.Name(), New) + module.Register((&FSStore{}).Name(), New) } diff --git a/internal/storage/blob/s3/s3.go b/internal/storage/blob/s3/s3.go index af01d888..075c8368 100644 --- a/internal/storage/blob/s3/s3.go +++ b/internal/storage/blob/s3/s3.go @@ -34,18 +34,18 @@ type Store struct { objectPrefix string } -func New(_, instName string, _, inlineArgs []string) (module.Module, error) { - if len(inlineArgs) != 0 { - return nil, fmt.Errorf("%s: expected 0 arguments", modName) - } - +func New(_, instName string) (module.Module, error) { return &Store{ instName: instName, log: log.Logger{Name: modName}, }, nil } -func (s *Store) Init(cfg *config.Map) error { +func (s *Store) Configure(inlineArgs []string, cfg *config.Map) error { + if len(inlineArgs) != 0 { + return fmt.Errorf("%s: expected 0 arguments", modName) + } + var ( secure bool accessKeyID string diff --git a/internal/storage/blob/s3/s3_test.go b/internal/storage/blob/s3/s3_test.go index 98dd228b..78ba9700 100644 --- a/internal/storage/blob/s3/s3_test.go +++ b/internal/storage/blob/s3/s3_test.go @@ -28,7 +28,7 @@ func TestFS(t *testing.T) { } st := &Store{instName: "test"} - err := st.Init(config.NewMap(map[string]interface{}{}, config.Node{ + err := st.Configure(nil, config.NewMap(map[string]interface{}{}, config.Node{ Children: []config.Node{ { Name: "endpoint", diff --git a/internal/storage/imapsql/delivery.go b/internal/storage/imapsql/delivery.go index 60cb2e1f..a9ce32e8 100644 --- a/internal/storage/imapsql/delivery.go +++ b/internal/storage/imapsql/delivery.go @@ -155,8 +155,8 @@ func (d *delivery) Commit(ctx context.Context) error { return d.d.Commit() } -func (store *Storage) Start(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { - defer trace.StartRegion(ctx, "sql/Start").End() +func (store *Storage) StartDelivery(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { + defer trace.StartRegion(ctx, "sql/StartDelivery").End() return &delivery{ store: store, diff --git a/internal/storage/imapsql/imapsql.go b/internal/storage/imapsql/imapsql.go index 711a34e1..ddee1358 100644 --- a/internal/storage/imapsql/imapsql.go +++ b/internal/storage/imapsql/imapsql.go @@ -28,6 +28,7 @@ package imapsql import ( "context" "crypto/sha1" + "database/sql" "encoding/hex" "errors" "fmt" @@ -61,8 +62,10 @@ type Storage struct { junkMbox string - driver string - dsn []string + driver string + dsn []string + blobStore module.BlobStore + opts *imapsql.Opts resolver dns.Resolver @@ -86,24 +89,25 @@ func (store *Storage) InstanceName() string { return store.instName } -func New(_, instName string, _, inlineArgs []string) (module.Module, error) { +func New(_, instName string) (module.Module, error) { store := &Storage{ instName: instName, Log: log.Logger{Name: "imapsql"}, resolver: dns.DefaultResolver(), } + return store, nil +} + +func (store *Storage) Configure(inlineArgs []string, cfg *config.Map) error { if len(inlineArgs) != 0 { if len(inlineArgs) == 1 { - return nil, errors.New("imapsql: expected at least 2 arguments") + return errors.New("imapsql: expected at least 2 arguments") } store.driver = inlineArgs[0] store.dsn = inlineArgs[1:] } - return store, nil -} -func (store *Storage) Init(cfg *config.Map) error { var ( driver string dsn []string @@ -115,7 +119,7 @@ func (store *Storage) Init(cfg *config.Map) error { blobStore module.BlobStore ) - opts := imapsql.Opts{} + opts := &imapsql.Opts{} cfg.String("driver", false, false, store.driver, &driver) cfg.StringList("dsn", false, false, store.dsn, &dsn) cfg.Callback("fsstore", func(m *config.Map, node config.Node) error { @@ -238,9 +242,6 @@ func (store *Storage) Init(cfg *config.Map) error { opts.MaxMsgBytes = new(uint32) *opts.MaxMsgBytes = uint32(appendlimitVal) } - var err error - - dsnStr := strings.Join(dsn, " ") if len(compression) != 0 { switch compression[0] { @@ -264,16 +265,33 @@ func (store *Storage) Init(cfg *config.Map) error { } } - store.Back, err = imapsql.New(driver, dsnStr, ExtBlobStore{Base: blobStore}, opts) - if err != nil { - return fmt.Errorf("imapsql: %s", err) + driverFound := false + for _, d := range sql.Drivers() { + if d == driver { + driverFound = true + break + } + } + if !driverFound { + return fmt.Errorf("imapsql: unknown driver %q", driver) } - - store.Log.Debugln("go-imap-sql version", imapsql.VersionStr) store.driver = driver store.dsn = dsn + store.blobStore = blobStore + store.opts = opts + store.Log.Debugln("go-imap-sql version", imapsql.VersionStr) + + return nil +} +func (store *Storage) Start() error { + dsnStr := strings.Join(store.dsn, " ") + var err error + store.Back, err = imapsql.New(store.driver, dsnStr, ExtBlobStore{Base: store.blobStore}, *store.opts) + if err != nil { + return fmt.Errorf("imapsql: %s", err) + } return nil } @@ -407,7 +425,7 @@ func (store *Storage) Lookup(ctx context.Context, key string) (string, bool, err return "", true, nil } -func (store *Storage) Close() error { +func (store *Storage) Stop() error { // Stop backend from generating new updates. store.Back.Close() diff --git a/internal/table/chain.go b/internal/table/chain.go index 72eceba0..3b29a1c0 100644 --- a/internal/table/chain.go +++ b/internal/table/chain.go @@ -34,14 +34,14 @@ type Chain struct { optional []bool } -func NewChain(modName, instName string, _, _ []string) (module.Module, error) { +func NewChain(modName, instName string) (module.Module, error) { return &Chain{ modName: modName, instName: instName, }, nil } -func (s *Chain) Init(cfg *config.Map) error { +func (s *Chain) Configure(inlineArgs []string, cfg *config.Map) error { cfg.Callback("step", func(m *config.Map, node config.Node) error { var tbl module.Table err := modconfig.ModuleFromNode("table", node.Args, node, m.Globals, &tbl) diff --git a/internal/table/email_localpart.go b/internal/table/email_localpart.go index a9d6f067..4371a3e3 100644 --- a/internal/table/email_localpart.go +++ b/internal/table/email_localpart.go @@ -32,7 +32,7 @@ type EmailLocalpart struct { allowNonEmail bool } -func NewEmailLocalpart(modName, instName string, _, _ []string) (module.Module, error) { +func NewEmailLocalpart(modName, instName string) (module.Module, error) { return &EmailLocalpart{ modName: modName, instName: instName, @@ -40,7 +40,7 @@ func NewEmailLocalpart(modName, instName string, _, _ []string) (module.Module, }, nil } -func (s *EmailLocalpart) Init(cfg *config.Map) error { +func (s *EmailLocalpart) Configure(inlineArgs []string, cfg *config.Map) error { return nil } diff --git a/internal/table/email_with_domain.go b/internal/table/email_with_domain.go index 4eb50b54..62d9c565 100644 --- a/internal/table/email_with_domain.go +++ b/internal/table/email_with_domain.go @@ -35,16 +35,17 @@ type EmailWithDomain struct { log log.Logger } -func NewEmailWithDomain(modName, instName string, _, inlineArgs []string) (module.Module, error) { +func NewEmailWithDomain(modName, instName string) (module.Module, error) { return &EmailWithDomain{ modName: modName, instName: instName, - domains: inlineArgs, log: log.Logger{Name: modName}, }, nil } -func (s *EmailWithDomain) Init(cfg *config.Map) error { +func (s *EmailWithDomain) Configure(inlineArgs []string, cfg *config.Map) error { + s.domains = inlineArgs + for _, d := range s.domains { if !address.ValidDomain(d) { return fmt.Errorf("%s: invalid domain: %s", s.modName, d) diff --git a/internal/table/file.go b/internal/table/file.go index a0286a9a..3b7fcc03 100644 --- a/internal/table/file.go +++ b/internal/table/file.go @@ -29,7 +29,6 @@ import ( "time" "github.com/foxcpp/maddy/framework/config" - "github.com/foxcpp/maddy/framework/hooks" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" ) @@ -50,7 +49,7 @@ type File struct { log log.Logger } -func NewFile(_, instName string, _, inlineArgs []string) (module.Module, error) { +func NewFile(_, instName string) (module.Module, error) { m := &File{ instName: instName, m: make(map[string][]string), @@ -59,14 +58,6 @@ func NewFile(_, instName string, _, inlineArgs []string) (module.Module, error) log: log.Logger{Name: FileModName}, } - switch len(inlineArgs) { - case 1: - m.file = inlineArgs[0] - case 0: - default: - return nil, fmt.Errorf("%s: cannot use multiple files with single %s, use %s multiple times to do so", FileModName, FileModName, FileModName) - } - return m, nil } @@ -78,7 +69,15 @@ func (f *File) InstanceName() string { return f.instName } -func (f *File) Init(cfg *config.Map) error { +func (f *File) Configure(inlineArgs []string, cfg *config.Map) error { + switch len(inlineArgs) { + case 1: + f.file = inlineArgs[0] + case 0: + default: + return fmt.Errorf("%s: cannot use multiple files with single %s, use %s multiple times to do so", FileModName, FileModName, FileModName) + } + var file string cfg.Bool("debug", true, false, &f.log.Debug) cfg.String("file", false, false, "", &file) @@ -100,11 +99,22 @@ func (f *File) Init(cfg *config.Map) error { f.log.Printf("ignoring non-existent file: %s", f.file) } + return nil +} + +func (f *File) Start() error { go f.reloader() - hooks.AddHook(hooks.EventReload, func() { - f.forceReload <- struct{}{} - }) + return nil +} +func (f *File) Reload() error { + f.forceReload <- struct{}{} + return nil +} + +func (f *File) Stop() error { + f.stopReloader <- struct{}{} + <-f.stopreloader return nil } @@ -181,12 +191,6 @@ func (f *File) reload() { f.mLck.Unlock() } -func (f *File) Close() error { - f.stopReloader <- struct{}{} - <-f.stopreloader - return nil -} - func readFile(path string, out map[string][]string) error { f, err := os.Open(path) if err != nil { diff --git a/internal/table/file_test.go b/internal/table/file_test.go index c51620d2..5c7389e3 100644 --- a/internal/table/file_test.go +++ b/internal/table/file_test.go @@ -98,15 +98,18 @@ func TestFileReload(t *testing.T) { } f.Close() - mod, err := NewFile("", "", nil, []string{f.Name()}) + mod, err := NewFile("", "") if err != nil { t.Fatal(err) } m := mod.(*File) + if err := m.Start(); err != nil { + t.Fatal(err) + } m.log = testutils.Logger(t, "file_map") - defer m.Close() + defer m.Stop() - if err := mod.Init(&config.Map{Block: config.Node{}}); err != nil { + if err := mod.Configure([]string{f.Name()}, &config.Map{Block: config.Node{}}); err != nil { t.Fatal(err) } @@ -149,15 +152,18 @@ func TestFileReload_Broken(t *testing.T) { } f.Close() - mod, err := NewFile("", "", nil, []string{f.Name()}) + mod, err := NewFile("", "") if err != nil { t.Fatal(err) } m := mod.(*File) + if err := m.Start(); err != nil { + t.Fatal(err) + } m.log = testutils.Logger(t, FileModName) - defer m.Close() + defer m.Stop() - if err := mod.Init(&config.Map{Block: config.Node{}}); err != nil { + if err := mod.Configure([]string{f.Name()}, &config.Map{Block: config.Node{}}); err != nil { t.Fatal(err) } @@ -194,15 +200,18 @@ func TestFileReload_Removed(t *testing.T) { } f.Close() - mod, err := NewFile("", "", nil, []string{f.Name()}) + mod, err := NewFile("", "") if err != nil { t.Fatal(err) } m := mod.(*File) + if err := m.Start(); err != nil { + t.Fatal(err) + } m.log = testutils.Logger(t, FileModName) - defer m.Close() + defer m.Stop() - if err := mod.Init(&config.Map{Block: config.Node{}}); err != nil { + if err := mod.Configure([]string{f.Name()}, &config.Map{Block: config.Node{}}); err != nil { t.Fatal(err) } diff --git a/internal/table/identity.go b/internal/table/identity.go index c405d3dc..6db17df8 100644 --- a/internal/table/identity.go +++ b/internal/table/identity.go @@ -30,14 +30,14 @@ type Identity struct { instName string } -func NewIdentity(modName, instName string, _, _ []string) (module.Module, error) { +func NewIdentity(modName, instName string) (module.Module, error) { return &Identity{ modName: modName, instName: instName, }, nil } -func (s *Identity) Init(cfg *config.Map) error { +func (s *Identity) Configure(inlineArgs []string, cfg *config.Map) error { return nil } diff --git a/internal/table/regexp.go b/internal/table/regexp.go index 069be6ce..0c22cd8a 100644 --- a/internal/table/regexp.go +++ b/internal/table/regexp.go @@ -29,9 +29,8 @@ import ( ) type Regexp struct { - modName string - instName string - inlineArgs []string + modName string + instName string re *regexp.Regexp replacements []string @@ -39,15 +38,14 @@ type Regexp struct { expandPlaceholders bool } -func NewRegexp(modName, instName string, _, inlineArgs []string) (module.Module, error) { +func NewRegexp(modName, instName string) (module.Module, error) { return &Regexp{ - modName: modName, - instName: instName, - inlineArgs: inlineArgs, + modName: modName, + instName: instName, }, nil } -func (r *Regexp) Init(cfg *config.Map) error { +func (r *Regexp) Configure(inlineArgs []string, cfg *config.Map) error { var ( fullMatch bool caseInsensitive bool @@ -59,9 +57,9 @@ func (r *Regexp) Init(cfg *config.Map) error { return err } - regex := r.inlineArgs[0] - if len(r.inlineArgs)> 1 { - r.replacements = r.inlineArgs[1:] + regex := inlineArgs[0] + if len(inlineArgs)> 1 { + r.replacements = inlineArgs[1:] } if fullMatch { diff --git a/internal/table/sql_query.go b/internal/table/sql_query.go index c15f710a..96bce8f0 100644 --- a/internal/table/sql_query.go +++ b/internal/table/sql_query.go @@ -32,6 +32,7 @@ import ( type SQL struct { modName string instName string + prepare func() error namedArgs bool @@ -43,7 +44,7 @@ type SQL struct { del *sql.Stmt } -func NewSQL(modName, instName string, _, _ []string) (module.Module, error) { +func NewSQL(modName, instName string) (module.Module, error) { return &SQL{ modName: modName, instName: instName, @@ -58,7 +59,7 @@ func (s *SQL) InstanceName() string { return s.instName } -func (s *SQL) Init(cfg *config.Map) error { +func (s *SQL) Configure(inlineArgs []string, cfg *config.Map) error { var ( driver string initQueries []string @@ -94,46 +95,52 @@ func (s *SQL) Init(cfg *config.Map) error { return config.NodeErr(cfg.Block, "failed to open db: %v", err) } s.db = db - - for _, init := range initQueries { - if _, err := db.Exec(init); err != nil { - return config.NodeErr(cfg.Block, "init query failed: %v", err) + s.prepare = func() error { + for _, init := range initQueries { + if _, err := db.Exec(init); err != nil { + return config.NodeErr(cfg.Block, "init query failed: %v", err) + } } - } - s.lookup, err = db.Prepare(lookupQuery) - if err != nil { - return config.NodeErr(cfg.Block, "failed to prepare lookup query: %v", err) - } - if addQuery != "" { - s.add, err = db.Prepare(addQuery) + s.lookup, err = db.Prepare(lookupQuery) if err != nil { - return config.NodeErr(cfg.Block, "failed to prepare add query: %v", err) + return fmt.Errorf("failed to prepare lookup query: %v", err) } - } - if listQuery != "" { - s.list, err = db.Prepare(listQuery) - if err != nil { - return config.NodeErr(cfg.Block, "failed to prepare list query: %v", err) + if addQuery != "" { + s.add, err = db.Prepare(addQuery) + if err != nil { + return fmt.Errorf("failed to prepare add query: %v", err) + } } - } - if setQuery != "" { - s.set, err = db.Prepare(setQuery) - if err != nil { - return config.NodeErr(cfg.Block, "failed to prepare set query: %v", err) + if listQuery != "" { + s.list, err = db.Prepare(listQuery) + if err != nil { + return config.NodeErr(cfg.Block, "failed to prepare list query: %v", err) + } } - } - if removeQuery != "" { - s.del, err = db.Prepare(removeQuery) - if err != nil { - return config.NodeErr(cfg.Block, "failed to prepare del query: %v", err) + if setQuery != "" { + s.set, err = db.Prepare(setQuery) + if err != nil { + return config.NodeErr(cfg.Block, "failed to prepare set query: %v", err) + } + } + if removeQuery != "" { + s.del, err = db.Prepare(removeQuery) + if err != nil { + return config.NodeErr(cfg.Block, "failed to prepare del query: %v", err) + } } + return nil } return nil } -func (s *SQL) Close() error { +func (s *SQL) Start() error { + return s.prepare() +} + +func (s *SQL) Stop() error { s.lookup.Close() return s.db.Close() } diff --git a/internal/table/sql_query_test.go b/internal/table/sql_query_test.go index fd160f76..99269c62 100644 --- a/internal/table/sql_query_test.go +++ b/internal/table/sql_query_test.go @@ -33,12 +33,12 @@ import ( func TestSQL(t *testing.T) { path := testutils.Dir(t) - mod, err := NewSQL("sql_table", "", nil, nil) + mod, err := NewSQL("sql_table", "") if err != nil { t.Fatal("Module create failed:", err) } tbl := mod.(*SQL) - err = tbl.Init(config.NewMap(nil, config.Node{ + err = tbl.Configure(nil, config.NewMap(nil, config.Node{ Children: []config.Node{ { Name: "driver", @@ -66,6 +66,9 @@ func TestSQL(t *testing.T) { if err != nil { t.Fatal("Init failed:", err) } + if err := tbl.Start(); err != nil { + t.Fatal(err) + } check := func(key, res string, ok, fail bool) { t.Helper() diff --git a/internal/table/sql_table.go b/internal/table/sql_table.go index e793dc48..edadb919 100644 --- a/internal/table/sql_table.go +++ b/internal/table/sql_table.go @@ -34,7 +34,7 @@ type SQLTable struct { wrapped *SQL } -func NewSQLTable(modName, instName string, _, _ []string) (module.Module, error) { +func NewSQLTable(modName, instName string) (module.Module, error) { return &SQLTable{ modName: modName, instName: instName, @@ -54,7 +54,7 @@ func (s *SQLTable) InstanceName() string { return s.instName } -func (s *SQLTable) Init(cfg *config.Map) error { +func (s *SQLTable) Configure(inlineArgs []string, cfg *config.Map) error { var ( driver string dsnParts []string @@ -99,7 +99,7 @@ func (s *SQLTable) Init(cfg *config.Map) error { delQuery = fmt.Sprintf("DELETE FROM %s WHERE %s = 1ドル", tableName, keyColumn) } - return s.wrapped.Init(config.NewMap(cfg.Globals, config.Node{ + return s.wrapped.Configure(nil, config.NewMap(cfg.Globals, config.Node{ Children: []config.Node{ { Name: "driver", @@ -144,8 +144,10 @@ func (s *SQLTable) Init(cfg *config.Map) error { })) } -func (s *SQLTable) Close() error { - return s.wrapped.Close() +func (s *SQLTable) Start() error { return s.wrapped.Start() } + +func (s *SQLTable) Stop() error { + return s.wrapped.Stop() } func (s *SQLTable) Lookup(ctx context.Context, val string) (string, bool, error) { diff --git a/internal/table/static.go b/internal/table/static.go index e55ffd58..4444f991 100644 --- a/internal/table/static.go +++ b/internal/table/static.go @@ -32,7 +32,7 @@ type Static struct { m map[string][]string } -func NewStatic(modName, instName string, _, _ []string) (module.Module, error) { +func NewStatic(modName, instName string) (module.Module, error) { return &Static{ modName: modName, instName: instName, @@ -40,7 +40,7 @@ func NewStatic(modName, instName string, _, _ []string) (module.Module, error) { }, nil } -func (s *Static) Init(cfg *config.Map) error { +func (s *Static) Configure(inlineArgs []string, cfg *config.Map) error { cfg.Callback("entry", func(_ *config.Map, node config.Node) error { if len(node.Args) < 2 { return config.NodeErr(node, "expected at least one value") diff --git a/internal/target/queue/queue.go b/internal/target/queue/queue.go index 264a70fb..663046b3 100644 --- a/internal/target/queue/queue.go +++ b/internal/target/queue/queue.go @@ -30,7 +30,7 @@ All scheduled deliveries are attempted to the configured DeliveryTarget. All metadata is preserved on disk. Failure status is determined on per-recipient basis: - - Delivery.Start fail handled as a failure for all recipients. + - Delivery.StartDelivery fail handled as a failure for all recipients. - Delivery.AddRcpt fail handled as a failure for the corresponding recipient. - Delivery.Body fail handled as a failure for all recipients. - If Delivery implements PartialDelivery, then @@ -133,6 +133,7 @@ type Queue struct { initialRetryTime time.Duration retryTimeScale float64 maxTries int + maxParallelism int // If any delivery is scheduled in less than postInitDelay // after Init, its delay will be increased by postInitDelay. @@ -188,7 +189,7 @@ type queueSlot struct { Body buffer.Buffer } -func NewQueue(_, instName string, _, inlineArgs []string) (module.Module, error) { +func NewQueue(_, instName string) (module.Module, error) { q := &Queue{ name: instName, initialRetryTime: 15 * time.Minute, @@ -196,22 +197,22 @@ func NewQueue(_, instName string, _, inlineArgs []string) (module.Module, error) postInitDelay: 10 * time.Second, Log: log.Logger{Name: "queue"}, } + return q, nil +} + +func (q *Queue) Configure(inlineArgs []string, cfg *config.Map) error { switch len(inlineArgs) { case 0: // Not inline definition. case 1: q.location = inlineArgs[0] default: - return nil, errors.New("queue: wrong amount of inline arguments") + return errors.New("queue: wrong amount of inline arguments") } - return q, nil -} -func (q *Queue) Init(cfg *config.Map) error { - var maxParallelism int cfg.Bool("debug", true, false, &q.Log.Debug) cfg.Int("max_tries", false, false, 20, &q.maxTries) - cfg.Int("max_parallelism", false, false, 16, &maxParallelism) + cfg.Int("max_parallelism", false, false, 16, &q.maxParallelism) cfg.String("location", false, false, q.location, &q.location) cfg.Custom("target", false, true, nil, modconfig.DeliveryDirective, &q.Target) cfg.String("hostname", true, true, "", &q.hostname) @@ -242,8 +243,11 @@ func (q *Queue) Init(cfg *config.Map) error { if err := os.MkdirAll(q.location, os.ModePerm); err != nil { return err } + return nil +} - return q.start(maxParallelism) +func (q *Queue) Start() error { + return q.start(q.maxParallelism) } func (q *Queue) start(maxParallelism int) error { @@ -259,7 +263,7 @@ func (q *Queue) start(maxParallelism int) error { return nil } -func (q *Queue) Close() error { +func (q *Queue) Stop() error { q.wheel.Close() q.deliveryWg.Wait() @@ -473,16 +477,16 @@ func (q *Queue) deliver(meta *QueueMetadata, header textproto.Header, body buffe defer msgTask.End() mailCtx, mailTask := trace.NewTask(msgCtx, "MAIL FROM") - delivery, err := q.Target.Start(mailCtx, msgMeta, meta.From) + delivery, err := q.Target.StartDelivery(mailCtx, msgMeta, meta.From) mailTask.End() if err != nil { - dl.Debugf("target.Start failed: %v", err) + dl.Debugf("target.StartDelivery failed: %v", err) for _, rcpt := range meta.To { perr.Errs[rcpt] = err } return perr } - dl.Debugf("target.Start OK") + dl.Debugf("target.StartDelivery OK") var acceptedRcpts []string for _, rcpt := range meta.To { @@ -605,7 +609,7 @@ func (qd *queueDelivery) Commit(ctx context.Context) error { return nil } -func (q *Queue) Start(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { +func (q *Queue) StartDelivery(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { meta := &QueueMetadata{ MsgMeta: msgMeta, From: mailFrom, @@ -958,7 +962,7 @@ func (q *Queue) emitDSN(meta *QueueMetadata, header textproto.Header, failedRcpt defer msgTask.End() mailCtx, mailTask := trace.NewTask(msgCtx, "MAIL FROM") - dsnDelivery, err := q.dsnPipeline.Start(mailCtx, dsnMeta, "") + dsnDelivery, err := q.dsnPipeline.StartDelivery(mailCtx, dsnMeta, "") mailTask.End() if err != nil { dl.Error("failed to enqueue DSN", err, "dsn_id", dsnID) diff --git a/internal/target/queue/queue_test.go b/internal/target/queue/queue_test.go index ff9a4f6a..205ba891 100644 --- a/internal/target/queue/queue_test.go +++ b/internal/target/queue/queue_test.go @@ -51,13 +51,13 @@ func newTestQueue(t *testing.T, target module.DeliveryTarget) *Queue { func cleanQueue(t *testing.T, q *Queue) { t.Log("--- queue.Close") - if err := q.Close(); err != nil { + if err := q.Stop(); err != nil { t.Fatal("queue.Close:", err) } } func newTestQueueDir(t *testing.T, target module.DeliveryTarget, dir string) *Queue { - mod, _ := NewQueue("", "queue", nil, nil) + mod, _ := NewQueue("", "queue") q := mod.(*Queue) q.initialRetryTime = 0 q.retryTimeScale = 1 @@ -159,7 +159,7 @@ func (utd *unreliableTargetDelivery) Commit(ctx context.Context) error { return nil } -func (ut *unreliableTarget) Start(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { +func (ut *unreliableTarget) StartDelivery(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { if ut.bodyFailuresPartial != nil { return &unreliableTargetDeliveryPartial{ &unreliableTargetDelivery{ @@ -245,7 +245,7 @@ func TestQueueDelivery(t *testing.T) { // Wait for the delivery to complete and stop processing. msg := readMsgChanTimeout(t, dt.committed, 5*time.Second) - q.Close() + q.Stop() testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester1@example.org", "tester2@example.org"}, "") @@ -269,7 +269,7 @@ func TestQueueDelivery_PermanentFail_NonPartial(t *testing.T) { // Queue will abort a delivery if it fails for all recipients. readMsgChanTimeout(t, dt.aborted, 5*time.Second) - q.Close() + q.Stop() // Delivery is failed permanently, hence no retry should be rescheduled. checkQueueDir(t, q, []string{}) @@ -296,7 +296,7 @@ func TestQueueDelivery_PermanentFail_Partial(t *testing.T) { // Here delivery fails for recipients too, but this is reported using PartialDelivery. readMsgChanTimeout(t, dt.aborted, 5*time.Second) - q.Close() + q.Stop() checkQueueDir(t, q, []string{}) } @@ -322,7 +322,7 @@ func TestQueueDelivery_TemporaryFail(t *testing.T) { msg := readMsgChanTimeout(t, dt.committed, 5*time.Second) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester1@example.org", "tester2@example.org"}, "") - q.Close() + q.Stop() // No more retries scheduled, queue storage is clear. defer checkQueueDir(t, q, []string{}) } @@ -355,7 +355,7 @@ func TestQueueDelivery_TemporaryFail_Partial(t *testing.T) { msg = readMsgChanTimeout(t, dt.committed, 5000*time.Second) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester2@example.org"}, "") - q.Close() + q.Stop() // No more retries scheduled, queue storage is clear. checkQueueDir(t, q, []string{}) } @@ -395,7 +395,7 @@ func TestQueueDelivery_MultipleAttempts(t *testing.T) { msg = readMsgChanTimeout(t, dt.committed, 5*time.Second) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester2@example.org"}, "") - q.Close() + q.Stop() // No more retries should be scheduled. checkQueueDir(t, q, []string{}) } @@ -420,7 +420,7 @@ func TestQueueDelivery_PermanentRcptReject(t *testing.T) { msg := readMsgChanTimeout(t, dt.committed, 5*time.Second) testutils.CheckMsgID(t, msg, "tester@example.org", []string{"tester2@example.org"}, "") - q.Close() + q.Stop() // No more retries should be scheduled. checkQueueDir(t, q, []string{}) } @@ -454,7 +454,7 @@ func TestQueueDelivery_TemporaryRcptReject(t *testing.T) { msg = readMsgChanTimeout(t, dt.committed, 5*time.Second) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester1@example.org"}, "") - q.Close() + q.Stop() // No more retries should be scheduled. checkQueueDir(t, q, []string{}) } @@ -488,7 +488,7 @@ func TestQueueDelivery_SerializationRoundtrip(t *testing.T) { testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester2@example.org"}, "") // Then stop it. - q.Close() + q.Stop() // Make sure it is saved. checkQueueDir(t, q, []string{deliveryID}) @@ -501,7 +501,7 @@ func TestQueueDelivery_SerializationRoundtrip(t *testing.T) { testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester1@example.org"}, "") // Close it again. - q.Close() + q.Stop() // No more retries should be scheduled. checkQueueDir(t, q, []string{}) } @@ -535,7 +535,7 @@ func TestQueueDelivery_DeserlizationCleanUp(t *testing.T) { msg := readMsgChanTimeout(t, dt.committed, 5*time.Second) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester2@example.org"}, "") - q.Close() + q.Stop() if err := os.Remove(filepath.Join(q.location, deliveryID+fileSuffix)); err != nil { t.Fatal(err) @@ -543,7 +543,7 @@ func TestQueueDelivery_DeserlizationCleanUp(t *testing.T) { // Dangling files should be removed during load. q = newTestQueueDir(t, &dt, q.location) - q.Close() + q.Stop() // Nothing should be left. checkQueueDir(t, q, []string{}) @@ -606,9 +606,9 @@ func TestQueueDelivery_AbortNoDangling(t *testing.T) { DontTraceSender: true, ID: encodedID, } - delivery, err := q.Start(context.Background(), &ctx, "test3@example.org") + delivery, err := q.StartDelivery(context.Background(), &ctx, "test3@example.org") if err != nil { - t.Fatalf("unexpected Start err: %v", err) + t.Fatalf("unexpected StartDelivery err: %v", err) } for _, rcpt := range [...]string{"test@example.org", "test2@example.org"} { if err := delivery.AddRcpt(context.Background(), rcpt, smtp.RcptOptions{}); err != nil { @@ -786,9 +786,9 @@ func TestQueueDSN_RcptRewrite(t *testing.T) { }, ID: encodedID, } - delivery, err := q.Start(context.Background(), &ctx, "test3@example.org") + delivery, err := q.StartDelivery(context.Background(), &ctx, "test3@example.org") if err != nil { - t.Fatalf("unexpected Start err: %v", err) + t.Fatalf("unexpected StartDelivery err: %v", err) } for _, rcpt := range [...]string{"test@example.org", "test2@example.org"} { if err := delivery.AddRcpt(context.Background(), rcpt, smtp.RcptOptions{}); err != nil { diff --git a/internal/target/remote/mxauth_test.go b/internal/target/remote/mxauth_test.go index 2bd8d2d4..e332bdb4 100644 --- a/internal/target/remote/mxauth_test.go +++ b/internal/target/remote/mxauth_test.go @@ -63,7 +63,7 @@ func TestRemoteDelivery_AuthMX_MTASTS(t *testing.T) { testSTSPolicy(t, zones, mtastsGet), }) tgt.tlsConfig = clientCfg - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -108,7 +108,7 @@ func TestRemoteDelivery_MTASTS_SkipNonMatching(t *testing.T) { &localPolicy{minMXLevel: module.MX_MTASTS}, }) tgt.tlsConfig = clientCfg - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -148,7 +148,7 @@ func TestRemoteDelivery_AuthMX_MTASTS_Fail(t *testing.T) { &localPolicy{minMXLevel: module.MX_MTASTS}, }) tgt.tlsConfig = clientCfg - defer tgt.Close() + defer tgt.Stop() _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -189,7 +189,7 @@ func TestRemoteDelivery_AuthMX_MTASTS_NoTLS(t *testing.T) { testSTSPolicy(t, zones, mtastsGet), &localPolicy{minMXLevel: module.MX_MTASTS}, }) - defer tgt.Close() + defer tgt.Stop() _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -230,7 +230,7 @@ func TestRemoteDelivery_AuthMX_MTASTS_RequirePKIX(t *testing.T) { testSTSPolicy(t, zones, mtastsGet), &localPolicy{minMXLevel: module.MX_MTASTS}, }) - defer tgt.Close() + defer tgt.Stop() _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -280,7 +280,7 @@ func TestRemoteDelivery_AuthMX_MTASTS_NoPolicy(t *testing.T) { testSTSPolicy(t, zones, mtastsGet), &localPolicy{minMXLevel: module.MX_MTASTS}, }) - defer tgt.Close() + defer tgt.Stop() _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -322,7 +322,7 @@ func TestRemoteDelivery_AuthMX_DNSSEC(t *testing.T) { extResolver.Cfg.Port = strconv.Itoa(addr.Port) tgt := testTarget(t, zones, extResolver, nil) - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -363,7 +363,7 @@ func TestRemoteDelivery_AuthMX_DNSSEC_Fail(t *testing.T) { tgt := testTarget(t, zones, extResolver, []module.MXAuthPolicy{ &localPolicy{minMXLevel: module.MX_DNSSEC}, }) - defer tgt.Close() + defer tgt.Stop() _, err = testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -405,7 +405,7 @@ func TestRemoteDelivery_REQUIRETLS(t *testing.T) { testSTSPolicy(t, zones, mtastsGet), }) tgt.tlsConfig = clientCfg - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDeliveryMeta(t, tgt, "test@example.com", []string{"test@example.invalid"}, &module.MsgMetadata{ OriginalFrom: "test@example.com", @@ -446,7 +446,7 @@ func TestRemoteDelivery_REQUIRETLS_Fail(t *testing.T) { testSTSPolicy(t, zones, mtastsGet), }) tgt.tlsConfig = clientCfg - defer tgt.Close() + defer tgt.Stop() if _, err := testutils.DoTestDeliveryErrMeta(t, tgt, "test@example.com", []string{"test@example.invalid"}, &module.MsgMetadata{ OriginalFrom: "test@example.com", @@ -492,7 +492,7 @@ func TestRemoteDelivery_REQUIRETLS_Relaxed(t *testing.T) { }) tgt.relaxedREQUIRETLS = true tgt.tlsConfig = clientCfg - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDeliveryMeta(t, tgt, "test@example.com", []string{"test@example.invalid"}, &module.MsgMetadata{ OriginalFrom: "test@example.com", @@ -529,7 +529,7 @@ func TestRemoteDelivery_REQUIRETLS_Relaxed_NoMXAuth(t *testing.T) { }) tgt.relaxedREQUIRETLS = true tgt.tlsConfig = clientCfg - defer tgt.Close() + defer tgt.Stop() if _, err := testutils.DoTestDeliveryErrMeta(t, tgt, "test@example.com", []string{"test@example.invalid"}, &module.MsgMetadata{ OriginalFrom: "test@example.com", @@ -575,7 +575,7 @@ func TestRemoteDelivery_REQUIRETLS_Relaxed_NoTLS(t *testing.T) { }) tgt.relaxedREQUIRETLS = true tgt.tlsConfig = nil - defer tgt.Close() + defer tgt.Stop() if _, err := testutils.DoTestDeliveryErrMeta(t, tgt, "test@example.com", []string{"test@example.invalid"}, &module.MsgMetadata{ OriginalFrom: "test@example.com", @@ -626,7 +626,7 @@ func TestRemoteDelivery_REQUIRETLS_Relaxed_TLSFail(t *testing.T) { srv.TLSConfig.MinVersion = tls.VersionTLS11 srv.TLSConfig.MaxVersion = tls.VersionTLS11 tgt.tlsConfig = clientCfg - defer tgt.Close() + defer tgt.Stop() if _, err := testutils.DoTestDeliveryErrMeta(t, tgt, "test@example.com", []string{"test@example.invalid"}, &module.MsgMetadata{ OriginalFrom: "test@example.com", diff --git a/internal/target/remote/policy_group.go b/internal/target/remote/policy_group.go index 68a2202b..992bcfa4 100644 --- a/internal/target/remote/policy_group.go +++ b/internal/target/remote/policy_group.go @@ -39,7 +39,7 @@ type PolicyGroup struct { pols map[string]module.MXAuthPolicy } -func (pg *PolicyGroup) Init(cfg *config.Map) error { +func (pg *PolicyGroup) Configure(inlineArgs []string, cfg *config.Map) error { var debugLog bool cfg.Bool("debug", true, false, &debugLog) cfg.AllowUnknown() @@ -87,16 +87,16 @@ func (pg *PolicyGroup) Init(cfg *config.Map) error { return nil } -func (PolicyGroup) Name() string { +func (*PolicyGroup) Name() string { return "mx_auth" } -func (pg PolicyGroup) InstanceName() string { +func (pg *PolicyGroup) InstanceName() string { return pg.instName } func init() { - module.Register("mx_auth", func(_, instName string, _, _ []string) (module.Module, error) { + module.Register("mx_auth", func(_, instName string) (module.Module, error) { return &PolicyGroup{ instName: instName, pols: map[string]module.MXAuthPolicy{}, diff --git a/internal/target/remote/remote.go b/internal/target/remote/remote.go index d4c42ed6..79b683fd 100644 --- a/internal/target/remote/remote.go +++ b/internal/target/remote/remote.go @@ -87,10 +87,7 @@ type Target struct { var _ module.DeliveryTarget = &Target{} -func New(_, instName string, _, inlineArgs []string) (module.Module, error) { - if len(inlineArgs) != 0 { - return nil, errors.New("remote: inline arguments are not used") - } +func New(_, instName string) (module.Module, error) { // Keep this synchronized with testTarget. return &Target{ name: instName, @@ -100,7 +97,11 @@ func New(_, instName string, _, inlineArgs []string) (module.Module, error) { }, nil } -func (rt *Target) Init(cfg *config.Map) error { +func (rt *Target) Configure(inlineArgs []string, cfg *config.Map) error { + if len(inlineArgs) != 0 { + return errors.New("remote: inline arguments are not used") + } + var err error rt.extResolver, err = dns.NewExtResolver() if err != nil { @@ -184,7 +185,11 @@ func (rt *Target) Init(cfg *config.Map) error { return nil } -func (rt *Target) Close() error { +func (rt *Target) Start() error { + return nil +} + +func (rt *Target) Stop() error { rt.pool.Close() return nil @@ -210,11 +215,11 @@ type remoteDelivery struct { policies []module.DeliveryMXAuthPolicy } -func (rt *Target) Start(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { +func (rt *Target) StartDelivery(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { policies := make([]module.DeliveryMXAuthPolicy, 0, len(rt.policies)) if !(msgMeta.TLSRequireOverride && rt.allowSecOverride) { for _, p := range rt.policies { - policies = append(policies, p.Start(msgMeta)) + policies = append(policies, p.StartDelivery(msgMeta)) } } diff --git a/internal/target/remote/remote_test.go b/internal/target/remote/remote_test.go index 4998e0c6..99b2492a 100644 --- a/internal/target/remote/remote_test.go +++ b/internal/target/remote/remote_test.go @@ -72,12 +72,12 @@ func testTarget(t *testing.T, zones map[string]mockdns.Zone, extResolver *dns.Ex } func testSTSPolicy(t *testing.T, zones map[string]mockdns.Zone, mtastsGet func(context.Context, string) (*mtasts.Policy, error)) *mtastsPolicy { - m, err := NewMTASTSPolicy("mx_auth.mtasts", "test", nil, nil) + m, err := NewMTASTSPolicy("mx_auth.mtasts", "test") if err != nil { t.Fatal(err) } p := m.(*mtastsPolicy) - err = p.Init(config.NewMap(nil, config.Node{ + err = p.Configure(nil, config.NewMap(nil, config.Node{ Children: []config.Node{ { Name: "cache", @@ -98,12 +98,12 @@ func testSTSPolicy(t *testing.T, zones map[string]mockdns.Zone, mtastsGet func(c } func testDANEPolicy(t *testing.T, extR *dns.ExtResolver) *danePolicy { - m, err := NewDANEPolicy("mx_auth.dane", "test", nil, nil) + m, err := NewDANEPolicy("mx_auth.dane", "test") if err != nil { t.Fatal(err) } p := m.(*danePolicy) - err = p.Init(config.NewMap(nil, config.Node{ + err = p.Configure(nil, config.NewMap(nil, config.Node{ Children: nil, })) if err != nil { @@ -129,7 +129,7 @@ func TestRemoteDelivery(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -146,9 +146,9 @@ func TestRemoteDelivery_NoMXFallback(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() - delivery, err := tgt.Start(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") + delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { t.Fatal(err) } @@ -176,7 +176,7 @@ func TestRemoteDelivery_EmptySender(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDelivery(t, tgt, "", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "", []string{"test@example.invalid"}) @@ -202,7 +202,7 @@ func TestRemoteDelivery_IPLiteral(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@[127.0.0.1]"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@[127.0.0.1]"}) @@ -219,7 +219,7 @@ func TestRemoteDelivery_FallbackMX(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -239,7 +239,7 @@ func TestRemoteDelivery_BodyNonAtomic(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() c := multipleErrs{ errs: map[string]error{}, @@ -267,9 +267,9 @@ func TestRemoteDelivery_Abort(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() - delivery, err := tgt.Start(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") + delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { t.Fatal(err) } @@ -297,9 +297,9 @@ func TestRemoteDelivery_CommitWithoutBody(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() - delivery, err := tgt.Start(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") + delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { t.Fatal(err) } @@ -334,9 +334,9 @@ func TestRemoteDelivery_MAILFROMErr(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() - delivery, err := tgt.Start(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") + delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { t.Fatal(err) } @@ -360,9 +360,9 @@ func TestRemoteDelivery_NoMX(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() - delivery, err := tgt.Start(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") + delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { t.Fatal(err) } @@ -390,9 +390,9 @@ func TestRemoteDelivery_NullMX(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() - delivery, err := tgt.Start(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") + delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { t.Fatal(err) } @@ -419,11 +419,11 @@ func TestRemoteDelivery_Quarantined(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() meta := module.MsgMetadata{ID: "test..."} - delivery, err := tgt.Start(context.Background(), &meta, "test@example.com") + delivery, err := tgt.StartDelivery(context.Background(), &meta, "test@example.com") if err != nil { t.Fatal(err) } @@ -467,9 +467,9 @@ func TestRemoteDelivery_MAILFROMErr_Repeated(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() - delivery, err := tgt.Start(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") + delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { t.Fatal(err) } @@ -507,9 +507,9 @@ func TestRemoteDelivery_RcptErr(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() - delivery, err := tgt.Start(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") + delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { t.Fatal(err) } @@ -558,7 +558,7 @@ func TestRemoteDelivery_DownMX(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -581,7 +581,7 @@ func TestRemoteDelivery_AllMXDown(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -612,7 +612,7 @@ func TestRemoteDelivery_Split(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid", "test@example2.invalid"}) @@ -651,9 +651,9 @@ func TestRemoteDelivery_Split_Fail(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() - delivery, err := tgt.Start(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") + delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { t.Fatal(err) } @@ -704,9 +704,9 @@ func TestRemoteDelivery_BodyErr(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() - delivery, err := tgt.Start(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") + delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { t.Fatal(err) } @@ -758,9 +758,9 @@ func TestRemoteDelivery_Split_BodyErr(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() - delivery, err := tgt.Start(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") + delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { t.Fatal(err) } @@ -814,9 +814,9 @@ func TestRemoteDelivery_Split_BodyErr_NonAtomic(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Close() + defer tgt.Stop() - delivery, err := tgt.Start(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") + delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { t.Fatal(err) } @@ -874,7 +874,7 @@ func TestRemoteDelivery_TLSErrFallback(t *testing.T) { tgt := testTarget(t, zones, nil, nil) tgt.tlsConfig = clientCfg - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -896,7 +896,7 @@ func TestRemoteDelivery_RequireTLS_Missing(t *testing.T) { tgt := testTarget(t, zones, nil, []module.MXAuthPolicy{ &localPolicy{minTLSLevel: module.TLSEncrypted}, }) - defer tgt.Close() + defer tgt.Stop() _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -921,7 +921,7 @@ func TestRemoteDelivery_RequireTLS_Present(t *testing.T) { &localPolicy{minTLSLevel: module.TLSEncrypted}, }) tgt.tlsConfig = clientCfg - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -950,7 +950,7 @@ func TestRemoteDelivery_RequireTLS_NoErrFallback(t *testing.T) { &localPolicy{minTLSLevel: module.TLSEncrypted}, }) tgt.tlsConfig = clientCfg - defer tgt.Close() + defer tgt.Stop() _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -975,7 +975,7 @@ func TestRemoteDelivery_TLS_FallbackNoVerify(t *testing.T) { tgt := testTarget(t, zones, nil, []module.MXAuthPolicy{ &localPolicy{minTLSLevel: module.TLSEncrypted}, }) - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -1008,7 +1008,7 @@ func TestRemoteDelivery_TLS_FallbackPlaintext(t *testing.T) { tgt := testTarget(t, zones, nil, nil) tgt.tlsConfig = clientCfg - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -1041,7 +1041,7 @@ func TestRemoteDelivery_ConnReuse(t *testing.T) { tgt := testTarget(t, zones, nil, nil) tgt.connReuseLimit = 5 - defer tgt.Close() + defer tgt.Stop() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) diff --git a/internal/target/remote/security.go b/internal/target/remote/security.go index a8177fb1..51572b89 100644 --- a/internal/target/remote/security.go +++ b/internal/target/remote/security.go @@ -52,7 +52,7 @@ type ( } ) -func NewMTASTSPolicy(_, instName string, _, _ []string) (module.Module, error) { +func NewMTASTSPolicy(_, instName string) (module.Module, error) { return &mtastsPolicy{ instName: instName, log: log.Logger{Name: "mx_auth.mtasts", Debug: log.DefaultLogger.Debug}, @@ -71,7 +71,7 @@ func (c *mtastsPolicy) Weight() int { return 10 } -func (c *mtastsPolicy) Init(cfg *config.Map) error { +func (c *mtastsPolicy) Configure(inlineArgs []string, cfg *config.Map) error { var ( storeType string storeDir string @@ -99,6 +99,11 @@ func (c *mtastsPolicy) Init(cfg *config.Map) error { return nil } +func (c *mtastsPolicy) Start() error { + c.StartUpdater() + return nil +} + // StartUpdater starts a goroutine to update MTA-STS cache periodically until // Close is called. // @@ -108,6 +113,15 @@ func (c *mtastsPolicy) StartUpdater() { go c.updater() } +func (c *mtastsPolicy) Stop() error { + if c.updaterStop != nil { + c.updaterStop <- struct{}{} + <-c.updaterstop + c.updaterStop = nil + } + return nil +} + func (c *mtastsPolicy) updater() { defer func() { if err := recover(); err != nil { @@ -142,22 +156,13 @@ func (c *mtastsPolicy) updater() { } } -func (c *mtastsPolicy) Start(msgMeta *module.MsgMetadata) module.DeliveryMXAuthPolicy { +func (c *mtastsPolicy) StartDelivery(msgMeta *module.MsgMetadata) module.DeliveryMXAuthPolicy { return &mtastsDelivery{ c: c, log: target.DeliveryLogger(c.log, msgMeta), } } -func (c *mtastsPolicy) Close() error { - if c.updaterStop != nil { - c.updaterStop <- struct{}{} - <-c.updaterstop - c.updaterStop = nil - } - return nil -} - func (c *mtastsDelivery) PrepareDomain(ctx context.Context, domain string) { c.policyFut = future.New() go func() { @@ -237,7 +242,7 @@ type stsPreloadPolicy struct { instName string } -func NewSTSPreload(_, instName string, _, _ []string) (module.Module, error) { +func NewSTSPreload(_, instName string) (module.Module, error) { return &stsPreloadPolicy{ instName: instName, log: log.Logger{Name: "mx_auth.sts_preload", Debug: log.DefaultLogger.Debug}, @@ -256,7 +261,7 @@ func (c *stsPreloadPolicy) Weight() int { return 30 // after MTA-STS } -func (c *stsPreloadPolicy) Init(cfg *config.Map) error { +func (c *stsPreloadPolicy) Configure(inlineArgs []string, cfg *config.Map) error { c.log.Println("sts_preload module is deprecated and is no-op as the list is expired and unmaintained") var ( @@ -276,7 +281,7 @@ type preloadDelivery struct { *stsPreloadPolicy } -func (p *stsPreloadPolicy) Start(*module.MsgMetadata) module.DeliveryMXAuthPolicy { +func (p *stsPreloadPolicy) StartDelivery(*module.MsgMetadata) module.DeliveryMXAuthPolicy { return &preloadDelivery{stsPreloadPolicy: p} } @@ -291,15 +296,11 @@ func (p *preloadDelivery) CheckConn(ctx context.Context, mxLevel module.MXLevel, return tlsLevel, nil } -func (p *stsPreloadPolicy) Close() error { - return nil -} - type dnssecPolicy struct { instName string } -func NewDNSSECPolicy(_, instName string, _, _ []string) (module.Module, error) { +func NewDNSSECPolicy(_, instName string) (module.Module, error) { return &dnssecPolicy{ instName: instName, }, nil @@ -317,19 +318,15 @@ func (c *dnssecPolicy) Weight() int { return 1 } -func (c *dnssecPolicy) Init(cfg *config.Map) error { +func (c *dnssecPolicy) Configure(inlineArgs []string, cfg *config.Map) error { _, err := cfg.Process() // will fail if there is any directive return err } -func (dnssecPolicy) Start(*module.MsgMetadata) module.DeliveryMXAuthPolicy { +func (dnssecPolicy) StartDelivery(*module.MsgMetadata) module.DeliveryMXAuthPolicy { return dnssecPolicy{} } -func (dnssecPolicy) Close() error { - return nil -} - func (dnssecPolicy) Reset(*module.MsgMetadata) {} func (dnssecPolicy) PrepareDomain(ctx context.Context, domain string) {} func (dnssecPolicy) PrepareConn(ctx context.Context, mx string) {} @@ -357,7 +354,7 @@ type ( } ) -func NewDANEPolicy(_, instName string, _, _ []string) (module.Module, error) { +func NewDANEPolicy(_, instName string) (module.Module, error) { return &danePolicy{ instName: instName, log: log.Logger{Name: "remote/dane", Debug: log.DefaultLogger.Debug}, @@ -376,7 +373,7 @@ func (c *danePolicy) Weight() int { return 10 } -func (c *danePolicy) Init(cfg *config.Map) error { +func (c *danePolicy) Configure(inlineArgs []string, cfg *config.Map) error { var err error c.extResolver, err = dns.NewExtResolver() if err != nil { @@ -389,14 +386,10 @@ func (c *danePolicy) Init(cfg *config.Map) error { return err } -func (c *danePolicy) Start(*module.MsgMetadata) module.DeliveryMXAuthPolicy { +func (c *danePolicy) StartDelivery(*module.MsgMetadata) module.DeliveryMXAuthPolicy { return &daneDelivery{c: c} } -func (c *danePolicy) Close() error { - return nil -} - func (c *daneDelivery) PrepareDomain(ctx context.Context, domain string) {} func (c *daneDelivery) discoverTLSA(ctx context.Context, mx string) ([]dns.TLSA, error) { @@ -536,7 +529,7 @@ type ( } ) -func NewLocalPolicy(_, instName string, _, _ []string) (module.Module, error) { +func NewLocalPolicy(_, instName string) (module.Module, error) { return &localPolicy{ instName: instName, }, nil @@ -554,7 +547,7 @@ func (c *localPolicy) Weight() int { return 1000 } -func (c *localPolicy) Init(cfg *config.Map) error { +func (c *localPolicy) Configure(inlineArgs []string, cfg *config.Map) error { var ( minTLSLevel string minMXLevel string @@ -589,19 +582,15 @@ func (c *localPolicy) Init(cfg *config.Map) error { return nil } -func (l localPolicy) Start(msgMeta *module.MsgMetadata) module.DeliveryMXAuthPolicy { +func (l *localPolicy) StartDelivery(msgMeta *module.MsgMetadata) module.DeliveryMXAuthPolicy { return l } -func (l localPolicy) Close() error { - return nil -} - -func (l localPolicy) Reset(*module.MsgMetadata) {} -func (l localPolicy) PrepareDomain(ctx context.Context, domain string) {} -func (l localPolicy) PrepareConn(ctx context.Context, mx string) {} +func (l *localPolicy) Reset(*module.MsgMetadata) {} +func (l *localPolicy) PrepareDomain(ctx context.Context, domain string) {} +func (l *localPolicy) PrepareConn(ctx context.Context, mx string) {} -func (l localPolicy) CheckMX(ctx context.Context, mxLevel module.MXLevel, domain, mx string, dnssec bool) (module.MXLevel, error) { +func (l *localPolicy) CheckMX(ctx context.Context, mxLevel module.MXLevel, domain, mx string, dnssec bool) (module.MXLevel, error) { if mxLevel < l.minMXLevel { return module.MXNone, &exterrors.SMTPError{ // Err on the side of caution if policy evaluation was messed up by @@ -618,7 +607,7 @@ func (l localPolicy) CheckMX(ctx context.Context, mxLevel module.MXLevel, domain return module.MXNone, nil } -func (l localPolicy) CheckConn(ctx context.Context, mxLevel module.MXLevel, tlsLevel module.TLSLevel, domain, mx string, tlsState tls.ConnectionState) (module.TLSLevel, error) { +func (l *localPolicy) CheckConn(ctx context.Context, mxLevel module.MXLevel, tlsLevel module.TLSLevel, domain, mx string, tlsState tls.ConnectionState) (module.TLSLevel, error) { if tlsLevel < l.minTLSLevel { return module.TLSNone, &exterrors.SMTPError{ Code: 451, diff --git a/internal/target/smtp/smtp_downstream.go b/internal/target/smtp/smtp_downstream.go index 8880f6cb..8c50b71b 100644 --- a/internal/target/smtp/smtp_downstream.go +++ b/internal/target/smtp/smtp_downstream.go @@ -48,10 +48,9 @@ import ( ) type Downstream struct { - modName string - instName string - lmtp bool - targetsArg []string + modName string + instName string + lmtp bool starttls bool hostname string @@ -76,17 +75,16 @@ func (u *Downstream) moduleError(err error) error { }) } -func NewDownstream(modName, instName string, _, inlineArgs []string) (module.Module, error) { +func NewDownstream(modName, instName string) (module.Module, error) { return &Downstream{ - modName: modName, - instName: instName, - lmtp: modName == "target.lmtp" || modName == "lmtp_downstream", /* compatibility with 0.3 configs */ - targetsArg: inlineArgs, - log: log.Logger{Name: modName}, + modName: modName, + instName: instName, + lmtp: modName == "target.lmtp" || modName == "lmtp_downstream", /* compatibility with 0.3 configs */ + log: log.Logger{Name: modName}, }, nil } -func (u *Downstream) Init(cfg *config.Map) error { +func (u *Downstream) Configure(inlineArgs []string, cfg *config.Map) error { var attemptTLS *bool var targetsArg []string @@ -142,8 +140,8 @@ func (u *Downstream) Init(cfg *config.Map) error { return fmt.Errorf("%s: cannot represent the hostname as an A-label name: %w", u.modName, err) } - u.targetsArg = append(u.targetsArg, targetsArg...) - for _, tgt := range u.targetsArg { + targetsArg = append(targetsArg, inlineArgs...) + for _, tgt := range targetsArg { endp, err := config.ParseEndpoint(tgt) if err != nil { return err @@ -183,8 +181,8 @@ type lmtpDelivery struct { *delivery } -func (u *Downstream) Start(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { - defer trace.StartRegion(ctx, "target.smtp/Start").End() +func (u *Downstream) StartDelivery(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { + defer trace.StartRegion(ctx, "target.smtp/StartDelivery").End() d := &delivery{ u: u, diff --git a/internal/target/smtp/smtputf8_test.go b/internal/target/smtp/smtputf8_test.go index 74aae232..f47fc778 100644 --- a/internal/target/smtp/smtputf8_test.go +++ b/internal/target/smtp/smtputf8_test.go @@ -30,11 +30,11 @@ func TestDownstreamDelivery_EHLO_ALabel(t *testing.T) { defer srv.Close() defer testutils.CheckSMTPConnLeak(t, srv) - mod, err := NewDownstream("", "", nil, []string{"tcp://127.0.0.1:" + testPort}) + mod, err := NewDownstream("", "") if err != nil { t.Fatal(err) } - if err := mod.Init(config.NewMap(nil, config.Node{ + if err := mod.Configure([]string{"tcp://127.0.0.1:" + testPort}, config.NewMap(nil, config.Node{ Children: []config.Node{ { Name: "hostname", diff --git a/internal/testutils/bench_delivery.go b/internal/testutils/bench_delivery.go index f434efc0..eedd0d2a 100644 --- a/internal/testutils/bench_delivery.go +++ b/internal/testutils/bench_delivery.go @@ -117,7 +117,7 @@ func BenchDelivery(b *testing.B, target module.DeliveryTarget, sender string, re b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - delivery, err := target.Start(benchCtx, &meta, sender) + delivery, err := target.StartDelivery(benchCtx, &meta, sender) if err != nil { b.Fatal(err) } diff --git a/internal/testutils/check.go b/internal/testutils/check.go index 399a78d4..2c44883d 100644 --- a/internal/testutils/check.go +++ b/internal/testutils/check.go @@ -54,7 +54,7 @@ func (c *Check) CheckStateForMsg(ctx context.Context, msgMeta *module.MsgMetadat return &checkState{msgMeta, c}, nil } -func (c *Check) Init(*config.Map) error { +func (c *Check) Configure([]string, *config.Map) error { return nil } @@ -104,8 +104,7 @@ func (cs *checkState) Close() error { } func init() { - module.Register("test_check", func(_, _ string, _, _ []string) (module.Module, error) { + module.Register("test_check", func(_, _ string) (module.Module, error) { return &Check{}, nil }) - module.RegisterInstance(&Check{}, nil) } diff --git a/internal/testutils/modifier.go b/internal/testutils/modifier.go index a96cbe48..1c6f135a 100644 --- a/internal/testutils/modifier.go +++ b/internal/testutils/modifier.go @@ -42,7 +42,7 @@ type Modifier struct { UnclosedStates int } -func (m Modifier) Init(*config.Map) error { +func (m Modifier) Configure([]string, *config.Map) error { return nil } @@ -115,8 +115,7 @@ func (ms modifierState) Close() error { } func init() { - module.Register("test_modifier", func(_, _ string, _, _ []string) (module.Module, error) { + module.Register("test_modifier", func(_, _ string) (module.Module, error) { return &Modifier{}, nil }) - module.RegisterInstance(&Modifier{}, nil) } diff --git a/internal/testutils/target.go b/internal/testutils/target.go index 68f5b394..19585215 100644 --- a/internal/testutils/target.go +++ b/internal/testutils/target.go @@ -62,7 +62,7 @@ type Target struct { module.Module is implemented with dummy functions for logging done by MsgPipeline code. */ -func (dt Target) Init(*config.Map) error { +func (dt Target) Configure([]string, *config.Map) error { return nil } @@ -86,7 +86,7 @@ type testTargetDeliveryPartial struct { testTargetDelivery } -func (dt *Target) Start(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { +func (dt *Target) StartDelivery(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { if dt.PartialBodyErr != nil { return &testTargetDeliveryPartial{ testTargetDelivery: testTargetDelivery{ @@ -211,10 +211,10 @@ func DoTestDeliveryNonAtomic(t *testing.T, c module.StatusCollector, tgt module. ID: encodedID, OriginalFrom: from, } - t.Log("-- tgt.Start", from) - delivery, err := tgt.Start(testCtx, &msgMeta, from) + t.Log("-- tgt.StartDelivery", from) + delivery, err := tgt.StartDelivery(testCtx, &msgMeta, from) if err != nil { - t.Log("-- ... tgt.Start", from, err, exterrors.Fields(err)) + t.Log("-- ... tgt.StartDelivery", from, err, exterrors.Fields(err)) t.Fatalf("Unexpected err: %v %+v", err, exterrors.Fields(err)) return encodedID } @@ -262,10 +262,10 @@ func DoTestDeliveryErrMeta(t *testing.T, tgt module.DeliveryTarget, from string, body := buffer.MemoryBuffer{Slice: []byte("foobar\r\n")} msgMeta.DontTraceSender = true msgMeta.ID = encodedID - t.Log("-- tgt.Start", from) - delivery, err := tgt.Start(testCtx, msgMeta, from) + t.Log("-- tgt.StartDelivery", from) + delivery, err := tgt.StartDelivery(testCtx, msgMeta, from) if err != nil { - t.Log("-- ... tgt.Start", from, err, exterrors.Fields(err)) + t.Log("-- ... tgt.StartDelivery", from, err, exterrors.Fields(err)) return encodedID, err } for _, rcpt := range to { diff --git a/internal/tls/acme/acme.go b/internal/tls/acme/acme.go index a09c3e09..0ebcd241 100644 --- a/internal/tls/acme/acme.go +++ b/internal/tls/acme/acme.go @@ -19,6 +19,7 @@ const modName = "tls.loader.acme" type Loader struct { instName string + names []string store certmagic.Storage cache *certmagic.Cache cfg *certmagic.Config @@ -27,17 +28,18 @@ type Loader struct { log log.Logger } -func New(_, instName string, _, inlineArgs []string) (module.Module, error) { - if len(inlineArgs) != 0 { - return nil, fmt.Errorf("%s: no inline args expected", modName) - } +func New(_, instName string) (module.Module, error) { return &Loader{ instName: instName, log: log.Logger{Name: modName}, }, nil } -func (l *Loader) Init(cfg *config.Map) error { +func (l *Loader) Configure(inlineArgs []string, cfg *config.Map) error { + if len(inlineArgs) != 0 { + return fmt.Errorf("%s: no inline args expected", modName) + } + var ( hostname string extraNames []string @@ -118,27 +120,28 @@ func (l *Loader) Init(cfg *config.Map) error { } l.cfg.Issuers = []certmagic.Issuer{issuer} - if module.NoRun { - return nil - } + l.names = append([]string{hostname}, extraNames...) + + return nil +} + +func (l *Loader) ConfigureTLS(c *tls.Config) error { + c.GetCertificate = l.cfg.GetCertificate + return nil +} +func (l *Loader) Start() error { manageCtx, cancelManage := context.WithCancel(context.Background()) - err := l.cfg.ManageAsync(manageCtx, append([]string{hostname}, extraNames...)) + err := l.cfg.ManageAsync(manageCtx, l.names) if err != nil { cancelManage() return err } l.cancelManage = cancelManage - - return nil -} - -func (l *Loader) ConfigureTLS(c *tls.Config) error { - c.GetCertificate = l.cfg.GetCertificate return nil } -func (l *Loader) Close() error { +func (l *Loader) Stop() error { l.cancelManage() l.cache.Stop() return nil diff --git a/internal/tls/file.go b/internal/tls/file.go index 943a59ed..ae260ec6 100644 --- a/internal/tls/file.go +++ b/internal/tls/file.go @@ -27,17 +27,15 @@ import ( "time" "github.com/foxcpp/maddy/framework/config" - "github.com/foxcpp/maddy/framework/hooks" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" ) type FileLoader struct { - instName string - inlineArgs []string - certPaths []string - keyPaths []string - log log.Logger + instName string + certPaths []string + keyPaths []string + log log.Logger certs []tls.Certificate certsLock sync.RWMutex @@ -46,16 +44,15 @@ type FileLoader struct { stopTick chan struct{} } -func NewFileLoader(_, instName string, _, inlineArgs []string) (module.Module, error) { +func NewFileLoader(_, instName string) (module.Module, error) { return &FileLoader{ - instName: instName, - inlineArgs: inlineArgs, - log: log.Logger{Name: "tls.loader.file", Debug: log.DefaultLogger.Debug}, - stopTick: make(chan struct{}), + instName: instName, + log: log.Logger{Name: "tls.loader.file", Debug: log.DefaultLogger.Debug}, + stopTick: make(chan struct{}), }, nil } -func (f *FileLoader) Init(cfg *config.Map) error { +func (f *FileLoader) Configure(inlineArgs []string, cfg *config.Map) error { cfg.StringList("certs", false, false, nil, &f.certPaths) cfg.StringList("keys", false, false, nil, &f.keyPaths) if _, err := cfg.Process(); err != nil { @@ -66,12 +63,12 @@ func (f *FileLoader) Init(cfg *config.Map) error { return errors.New("tls.loader.file: mismatch in certs and keys count") } - if len(f.inlineArgs)%2 != 0 { + if len(inlineArgs)%2 != 0 { return errors.New("tls.loader.file: odd amount of arguments") } - for i := 0; i < len(f.inlineArgs); i += 2 { - f.certPaths = append(f.certPaths, f.inlineArgs[i]) - f.keyPaths = append(f.keyPaths, f.inlineArgs[i+1]) + for i := 0; i < len(inlineArgs); i += 2 { + f.certPaths = append(f.certPaths, inlineArgs[i]) + f.keyPaths = append(f.keyPaths, inlineArgs[i+1]) } for _, certPath := range f.certPaths { @@ -84,19 +81,21 @@ func (f *FileLoader) Init(cfg *config.Map) error { return err } - hooks.AddHook(hooks.EventReload, func() { - f.log.Println("reloading certificates") - if err := f.loadCerts(); err != nil { - f.log.Error("reload failed", err) - } - }) + return nil +} +func (f *FileLoader) Start() error { f.reloadTick = time.NewTicker(time.Minute) go f.reloadTicker() return nil } -func (f *FileLoader) Close() error { +func (f *FileLoader) Reload() error { + f.log.Println("reloading certificates") + return f.loadCerts() +} + +func (f *FileLoader) Stop() error { f.reloadTick.Stop() f.stopTick <- struct{}{} return nil diff --git a/internal/tls/self_signed.go b/internal/tls/self_signed.go index d5e174f3..269b0c0d 100644 --- a/internal/tls/self_signed.go +++ b/internal/tls/self_signed.go @@ -40,14 +40,14 @@ type SelfSignedLoader struct { cert tls.Certificate } -func NewSelfSignedLoader(_, instName string, _, inlineArgs []string) (module.Module, error) { +func NewSelfSignedLoader(_, instName string) (module.Module, error) { return &SelfSignedLoader{ - instName: instName, - serverNames: inlineArgs, + instName: instName, }, nil } -func (f *SelfSignedLoader) Init(cfg *config.Map) error { +func (f *SelfSignedLoader) Configure(inlineArgs []string, cfg *config.Map) error { + f.serverNames = inlineArgs if _, err := cfg.Process(); err != nil { return err } diff --git a/maddy.go b/maddy.go index e6a05cf1..4b7204e1 100644 --- a/maddy.go +++ b/maddy.go @@ -21,7 +21,6 @@ package maddy import ( "errors" "fmt" - "io" "net/http" "os" "path/filepath" @@ -33,6 +32,7 @@ import ( "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" "github.com/foxcpp/maddy/framework/config/tls" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/hooks" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" @@ -110,6 +110,18 @@ func init() { Value: filepath.Join(ConfigDirectory, "maddy.conf"), }, ) + maddycli.AddSubcommand(&cli.Command{ + Name: "verify-config", + Usage: "Check configuration file for errors", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "debug", + Usage: "enable debug logging early", + Destination: &log.DefaultLogger.Debug, + }, + }, + Action: VerifyConfig, + }) maddycli.AddSubcommand(&cli.Command{ Name: "run", Usage: "Start the server", @@ -189,26 +201,27 @@ func Run(c *cli.Context) error { os.Setenv("PATH", config.LibexecDirectory+string(filepath.ListSeparator)+os.Getenv("PATH")) - f, err := os.Open(c.Path("config")) - if err != nil { - systemdStatusErr(err) - return cli.Exit(err.Error(), 2) - } - defer f.Close() + hooks.AddHook(hooks.EventLogRotate, reinitLogging) + defer log.DefaultLogger.Out.Close() + defer hooks.RunHooks(hooks.EventShutdown) - cfg, err := parser.Read(f, c.Path("config")) - if err != nil { + if err := moduleMain(c.Path("config")); err != nil { systemdStatusErr(err) - return cli.Exit(err.Error(), 2) + return cli.Exit(err.Error(), 1) } - defer log.DefaultLogger.Out.Close() + return nil +} - if err := moduleMain(cfg); err != nil { - systemdStatusErr(err) - return cli.Exit(err.Error(), 1) +func VerifyConfig(c *cli.Context) error { + os.Setenv("PATH", config.LibexecDirectory+string(filepath.ListSeparator)+os.Getenv("PATH")) + + if _, err := moduleConfigure(c.Path("config")); err != nil { + return cli.Exit(err.Error(), 2) } + log.DefaultLogger.Msg("No errors detected") + return nil } @@ -235,42 +248,46 @@ func initDebug(c *cli.Context) { } } -func InitDirs() error { - if config.StateDirectory == "" { - config.StateDirectory = DefaultStateDirectory +func InitDirs(c *container.C) error { + if c.Config.StateDirectory == "" { + c.Config.StateDirectory = DefaultStateDirectory } - if config.RuntimeDirectory == "" { - config.RuntimeDirectory = DefaultRuntimeDirectory + if c.Config.RuntimeDirectory == "" { + c.Config.RuntimeDirectory = DefaultRuntimeDirectory } - if config.LibexecDirectory == "" { - config.LibexecDirectory = DefaultLibexecDirectory + if c.Config.LibexecDirectory == "" { + c.Config.LibexecDirectory = DefaultLibexecDirectory } - if err := ensureDirectoryWritable(config.StateDirectory); err != nil { + if err := ensureDirectoryWritable(c.Config.StateDirectory); err != nil { return err } - if err := ensureDirectoryWritable(config.RuntimeDirectory); err != nil { + if err := ensureDirectoryWritable(c.Config.RuntimeDirectory); err != nil { return err } // Make sure all paths we are going to use are absolute // before we change the working directory. - if !filepath.IsAbs(config.StateDirectory) { + if !filepath.IsAbs(c.Config.StateDirectory) { return errors.New("statedir should be absolute") } - if !filepath.IsAbs(config.RuntimeDirectory) { + if !filepath.IsAbs(c.Config.RuntimeDirectory) { return errors.New("runtimedir should be absolute") } - if !filepath.IsAbs(config.LibexecDirectory) { + if !filepath.IsAbs(c.Config.LibexecDirectory) { return errors.New("-libexec should be absolute") } // Change the working directory to make all relative paths // in configuration relative to state directory. - if err := os.Chdir(config.StateDirectory); err != nil { + if err := os.Chdir(c.Config.StateDirectory); err != nil { log.Println(err) } + config.StateDirectory = c.Config.StateDirectory + config.RuntimeDirectory = c.Config.RuntimeDirectory + config.LibexecDirectory = c.Config.LibexecDirectory + return nil } @@ -287,10 +304,10 @@ func ensureDirectoryWritable(path string) error { return os.Remove(testFile.Name()) } -func ReadGlobals(cfg []config.Node) (map[string]interface{}, []config.Node, error) { +func ReadGlobals(c *container.C, cfg []config.Node) (map[string]interface{}, []config.Node, error) { globals := config.NewMap(nil, config.Node{Children: cfg}) - globals.String("state_dir", false, false, DefaultStateDirectory, &config.StateDirectory) - globals.String("runtime_dir", false, false, DefaultRuntimeDirectory, &config.RuntimeDirectory) + globals.String("state_dir", false, false, DefaultStateDirectory, &c.Config.StateDirectory) + globals.String("runtime_dir", false, false, DefaultRuntimeDirectory, &c.Config.RuntimeDirectory) globals.String("hostname", false, false, "", nil) globals.String("autogenerated_msg_domain", false, false, "", nil) globals.Custom("tls", false, false, nil, tls.TLSDirective, nil) @@ -298,8 +315,8 @@ func ReadGlobals(cfg []config.Node) (map[string]interface{}, []config.Node, erro globals.Bool("storage_perdomain", false, false, nil) globals.Bool("auth_perdomain", false, false, nil) globals.StringList("auth_domains", false, false, nil, nil) - globals.Custom("log", false, false, defaultLogOutput, logOutput, &log.DefaultLogger.Out) - globals.Bool("debug", false, log.DefaultLogger.Debug, &log.DefaultLogger.Debug) + globals.Custom("log", false, false, defaultLogOutput, logOutput, &c.DefaultLogger.Out) + globals.Bool("debug", false, log.DefaultLogger.Debug, &c.DefaultLogger.Debug) config.EnumMapped(globals, "auth_map_normalize", true, false, authz.NormalizeFuncs, authz.NormalizeAuto, nil) modconfig.Table(globals, "auth_map", true, false, nil, nil) globals.AllowUnknown() @@ -307,46 +324,86 @@ func ReadGlobals(cfg []config.Node) (map[string]interface{}, []config.Node, erro return globals.Values, unknown, err } -func moduleMain(cfg []config.Node) error { - globals, modBlocks, err := ReadGlobals(cfg) +func ReadConfig(path string) ([]config.Node, error) { + f, err := os.Open(path) if err != nil { - return err + return nil, err + } + defer f.Close() + + return parser.Read(f, path) +} + +func moduleConfigure(configPath string) (*container.C, error) { + c := container.New() + container.Global = c + + cfg, err := ReadConfig(configPath) + if err != nil { + return nil, fmt.Errorf("failed to read config %s: %w", configPath, err) } - if err := InitDirs(); err != nil { - return err + globals, modBlocks, err := ReadGlobals(c, cfg) + if err != nil { + return nil, err } - hooks.AddHook(hooks.EventLogRotate, reinitLogging) + if err := InitDirs(c); err != nil { + return nil, err + } - endpoints, mods, err := RegisterModules(globals, modBlocks) + err = RegisterModules(c, globals, modBlocks) if err != nil { - return err + return nil, err + } + + for _, inst := range c.Modules.NotInitialized() { + return nil, fmt.Errorf("unused configuration block %s (%s)", + inst.InstanceName(), inst.Name()) } - err = initModules(globals, endpoints, mods) + return c, nil +} + +func moduleStart(c *container.C) error { + return c.Lifetime.StartAll() +} + +func moduleStop(c *container.C) { + c.Lifetime.StopAll() +} + +func moduleMain(configPath string) error { + log.DefaultLogger.Msg("loading configuration...") + + c, err := moduleConfigure(configPath) if err != nil { return err } - systemdStatus(SDReady, "Listening for incoming connections...") + c.DefaultLogger.Msg("configuration loaded") + if err := moduleStart(c); err != nil { + return err + } + c.DefaultLogger.Msg("server started", "version", Version) + + systemdStatus(SDReady, "Listening for incoming connections...") handleSignals() + c.DefaultLogger.Msg("server stopping...") systemdStatus(SDStopping, "Waiting for running transactions to complete...") - hooks.RunHooks(hooks.EventShutdown) - + moduleStop(c) + c.DefaultLogger.Msg("server stopped") return nil } -type ModInfo struct { - Instance module.Module - Cfg config.Node -} - -func RegisterModules(globals map[string]interface{}, nodes []config.Node) (endpoints, mods []ModInfo, err error) { - mods = make([]ModInfo, 0, len(nodes)) +func RegisterModules(c *container.C, globals map[string]interface{}, nodes []config.Node) (err error) { + var endpoints []struct { + Endpoint module.LifetimeModule + Cfg *config.Map + } for _, block := range nodes { var instName string @@ -364,70 +421,66 @@ func RegisterModules(globals map[string]interface{}, nodes []config.Node) (endpo if endpFactory != nil { inst, err := endpFactory(modName, block.Args) if err != nil { - return nil, nil, err + return err } - endpoints = append(endpoints, ModInfo{Instance: inst, Cfg: block}) + endpoints = append(endpoints, struct { + Endpoint module.LifetimeModule + Cfg *config.Map + }{Endpoint: inst, Cfg: config.NewMap(globals, block)}) continue } factory := module.Get(modName) if factory == nil { - return nil, nil, config.NodeErr(block, "unknown module or global directive: %s", modName) + return config.NodeErr(block, "unknown module or global directive: %s", modName) } - if module.HasInstance(instName) { - return nil, nil, config.NodeErr(block, "config block named %s already exists", instName) + inst, err := factory(modName, instName) + if err != nil { + return err } - inst, err := factory(modName, instName, modAliases, nil) + err = c.Modules.Register(inst, func() error { + err := inst.Configure(nil, config.NewMap(globals, block)) + if err != nil { + return err + } + + if lt, ok := inst.(module.LifetimeModule); ok { + c.Lifetime.Add(lt) + } + return nil + }) if err != nil { - return nil, nil, err + if errors.Is(err, module.ErrInstanceNameDuplicate) { + return config.NodeErr(block, "config block named %s already exists", inst.InstanceName()) + } + return err } - module.RegisterInstance(inst, config.NewMap(globals, block)) for _, alias := range modAliases { - if module.HasInstance(alias) { - return nil, nil, config.NodeErr(block, "config block named %s already exists", alias) + if err := c.Modules.AddAlias(instName, alias); err != nil { + if errors.Is(err, module.ErrInstanceNameDuplicate) { + return config.NodeErr(block, "config block named %s already exists", alias) + } + return err } - module.RegisterAlias(alias, instName) } log.Debugf("%v:%v: register config block %v %v", block.File, block.Line, instName, modAliases) - mods = append(mods, ModInfo{Instance: inst, Cfg: block}) } if len(endpoints) == 0 { - return nil, nil, fmt.Errorf("at least one endpoint should be configured") + return fmt.Errorf("at least one endpoint should be configured") } - return endpoints, mods, nil -} - -func initModules(globals map[string]interface{}, endpoints, mods []ModInfo) error { + // Endpoints are configured directly after registration. for _, endp := range endpoints { - if err := endp.Instance.Init(config.NewMap(globals, endp.Cfg)); err != nil { + if err := endp.Endpoint.Configure(nil, endp.Cfg); err != nil { return err } - - if closer, ok := endp.Instance.(io.Closer); ok { - endp := endp - hooks.AddHook(hooks.EventShutdown, func() { - log.Debugf("close %s (%s)", endp.Instance.Name(), endp.Instance.InstanceName()) - if err := closer.Close(); err != nil { - log.Printf("module %s (%s) close failed: %v", endp.Instance.Name(), endp.Instance.InstanceName(), err) - } - }) - } - } - - for _, inst := range mods { - if module.Initialized[inst.Instance.InstanceName()] { - continue - } - - return fmt.Errorf("Unused configuration block at %s:%d - %s (%s)", - inst.Cfg.File, inst.Cfg.Line, inst.Instance.InstanceName(), inst.Instance.Name()) + c.Lifetime.Add(endp.Endpoint) } return nil diff --git a/signal_nonposix.go b/signal_nonposix.go index 87ea0cd8..f5750a61 100644 --- a/signal_nonposix.go +++ b/signal_nonposix.go @@ -40,6 +40,6 @@ func handleSignals() os.Signal { os.Exit(1) }() - log.Printf("signal received (%v), next signal will force immediate shutdown.", s) + log.Printf("signal received (%v)", s) return s } From 3ce6ebf60c25114a91685cbcbd39bce0e3222b1a Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月29日 21:41:29 +0300 Subject: [PATCH 100/171] Add code to trigger module reinitialization on SIGUSR2 --- maddy.go | 40 +++++++++++++++++++++++++++++++++++++++- signal.go | 11 +++++------ 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/maddy.go b/maddy.go index 4b7204e1..829cdf76 100644 --- a/maddy.go +++ b/maddy.go @@ -376,6 +376,12 @@ func moduleStop(c *container.C) { func moduleMain(configPath string) error { log.DefaultLogger.Msg("loading configuration...") + // Make path absolute to make sure we can still read it if current directory changes (in moduleConfigure). + configPath, err := filepath.Abs(configPath) + if err != nil { + return err + } + c, err := moduleConfigure(configPath) if err != nil { return err @@ -389,7 +395,12 @@ func moduleMain(configPath string) error { c.DefaultLogger.Msg("server started", "version", Version) systemdStatus(SDReady, "Listening for incoming connections...") - handleSignals() + for handleSignals() { + systemdStatus(SDReloading, "Reloading state...") + hooks.RunHooks(hooks.EventReload) + + c = moduleReload(c, configPath) + } c.DefaultLogger.Msg("server stopping...") systemdStatus(SDStopping, "Waiting for running transactions to complete...") @@ -399,6 +410,33 @@ func moduleMain(configPath string) error { return nil } +func moduleReload(oldContainer *container.C, configPath string) *container.C { + oldContainer.DefaultLogger.Msg("reloading server...") + + oldContainer.DefaultLogger.Msg("loading new configuration...") + newContainer, err := moduleConfigure(configPath) + if err != nil { + oldContainer.DefaultLogger.Error("failed to load new configuration", err) + return oldContainer + } + + oldContainer.DefaultLogger.Msg("configuration loaded") + + oldContainer.DefaultLogger.Msg("starting new server") + if err := moduleStart(newContainer); err != nil { + oldContainer.DefaultLogger.Error("failed to start new server", err) + container.Global = oldContainer + return oldContainer + } + + newContainer.DefaultLogger.Msg("server started", "version", Version) + oldContainer.DefaultLogger.Msg("stopping server") + moduleStop(oldContainer) + oldContainer.DefaultLogger.Msg("server stopped") + + return newContainer +} + func RegisterModules(c *container.C, globals map[string]interface{}, nodes []config.Node) (err error) { var endpoints []struct { Endpoint module.LifetimeModule diff --git a/signal.go b/signal.go index e952fcf6..4925b3db 100644 --- a/signal.go +++ b/signal.go @@ -36,9 +36,10 @@ import ( // (SIGTERM, SIGHUP, SIGINT) will cause this function to return. // // SIGUSR1 will call reinitLogging without returning. -func handleSignals() os.Signal { +func handleSignals() (reload bool) { sig := make(chan os.Signal, 5) signal.Notify(sig, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGINT, syscall.SIGUSR1, syscall.SIGUSR2) + defer signal.Stop(sig) for { switch s := <-sig; s { @@ -48,10 +49,8 @@ func handleSignals() os.Signal { hooks.RunHooks(hooks.EventLogRotate) systemdStatus(SDReady, "Listening for incoming connections...") case syscall.SIGUSR2: - log.Printf("signal received (%s), reloading state", s.String()) - systemdStatus(SDReloading, "Reloading state...") - hooks.RunHooks(hooks.EventReload) - systemdStatus(SDReady, "Listening for incoming connections...") + log.Printf("signal received (%s), reloading configuration", s.String()) + return true default: go func() { s := handleSignals() @@ -60,7 +59,7 @@ func handleSignals() os.Signal { }() log.Printf("signal received (%v), next signal will force immediate shutdown.", s) - return s + return false } } } From 187fc66be1495349dee2c4993781398bf0654f0a Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月29日 23:38:08 +0300 Subject: [PATCH 101/171] Fix-up c48332a9401151a93df75fae55dd34bea2cafc56 --- internal/auth/sasl.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/auth/sasl.go b/internal/auth/sasl.go index 5968c3c0..21a3d897 100644 --- a/internal/auth/sasl.go +++ b/internal/auth/sasl.go @@ -166,7 +166,7 @@ func (s *SASLAuth) CreateSASL(mech string, remoteAddr net.Addr, successCb func(i return err } - err := s.AuthPlain(username, password) + err = s.AuthPlain(username, password) if err != nil { s.Log.Error("authentication failed", err, "username", username, "src_ip", remoteAddr) return ErrInvalidAuthCred From f82742b392bfc459dc767ead2636a9df60e76356 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月30日 01:45:19 +0300 Subject: [PATCH 102/171] Implement net.Listener hand-over during config reload Log library refactored a little to make it easier to enable debug logging. --- framework/log/log.go | 67 ++++++++------ framework/log/orderedjson.go | 2 +- framework/log/zap.go | 2 +- framework/module/lifetime.go | 4 +- framework/module/registry.go | 4 +- framework/resource/netresource/dup.go | 27 ++++++ framework/resource/netresource/fd.go | 47 ++++++++++ framework/resource/netresource/listen.go | 38 ++++++++ framework/resource/netresource/tracker.go | 91 +++++++++++++++++++ framework/resource/resource.go | 18 ++++ framework/resource/singleton.go | 64 +++++++++++++ framework/resource/tracker.go | 47 ++++++++++ internal/auth/netauth/netauth.go | 2 +- .../endpoint/dovecot_sasld/dovecot_sasl.go | 5 +- internal/endpoint/imap/imap.go | 5 +- internal/endpoint/openmetrics/om.go | 4 +- internal/endpoint/smtp/smtp.go | 5 +- internal/updatepipe/unix_pipe.go | 3 +- maddy.go | 3 + 19 files changed, 394 insertions(+), 44 deletions(-) create mode 100644 framework/resource/netresource/dup.go create mode 100644 framework/resource/netresource/fd.go create mode 100644 framework/resource/netresource/listen.go create mode 100644 framework/resource/netresource/tracker.go create mode 100644 framework/resource/resource.go create mode 100644 framework/resource/singleton.go create mode 100644 framework/resource/tracker.go diff --git a/framework/log/log.go b/framework/log/log.go index da8647f6..366cf156 100644 --- a/framework/log/log.go +++ b/framework/log/log.go @@ -42,6 +42,8 @@ import ( // No serialization is provided by Logger, its log.Output responsibility to // ensure goroutine-safety if necessary. type Logger struct { + Parent *Logger + Out Output Name string Debug bool @@ -51,30 +53,37 @@ type Logger struct { Fields map[string]interface{} } -func (l Logger) Zap() *zap.Logger { +func (l *Logger) Zap() *zap.Logger { // TODO: Migrate to using zap natively. return zap.New(zapLogger{L: l}) } -func (l Logger) Debugf(format string, val ...interface{}) { - if !l.Debug { +func (l *Logger) IsDebug() bool { + if l.Parent == nil { + return l.Debug + } + return l.Debug || l.Parent.IsDebug() +} + +func (l *Logger) Debugf(format string, val ...interface{}) { + if !l.IsDebug() { return } l.log(true, l.formatMsg(fmt.Sprintf(format, val...), nil)) } -func (l Logger) Debugln(val ...interface{}) { - if !l.Debug { +func (l *Logger) Debugln(val ...interface{}) { + if !l.IsDebug() { return } l.log(true, l.formatMsg(strings.TrimRight(fmt.Sprintln(val...), "\n"), nil)) } -func (l Logger) Printf(format string, val ...interface{}) { +func (l *Logger) Printf(format string, val ...interface{}) { l.log(false, l.formatMsg(fmt.Sprintf(format, val...), nil)) } -func (l Logger) Println(val ...interface{}) { +func (l *Logger) Println(val ...interface{}) { l.log(false, l.formatMsg(strings.TrimRight(fmt.Sprintln(val...), "\n"), nil)) } @@ -87,13 +96,13 @@ func (l Logger) Println(val ...interface{}) { // followed by corresponding values. That is, for example, []interface{"key", // "value", "key2", "value2"}. // -// If value in fields implements LogFormatter, it will be represented by the +// If value in fields implements Formatter, it will be represented by the // string returned by FormatLog method. Same goes for fmt.Stringer and error // interfaces. // // Additionally, time.Time is written as a string in ISO 8601 format. // time.Duration follows fmt.Stringer rule above. -func (l Logger) Msg(msg string, fields ...interface{}) { +func (l *Logger) Msg(msg string, fields ...interface{}) { m := make(map[string]interface{}, len(fields)/2) fieldsToMap(fields, m) l.log(false, l.formatMsg(msg, m)) @@ -112,7 +121,7 @@ func (l Logger) Msg(msg string, fields ...interface{}) { // In the context of Error method, "msg" typically indicates the top-level // context in which the error is *handled*. For example, if error leads to // rejection of SMTP DATA command, msg will probably be "DATA error". -func (l Logger) Error(msg string, err error, fields ...interface{}) { +func (l *Logger) Error(msg string, err error, fields ...interface{}) { if err == nil { return } @@ -133,8 +142,8 @@ func (l Logger) Error(msg string, err error, fields ...interface{}) { l.log(false, l.formatMsg(msg, allFields)) } -func (l Logger) DebugMsg(kind string, fields ...interface{}) { - if !l.Debug { +func (l *Logger) DebugMsg(kind string, fields ...interface{}) { + if !l.IsDebug() { return } m := make(map[string]interface{}, len(fields)/2) @@ -162,7 +171,7 @@ func fieldsToMap(fields []interface{}, out map[string]interface{}) { } } -func (l Logger) formatMsg(msg string, fields map[string]interface{}) string { +func (l *Logger) formatMsg(msg string, fields map[string]interface{}) string { formatted := strings.Builder{} formatted.WriteString(msg) @@ -184,14 +193,17 @@ func (l Logger) formatMsg(msg string, fields map[string]interface{}) string { return formatted.String() } -type LogFormatter interface { +type Formatter interface { FormatLog() string } // Write implements io.Writer, all bytes sent // to it will be written as a separate log messages. // No line-buffering is done. -func (l Logger) Write(s []byte) (int, error) { +func (l *Logger) Write(s []byte) (int, error) { + if !l.IsDebug() { + return len(s), nil + } l.log(false, strings.TrimRight(string(s), "\n")) return len(s), nil } @@ -199,15 +211,13 @@ func (l Logger) Write(s []byte) (int, error) { // DebugWriter returns a writer that will act like Logger.Write // but will use debug flag on messages. If Logger.Debug is false, // Write method of returned object will be no-op. -func (l Logger) DebugWriter() io.Writer { - if !l.Debug { - return io.Discard - } - l.Debug = true - return &l +func (l *Logger) DebugWriter() io.Writer { + l2 := l.Sublogger("") + l2.Debug = true + return l2 } -func (l Logger) log(debug bool, s string) { +func (l *Logger) log(debug bool, s string) { if l.Name != "" { s = l.Name + ": " + s } @@ -224,14 +234,15 @@ func (l Logger) log(debug bool, s string) { // Logging is disabled - do nothing. } -func (l Logger) Sublogger(name string) Logger { - if l.Name != "" { +func (l *Logger) Sublogger(name string) *Logger { + if l.Name != "" && name != "" { name = l.Name + "/" + name } - return Logger{ - Out: l.Out, - Name: name, - Debug: l.Debug, + return &Logger{ + Parent: l, + Out: l.Out, + Name: name, + Debug: l.Debug, } } diff --git a/framework/log/orderedjson.go b/framework/log/orderedjson.go index 834f3a6c..9de32baf 100644 --- a/framework/log/orderedjson.go +++ b/framework/log/orderedjson.go @@ -58,7 +58,7 @@ func marshalOrderedJSON(output *strings.Builder, m map[string]interface{}) error val = casted.Format("2006-01-02T15:04:05.000") case time.Duration: val = casted.String() - case LogFormatter: + case Formatter: val = casted.FormatLog() case fmt.Stringer: val = casted.String() diff --git a/framework/log/zap.go b/framework/log/zap.go index 23821f84..893bf484 100644 --- a/framework/log/zap.go +++ b/framework/log/zap.go @@ -7,7 +7,7 @@ import ( // TODO: Migrate to using actual zapcore to improve logging performance type zapLogger struct { - L Logger + L *Logger } func (l zapLogger) Enabled(level zapcore.Level) bool { diff --git a/framework/module/lifetime.go b/framework/module/lifetime.go index 1b0339da..ede5d2b4 100644 --- a/framework/module/lifetime.go +++ b/framework/module/lifetime.go @@ -38,7 +38,7 @@ type ReloadModule interface { } type LifetimeTracker struct { - logger log.Logger + logger *log.Logger instances []*struct { mod LifetimeModule started bool @@ -114,7 +114,7 @@ func (lt *LifetimeTracker) StopAll() error { return nil } -func NewLifetime(log log.Logger) *LifetimeTracker { +func NewLifetime(log *log.Logger) *LifetimeTracker { return &LifetimeTracker{ logger: log, } diff --git a/framework/module/registry.go b/framework/module/registry.go index ec62edf7..ce133402 100644 --- a/framework/module/registry.go +++ b/framework/module/registry.go @@ -35,14 +35,14 @@ type registryEntry struct { } type Registry struct { - logger log.Logger + logger *log.Logger instances map[string]registryEntry initialized map[string]struct{} started map[string]struct{} aliases map[string]string } -func NewRegistry(log log.Logger) *Registry { +func NewRegistry(log *log.Logger) *Registry { return &Registry{ logger: log, instances: make(map[string]registryEntry), diff --git a/framework/resource/netresource/dup.go b/framework/resource/netresource/dup.go new file mode 100644 index 00000000..00d047dc --- /dev/null +++ b/framework/resource/netresource/dup.go @@ -0,0 +1,27 @@ +package netresource + +import "net" + +func dupTCPListener(l *net.TCPListener) (*net.TCPListener, error) { + f, err := l.File() + if err != nil { + return nil, err + } + l2, err := net.FileListener(f) + if err != nil { + return nil, err + } + return l2.(*net.TCPListener), nil +} + +func dupUnixListener(l *net.UnixListener) (*net.UnixListener, error) { + f, err := l.File() + if err != nil { + return nil, err + } + l2, err := net.FileListener(f) + if err != nil { + return nil, err + } + return l2.(*net.UnixListener), nil +} diff --git a/framework/resource/netresource/fd.go b/framework/resource/netresource/fd.go new file mode 100644 index 00000000..395ddbcc --- /dev/null +++ b/framework/resource/netresource/fd.go @@ -0,0 +1,47 @@ +package netresource + +import ( + "errors" + "fmt" + "net" + "os" + "strconv" + "strings" +) + +func ListenFD(fd uint) (net.Listener, error) { + file := os.NewFile(uintptr(fd), strconv.FormatUint(uint64(fd), 10)) + defer file.Close() + return net.FileListener(file) +} + +func ListenFDName(name string) (net.Listener, error) { + listenPDStr := os.Getenv("LISTEN_PID") + if listenPDStr == "" { + return nil, errors.New("$LISTEN_PID is not set") + } + listenPid, err := strconv.Atoi(listenPDStr) + if err != nil { + return nil, errors.New("$LISTEN_PID is not integer") + } + if listenPid != os.Getpid() { + return nil, fmt.Errorf("$LISTEN_PID (%d) is not our PID (%d)", listenPid, os.Getpid()) + } + + names := strings.Split(os.Getenv("LISTEN_FDNAMES"), ":") + fd := uintptr(0) + for i, fdName := range names { + if fdName == name { + fd = uintptr(3 + i) + break + } + } + + if fd == 0 { + return nil, fmt.Errorf("name %s not found in $LISTEN_FDNAMES", name) + } + + file := os.NewFile(3+fd, name) + defer file.Close() + return net.FileListener(file) +} diff --git a/framework/resource/netresource/listen.go b/framework/resource/netresource/listen.go new file mode 100644 index 00000000..e2328002 --- /dev/null +++ b/framework/resource/netresource/listen.go @@ -0,0 +1,38 @@ +package netresource + +import ( + "fmt" + "net" + "strconv" + + "github.com/foxcpp/maddy/framework/log" +) + +var ( + tracker = NewListenerTracker(log.DefaultLogger.Sublogger("netresource")) +) + +func CloseUnusedListeners() error { + return tracker.Close() +} + +func ResetListenersUsage() { + tracker.ResetUsage() +} + +func Listen(network, addr string) (net.Listener, error) { + switch network { + case "fd": + fd, err := strconv.ParseUint(addr, 10, strconv.IntSize) + if err != nil { + return nil, fmt.Errorf("invalid FD number: %v", addr) + } + return ListenFD(uint(fd)) + case "fdname": + return ListenFDName(addr) + case "tcp", "tcp4", "tcp6", "unix": + return tracker.Get(network, addr) + default: + return nil, fmt.Errorf("unsupported network: %v", network) + } +} diff --git a/framework/resource/netresource/tracker.go b/framework/resource/netresource/tracker.go new file mode 100644 index 00000000..97989c62 --- /dev/null +++ b/framework/resource/netresource/tracker.go @@ -0,0 +1,91 @@ +package netresource + +import ( + "fmt" + "net" + "net/netip" + + "github.com/foxcpp/maddy/framework/log" + "github.com/foxcpp/maddy/framework/resource" +) + +type ListenerTracker struct { + logger *log.Logger + tcp *resource.Tracker[*net.TCPListener] + unix *resource.Tracker[*net.UnixListener] +} + +func (lt *ListenerTracker) Get(network, addr string) (net.Listener, error) { + switch network { + case "tcp", "tcp4", "tcp6": + l, err := lt.tcp.GetOpen(addr, func() (*net.TCPListener, error) { + addrPort, err := netip.ParseAddrPort(addr) + if err != nil { + return nil, err + } + lt.logger.DebugMsg("new listener", "network", network, "address", addr) + return net.ListenTCP(network, net.TCPAddrFromAddrPort(addrPort)) + }) + if err != nil { + return nil, err + } + + // We return duplicated listener so when listener is closed by user endpoint + // the tracked resource remains available and listening on the port doesn't + // actually stop. + l2, err := dupTCPListener(l) + if err != nil { + return nil, err + } + return l2, nil + case "unix": + l, err := lt.unix.GetOpen(addr, func() (*net.UnixListener, error) { + addr, err := net.ResolveUnixAddr(network, addr) + if err != nil { + return nil, err + } + lt.logger.DebugMsg("new listener", "network", network, "address", addr) + return net.ListenUnix(network, addr) + }) + if err != nil { + return nil, err + } + + l2, err := dupUnixListener(l) + if err != nil { + return nil, err + } + return l2, nil + default: + return nil, fmt.Errorf("unsupported network type: %s", network) + } +} + +func (lt *ListenerTracker) ResetUsage() { + lt.tcp.MarkAllUnused() + lt.unix.MarkAllUnused() +} + +func (lt *ListenerTracker) CloseUnused() error { + lt.tcp.CloseUnused(func(key string) bool { + return false + }) + lt.unix.CloseUnused(func(key string) bool { + return false + }) + return nil +} + +func (lt *ListenerTracker) Close() error { + lt.tcp.Close() + lt.unix.Close() + return nil +} + +func NewListenerTracker(log *log.Logger) *ListenerTracker { + return &ListenerTracker{ + logger: log, + tcp: resource.NewTracker[*net.TCPListener](resource.NewSingleton[*net.TCPListener]()), + unix: resource.NewTracker[*net.UnixListener](resource.NewSingleton[*net.UnixListener]()), + } +} diff --git a/framework/resource/resource.go b/framework/resource/resource.go new file mode 100644 index 00000000..a34942a6 --- /dev/null +++ b/framework/resource/resource.go @@ -0,0 +1,18 @@ +package resource + +import ( + "io" +) + +type Resource = io.Closer + +type CheckableResource interface { + Resource + IsUsable() bool +} + +type Container[T Resource] interface { + io.Closer + GetOpen(key string, open func() (T, error)) (T, error) + CloseUnused(isUsed func(key string) bool) error +} diff --git a/framework/resource/singleton.go b/framework/resource/singleton.go new file mode 100644 index 00000000..2614cee2 --- /dev/null +++ b/framework/resource/singleton.go @@ -0,0 +1,64 @@ +package resource + +import ( + "sync" +) + +// Singleton represents a set of resources identified by an unique key. +type Singleton[T Resource] struct { + lock sync.RWMutex + resources map[string]T +} + +func NewSingleton[T Resource]() *Singleton[T] { + return &Singleton[T]{ + resources: make(map[string]T), + } +} + +func (s *Singleton[T]) GetOpen(key string, open func() (T, error)) (T, error) { + s.lock.Lock() + defer s.lock.Unlock() + + existing, ok := s.resources[key] + if ok { + return existing, nil + } + + res, err := open() + if err != nil { + var empty T + return empty, err + } + + s.resources[key] = res + + return res, nil +} + +func (s *Singleton[T]) CloseUnused(isUsed func(key string) bool) error { + s.lock.Lock() + defer s.lock.Unlock() + + for key, res := range s.resources { + if isUsed(key) { + continue + } + res.Close() + delete(s.resources, key) + } + + return nil +} + +func (s *Singleton[T]) Close() error { + s.lock.Lock() + defer s.lock.Unlock() + + for key, res := range s.resources { + res.Close() + delete(s.resources, key) + } + + return nil +} diff --git a/framework/resource/tracker.go b/framework/resource/tracker.go new file mode 100644 index 00000000..318cf298 --- /dev/null +++ b/framework/resource/tracker.go @@ -0,0 +1,47 @@ +package resource + +import ( + "sync" +) + +// Tracker is a container wrapper that tracks whether resources were used since +// last MarkAllUnused call. +type Tracker[T Resource] struct { + C Container[T] + + usedLock sync.Mutex + used map[string]bool +} + +func NewTracker[T Resource](c Container[T]) *Tracker[T] { + return &Tracker[T]{C: c, used: make(map[string]bool)} +} + +func (t *Tracker[T]) Close() error { + return t.C.Close() +} + +func (t *Tracker[T]) MarkAllUnused() { + t.usedLock.Lock() + defer t.usedLock.Unlock() + + t.used = make(map[string]bool) +} + +func (t *Tracker[T]) GetOpen(key string, open func() (T, error)) (T, error) { + t.usedLock.Lock() + t.used[key] = true + t.usedLock.Unlock() + + return t.C.GetOpen(key, open) +} + +func (t *Tracker[T]) CloseUnused(isUsed func(key string) bool) error { + t.usedLock.Lock() + defer t.usedLock.Unlock() + + return t.C.CloseUnused(func(key string) bool { + used := t.used[key] + return used && isUsed(key) + }) +} diff --git a/internal/auth/netauth/netauth.go b/internal/auth/netauth/netauth.go index 62d6c6cc..db84cd18 100644 --- a/internal/auth/netauth/netauth.go +++ b/internal/auth/netauth/netauth.go @@ -42,7 +42,7 @@ func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { return fmt.Errorf("%s: inline arguments are not used", modName) } - l := hclog.New(&hclog.LoggerOptions{Output: a.log}) + l := hclog.New(&hclog.LoggerOptions{Output: &a.log}) n, err := netauth.NewWithLog(l) if err != nil { return err diff --git a/internal/endpoint/dovecot_sasld/dovecot_sasl.go b/internal/endpoint/dovecot_sasld/dovecot_sasl.go index b1bcd159..f4dee1a7 100644 --- a/internal/endpoint/dovecot_sasld/dovecot_sasl.go +++ b/internal/endpoint/dovecot_sasld/dovecot_sasl.go @@ -31,6 +31,7 @@ import ( modconfig "github.com/foxcpp/maddy/framework/config/module" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/resource/netresource" "github.com/foxcpp/maddy/internal/auth" "github.com/foxcpp/maddy/internal/authz" ) @@ -79,7 +80,7 @@ func (endp *Endpoint) Configure(_ []string, cfg *config.Map) error { } endp.srv = dovecotsasl.NewServer() - endp.srv.Log = stdlog.New(endp.log, "", 0) + endp.srv.Log = stdlog.New(&endp.log, "", 0) for _, mech := range endp.saslAuth.SASLMechanisms() { endp.srv.AddMechanism(mech, mechInfo[mech], func(req *dovecotsasl.AuthReq) sasl.Server { @@ -106,7 +107,7 @@ func (endp *Endpoint) Configure(_ []string, cfg *config.Map) error { func (endp *Endpoint) Start() error { for _, addr := range endp.endpoints { - l, err := net.Listen(addr.Network(), addr.Address()) + l, err := netresource.Listen(addr.Network(), addr.Address()) if err != nil { return fmt.Errorf("%s: %v", modName, err) } diff --git a/internal/endpoint/imap/imap.go b/internal/endpoint/imap/imap.go index 765c429f..13116225 100644 --- a/internal/endpoint/imap/imap.go +++ b/internal/endpoint/imap/imap.go @@ -42,6 +42,7 @@ import ( tls2 "github.com/foxcpp/maddy/framework/config/tls" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/resource/netresource" "github.com/foxcpp/maddy/internal/auth" "github.com/foxcpp/maddy/internal/authz" "github.com/foxcpp/maddy/internal/proxy_protocol" @@ -126,7 +127,7 @@ func (endp *Endpoint) Configure(_ []string, cfg *config.Map) error { if ioErrors { endp.serv.ErrorLog = &endp.Log } else { - endp.serv.ErrorLog = log.Logger{Out: log.NopOutput{}} + endp.serv.ErrorLog = &log.Logger{Out: log.NopOutput{}} } if ioDebug { endp.serv.Debug = endp.Log.DebugWriter() @@ -174,7 +175,7 @@ func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { for _, addr := range addresses { var l net.Listener var err error - l, err = net.Listen(addr.Network(), addr.Address()) + l, err = netresource.Listen(addr.Network(), addr.Address()) if err != nil { return fmt.Errorf("imap: %v", err) } diff --git a/internal/endpoint/openmetrics/om.go b/internal/endpoint/openmetrics/om.go index 136d4af5..44ac64db 100644 --- a/internal/endpoint/openmetrics/om.go +++ b/internal/endpoint/openmetrics/om.go @@ -21,13 +21,13 @@ package openmetrics import ( "errors" "fmt" - "net" "net/http" "sync" "github.com/foxcpp/maddy/framework/config" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/resource/netresource" "github.com/prometheus/client_golang/prometheus/promhttp" ) @@ -83,7 +83,7 @@ func (e *Endpoint) InstanceName() string { func (e *Endpoint) Start() error { for _, endp := range e.endpoints { - l, err := net.Listen(endp.Network(), endp.Address()) + l, err := netresource.Listen(endp.Network(), endp.Address()) if err != nil { e.Stop() return fmt.Errorf("%s: %v", modName, err) diff --git a/internal/endpoint/smtp/smtp.go b/internal/endpoint/smtp/smtp.go index 7e5f3ab0..ee433e86 100644 --- a/internal/endpoint/smtp/smtp.go +++ b/internal/endpoint/smtp/smtp.go @@ -41,6 +41,7 @@ import ( "github.com/foxcpp/maddy/framework/future" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/resource/netresource" "github.com/foxcpp/maddy/internal/auth" "github.com/foxcpp/maddy/internal/authz" "github.com/foxcpp/maddy/internal/limits" @@ -107,7 +108,7 @@ func New(modName string, addrs []string) (module.LifetimeModule, error) { func (endp *Endpoint) Configure(_ []string, cfg *config.Map) error { endp.serv = smtp.NewServer(endp) - endp.serv.ErrorLog = endp.Log + endp.serv.ErrorLog = &endp.Log endp.serv.LMTP = endp.lmtp endp.serv.EnableSMTPUTF8 = true endp.serv.EnableREQUIRETLS = true @@ -328,7 +329,7 @@ func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { for _, addr := range addresses { var l net.Listener var err error - l, err = net.Listen(addr.Network(), addr.Address()) + l, err = netresource.Listen(addr.Network(), addr.Address()) if err != nil { return fmt.Errorf("%s: %w", endp.name, err) } diff --git a/internal/updatepipe/unix_pipe.go b/internal/updatepipe/unix_pipe.go index a8249f90..945c4f95 100644 --- a/internal/updatepipe/unix_pipe.go +++ b/internal/updatepipe/unix_pipe.go @@ -27,6 +27,7 @@ import ( mess "github.com/foxcpp/go-imap-mess" "github.com/foxcpp/maddy/framework/log" + "github.com/foxcpp/maddy/framework/resource/netresource" ) // UnixSockPipe implements the UpdatePipe interface by serializating updates @@ -74,7 +75,7 @@ func (usp *UnixSockPipe) readUpdates(conn net.Conn, updCh chan<- mess.Update) { } func (usp *UnixSockPipe) Listen(upd chan<- mess.Update) error { - l, err := net.Listen("unix", usp.SockPath) + l, err := netresource.Listen("unix", usp.SockPath) if err != nil { return err } diff --git a/maddy.go b/maddy.go index 829cdf76..e0a33e1d 100644 --- a/maddy.go +++ b/maddy.go @@ -36,6 +36,7 @@ import ( "github.com/foxcpp/maddy/framework/hooks" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/resource/netresource" "github.com/foxcpp/maddy/internal/authz" maddycli "github.com/foxcpp/maddy/internal/cli" "github.com/urfave/cli/v2" @@ -422,12 +423,14 @@ func moduleReload(oldContainer *container.C, configPath string) *container.C { oldContainer.DefaultLogger.Msg("configuration loaded") + netresource.ResetListenersUsage() oldContainer.DefaultLogger.Msg("starting new server") if err := moduleStart(newContainer); err != nil { oldContainer.DefaultLogger.Error("failed to start new server", err) container.Global = oldContainer return oldContainer } + netresource.CloseUnusedListeners() newContainer.DefaultLogger.Msg("server started", "version", Version) oldContainer.DefaultLogger.Msg("stopping server") From d712d8cc64e6d01437dc74a0f5a3918d06a1e695 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月30日 03:36:55 +0300 Subject: [PATCH 103/171] netresource: Improve logging, fix bug with listeners being recreated on reload --- dist/systemd/maddy.service | 1 - dist/systemd/maddy@.service | 1 - docs/reference/endpoints/smtp.md | 9 ++++++ framework/module/lifetime.go | 4 ++- framework/resource/netresource/listen.go | 6 +++- framework/resource/netresource/tracker.go | 16 +++++----- framework/resource/singleton.go | 10 ++++++- framework/resource/tracker.go | 6 +++- internal/endpoint/smtp/smtp.go | 11 +++++-- maddy.go | 36 +++++++++++++++++------ 10 files changed, 74 insertions(+), 26 deletions(-) diff --git a/dist/systemd/maddy.service b/dist/systemd/maddy.service index ec1ac29c..18236568 100644 --- a/dist/systemd/maddy.service +++ b/dist/systemd/maddy.service @@ -75,7 +75,6 @@ RestartPreventExitStatus=2 ExecStart=/usr/local/bin/maddy run -ExecReload=/bin/kill -USR1 $MAINPID ExecReload=/bin/kill -USR2 $MAINPID [Install] diff --git a/dist/systemd/maddy@.service b/dist/systemd/maddy@.service index ea60ff84..4ba3b54d 100644 --- a/dist/systemd/maddy@.service +++ b/dist/systemd/maddy@.service @@ -71,7 +71,6 @@ RestartPreventExitStatus=2 ExecStart=/usr/local/bin/maddy --config /etc/maddy/%i.conf run -ExecReload=/bin/kill -USR1 $MAINPID ExecReload=/bin/kill -USR2 $MAINPID [Install] diff --git a/docs/reference/endpoints/smtp.md b/docs/reference/endpoints/smtp.md index 4dfa723a..58d3d9b1 100644 --- a/docs/reference/endpoints/smtp.md +++ b/docs/reference/endpoints/smtp.md @@ -14,6 +14,7 @@ smtp tcp://0.0.0.0:25 { sasl_login no read_timeout 10m write_timeout 1m + shutdown_timeout 3m max_message_size 32M max_header_size 1M auth pam @@ -125,6 +126,14 @@ I/O write timeout. --- +### shutdown_timeout _duration_ +Default: `3m` + +Time to wait until forcibly closing connections on server shutdown +or configuration reload. + +--- + ### max_message_size _size_ Default: `32M` diff --git a/framework/module/lifetime.go b/framework/module/lifetime.go index ede5d2b4..5338b01f 100644 --- a/framework/module/lifetime.go +++ b/framework/module/lifetime.go @@ -96,7 +96,9 @@ func (lt *LifetimeTracker) ReloadAll() error { // StopAll calls Stop for all registered LifetimeModule instances. func (lt *LifetimeTracker) StopAll() error { - for _, entry := range lt.instances { + for i := len(lt.instances) - 1; i>= 0; i-- { + entry := lt.instances[i] + if !entry.started { continue } diff --git a/framework/resource/netresource/listen.go b/framework/resource/netresource/listen.go index e2328002..6d164fd7 100644 --- a/framework/resource/netresource/listen.go +++ b/framework/resource/netresource/listen.go @@ -13,7 +13,11 @@ var ( ) func CloseUnusedListeners() error { - return tracker.Close() + return tracker.CloseUnused() +} + +func CloseAllListeners() { + tracker.Close() } func ResetListenersUsage() { diff --git a/framework/resource/netresource/tracker.go b/framework/resource/netresource/tracker.go index 97989c62..a1827aa2 100644 --- a/framework/resource/netresource/tracker.go +++ b/framework/resource/netresource/tracker.go @@ -67,12 +67,8 @@ func (lt *ListenerTracker) ResetUsage() { } func (lt *ListenerTracker) CloseUnused() error { - lt.tcp.CloseUnused(func(key string) bool { - return false - }) - lt.unix.CloseUnused(func(key string) bool { - return false - }) + lt.tcp.CloseUnused(func(key string) bool { return true }) + lt.unix.CloseUnused(func(key string) bool { return true }) return nil } @@ -83,9 +79,11 @@ func (lt *ListenerTracker) Close() error { } func NewListenerTracker(log *log.Logger) *ListenerTracker { - return &ListenerTracker{ + lt := &ListenerTracker{ logger: log, - tcp: resource.NewTracker[*net.TCPListener](resource.NewSingleton[*net.TCPListener]()), - unix: resource.NewTracker[*net.UnixListener](resource.NewSingleton[*net.UnixListener]()), + tcp: resource.NewTracker[*net.TCPListener](resource.NewSingleton[*net.TCPListener](log.Sublogger("tcp"))), + unix: resource.NewTracker[*net.UnixListener](resource.NewSingleton[*net.UnixListener](log.Sublogger("unix"))), } + + return lt } diff --git a/framework/resource/singleton.go b/framework/resource/singleton.go index 2614cee2..45f5b903 100644 --- a/framework/resource/singleton.go +++ b/framework/resource/singleton.go @@ -2,16 +2,20 @@ package resource import ( "sync" + + "github.com/foxcpp/maddy/framework/log" ) // Singleton represents a set of resources identified by an unique key. type Singleton[T Resource] struct { + log *log.Logger lock sync.RWMutex resources map[string]T } -func NewSingleton[T Resource]() *Singleton[T] { +func NewSingleton[T Resource](log *log.Logger) *Singleton[T] { return &Singleton[T]{ + log: log, resources: make(map[string]T), } } @@ -22,6 +26,7 @@ func (s *Singleton[T]) GetOpen(key string, open func() (T, error)) (T, error) { existing, ok := s.resources[key] if ok { + s.log.DebugMsg("resource reused", "key", key) return existing, nil } @@ -31,6 +36,7 @@ func (s *Singleton[T]) GetOpen(key string, open func() (T, error)) (T, error) { return empty, err } + s.log.DebugMsg("new resource", "key", key) s.resources[key] = res return res, nil @@ -44,6 +50,7 @@ func (s *Singleton[T]) CloseUnused(isUsed func(key string) bool) error { if isUsed(key) { continue } + s.log.DebugMsg("resource released", "key", key) res.Close() delete(s.resources, key) } @@ -56,6 +63,7 @@ func (s *Singleton[T]) Close() error { defer s.lock.Unlock() for key, res := range s.resources { + s.log.DebugMsg("resource released", "key", key) res.Close() delete(s.resources, key) } diff --git a/framework/resource/tracker.go b/framework/resource/tracker.go index 318cf298..ae2d9796 100644 --- a/framework/resource/tracker.go +++ b/framework/resource/tracker.go @@ -42,6 +42,10 @@ func (t *Tracker[T]) CloseUnused(isUsed func(key string) bool) error { return t.C.CloseUnused(func(key string) bool { used := t.used[key] - return used && isUsed(key) + used = used && isUsed(key) + if !used { + delete(t.used, key) + } + return used }) } diff --git a/internal/endpoint/smtp/smtp.go b/internal/endpoint/smtp/smtp.go index ee433e86..8c1b9616 100644 --- a/internal/endpoint/smtp/smtp.go +++ b/internal/endpoint/smtp/smtp.go @@ -72,7 +72,8 @@ type Endpoint struct { maxReceived int maxHeaderBytes int64 - sessionCnt atomic.Int32 + sessionCnt atomic.Int32 + shutdownTimeout time.Duration authNormalize authz.NormalizeFunc authMap module.Table @@ -251,6 +252,7 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { modconfig.Table(cfg, "auth_map", true, false, nil, &endp.saslAuth.AuthMap) cfg.Duration("write_timeout", false, false, 1*time.Minute, &endp.serv.WriteTimeout) cfg.Duration("read_timeout", false, false, 10*time.Minute, &endp.serv.ReadTimeout) + cfg.Duration("shutdown_timeout", false, false, 3*time.Minute, &endp.shutdownTimeout) cfg.DataSize("max_message_size", false, false, 32*1024*1024, &endp.serv.MaxMessageBytes) cfg.DataSize("max_header_size", false, false, 1*1024*1024, &endp.maxHeaderBytes) cfg.Int("max_recipients", false, false, 20000, &endp.serv.MaxRecipients) @@ -424,8 +426,13 @@ func (endp *Endpoint) ConnectionCount() int { } func (endp *Endpoint) Stop() error { - endp.serv.Close() + ctx, cancel := context.WithTimeout(context.Background(), endp.shutdownTimeout) + defer cancel() + + endp.serv.Shutdown(ctx) + endp.listenersWg.Wait() + return nil } diff --git a/maddy.go b/maddy.go index e0a33e1d..c60360e2 100644 --- a/maddy.go +++ b/maddy.go @@ -26,6 +26,7 @@ import ( "path/filepath" "runtime" "runtime/debug" + "sync" "github.com/caddyserver/certmagic" parser "github.com/foxcpp/maddy/framework/cfgparser" @@ -206,6 +207,8 @@ func Run(c *cli.Context) error { defer log.DefaultLogger.Out.Close() defer hooks.RunHooks(hooks.EventShutdown) + hooks.AddHook(hooks.EventShutdown, netresource.CloseAllListeners) + if err := moduleMain(c.Path("config")); err != nil { systemdStatusErr(err) return cli.Exit(err.Error(), 1) @@ -395,24 +398,29 @@ func moduleMain(configPath string) error { } c.DefaultLogger.Msg("server started", "version", Version) - systemdStatus(SDReady, "Listening for incoming connections...") + systemdStatus(SDReady, "Configuration running.") + asyncStopWg := sync.WaitGroup{} // Some containers might still be waiting on moduleStop for handleSignals() { - systemdStatus(SDReloading, "Reloading state...") hooks.RunHooks(hooks.EventReload) - c = moduleReload(c, configPath) + c = moduleReload(c, configPath, &asyncStopWg) } c.DefaultLogger.Msg("server stopping...") - systemdStatus(SDStopping, "Waiting for running transactions to complete...") + systemdStatus(SDStopping, "Waiting for old configuration to stop...") + asyncStopWg.Wait() + + systemdStatus(SDStopping, "Waiting for current configuration to stop...") moduleStop(c) c.DefaultLogger.Msg("server stopped") + return nil } -func moduleReload(oldContainer *container.C, configPath string) *container.C { +func moduleReload(oldContainer *container.C, configPath string, asyncStopWg *sync.WaitGroup) *container.C { oldContainer.DefaultLogger.Msg("reloading server...") + systemdStatus(SDReloading, "Reloading server...") oldContainer.DefaultLogger.Msg("loading new configuration...") newContainer, err := moduleConfigure(configPath) @@ -430,12 +438,22 @@ func moduleReload(oldContainer *container.C, configPath string) *container.C { container.Global = oldContainer return oldContainer } - netresource.CloseUnusedListeners() newContainer.DefaultLogger.Msg("server started", "version", Version) - oldContainer.DefaultLogger.Msg("stopping server") - moduleStop(oldContainer) - oldContainer.DefaultLogger.Msg("server stopped") + + systemdStatus(SDReloading, "New configuration running. Waiting for old connections and transactions to finish...") + + asyncStopWg.Add(1) + go func() { + defer asyncStopWg.Done() + defer netresource.CloseUnusedListeners() + + oldContainer.DefaultLogger.Msg("stopping old server") + moduleStop(oldContainer) + oldContainer.DefaultLogger.Msg("old server stopped") + + systemdStatus(SDReloading, "Configuration running.") + }() return newContainer } From 95ba6bf1fc71d12c2fed0f1eadcb5fc237d2a5c8 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月30日 03:52:41 +0300 Subject: [PATCH 104/171] cli/cli: Fix panic on module access --- internal/cli/ctl/moduleinit.go | 1 + internal/table/sql_query.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/cli/ctl/moduleinit.go b/internal/cli/ctl/moduleinit.go index ef58d7b4..1129a516 100644 --- a/internal/cli/ctl/moduleinit.go +++ b/internal/cli/ctl/moduleinit.go @@ -44,6 +44,7 @@ func getCfgBlockModule(ctx *cli.Context) (*container.C, module.Module, error) { } c := container.New() + container.Global = c cfg, err := maddy.ReadConfig(cfgPath) if err != nil { diff --git a/internal/table/sql_query.go b/internal/table/sql_query.go index 96bce8f0..0fe5c64f 100644 --- a/internal/table/sql_query.go +++ b/internal/table/sql_query.go @@ -133,11 +133,11 @@ func (s *SQL) Configure(inlineArgs []string, cfg *config.Map) error { return nil } - return nil + return s.prepare() } func (s *SQL) Start() error { - return s.prepare() + return nil } func (s *SQL) Stop() error { From b4e8716bba8e9c067cd66ea28e85884b73bf788a Mon Sep 17 00:00:00 2001 From: James Mills Date: 2025年1月27日 09:17:01 +1000 Subject: [PATCH 105/171] Add support for GCore DNS --- .gitignore | 2 ++ go.mod | 6 +++++- go.sum | 4 ++++ internal/libdns/gcore.go | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 internal/libdns/gcore.go diff --git a/.gitignore b/.gitignore index 5bf94b90..790b848a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,8 @@ _testmain.go *.exe~ *.test *.prof +**/.envrc +**/.DS_Store # Tests coverage *.out diff --git a/go.mod b/go.mod index bfa5673c..668bf27b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/foxcpp/maddy -go 1.23 +go 1.23.1 + +toolchain go1.23.5 require ( blitiri.com.ar/go/spf v1.5.1 @@ -34,6 +36,7 @@ require ( github.com/libdns/cloudflare v0.1.1 github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea github.com/libdns/gandi v1.0.3 + github.com/libdns/gcore v0.0.0-20250127070537-4a9d185c9d20 github.com/libdns/googleclouddns v1.1.0 github.com/libdns/hetzner v0.0.1 github.com/libdns/leaseweb v0.4.0 @@ -64,6 +67,7 @@ require ( cloud.google.com/go/compute/metadata v0.6.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect + github.com/G-Core/gcore-dns-sdk-go v0.2.9 // indirect github.com/aws/aws-sdk-go v1.44.40 // indirect github.com/aws/aws-sdk-go-v2 v1.33.0 // indirect github.com/aws/aws-sdk-go-v2/config v1.29.1 // indirect diff --git a/go.sum b/go.sum index 2f99728a..4d3eaea7 100644 --- a/go.sum +++ b/go.sum @@ -190,6 +190,8 @@ github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzS github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/G-Core/gcore-dns-sdk-go v0.2.9 h1:LMMZIRX8y3aJJuAviNSpFmLbovZUw+6Om+8VElp1F90= +github.com/G-Core/gcore-dns-sdk-go v0.2.9/go.mod h1:35t795gOfzfVanhzkFyUXEzaBuMXwETmJldPpP28MN4= github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 h1:IEjq88XO4PuBDcvmjQJcQGg+w+UaafSy8G5Kcb5tBhI= github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5/go.mod h1:exZ0C/1emQJAw5tHOaUDyY1ycttqBAPcxuzf7QbY6ec= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -521,6 +523,8 @@ github.com/libdns/cloudflare v0.1.1 h1:FVPfWwP8zZCqj268LZjmkDleXlHPlFU9KC4OJ3yn0 github.com/libdns/cloudflare v0.1.1/go.mod h1:9VK91idpOjg6v7/WbjkEW49bSCxj00ALesIFDhJ8PBU= github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea h1:IGlMNZCUp8Ho7NYYorpP5ZJgg2mFXARs6eHs/pSqFkA= github.com/libdns/digitalocean v0.0.0-20230728223659-4f9064657aea/go.mod h1:B2TChhOTxvBflpRTHlguXWtwa1Ha5WI6JkB6aCViM+0= +github.com/libdns/gcore v0.0.0-20250127070537-4a9d185c9d20 h1:bQwFw+C9sX/zYZlV53ey0KnNkxrfWYIFpvptuAVhJ1Y= +github.com/libdns/gcore v0.0.0-20250127070537-4a9d185c9d20/go.mod h1:JGoT1mbmqQwtYQqN5F/vGc9j4TTTMKw/hDm5vXADHUI= github.com/libdns/googleclouddns v1.1.0 h1:murPR1LfTZZObLV2OLxUVmymWH25glkMFKpDjkk2m0E= github.com/libdns/googleclouddns v1.1.0/go.mod h1:3tzd056dfqKlf71V8Oy19En4WjJ3ybyuWx6P9bQSCIw= github.com/libdns/hetzner v0.0.1 h1:WsmcsOKnfpKmzwhfyqhGQEIlEeEaEUvb7ezoJgBKaqU= diff --git a/internal/libdns/gcore.go b/internal/libdns/gcore.go new file mode 100644 index 00000000..01d7afb6 --- /dev/null +++ b/internal/libdns/gcore.go @@ -0,0 +1,34 @@ +//go:build libdns_gcore || !libdns_separate +// +build libdns_gcore !libdns_separate + +package libdns + +import ( + "fmt" + + "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/module" + "github.com/libdns/gcore" +) + +func init() { + module.Register("libdns.gcore", func(modName, instName string, _, _ []string) (module.Module, error) { + p := gcore.Provider{} + return &ProviderModule{ + RecordDeleter: &p, + RecordAppender: &p, + setConfig: func(c *config.Map) { + c.String("api_key", false, false, "", &p.APIKey) + }, + afterConfig: func() error { + if p.APIKey == "" { + return fmt.Errorf("libdns.gcore: api_key should be specified") + } + return nil + }, + + instName: instName, + modName: modName, + }, nil + }) +} From 91c1617698b661d1135a8da5b832b56e397de3c1 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2025年1月30日 21:50:28 +0300 Subject: [PATCH 106/171] tests: Fix-up linter nit --- tests/t.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/t.go b/tests/t.go index 3ae27c65..c1c04bc3 100644 --- a/tests/t.go +++ b/tests/t.go @@ -242,7 +242,8 @@ func (t *T) MustRunCLIGroup(args ...[]string) { _, err := t.RunCLI(arg...) if err != nil { - t.Fatalf("maddy %v: %v", arg, err) + t.Printf("maddy %v: %v", arg, err) + t.Fail() } }() } From 1d044249c2400a6585e79723e125ca270898747e Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Sat, 1 Feb 2025 19:14:45 +0300 Subject: [PATCH 107/171] endpoint/smtp: Fix auth_map ignored --- internal/endpoint/smtp/smtp.go | 4 +- tests/multiple_domains_test.go | 69 ++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/internal/endpoint/smtp/smtp.go b/internal/endpoint/smtp/smtp.go index 004d92e5..1d38c57b 100644 --- a/internal/endpoint/smtp/smtp.go +++ b/internal/endpoint/smtp/smtp.go @@ -289,6 +289,8 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { return err } + endp.saslAuth.Log.Debug = endp.Log.Debug + // INTERNATIONALIZATION: See RFC 6531 Section 3.3. endp.serv.Domain, err = idna.ToASCII(hostname) if err != nil { @@ -310,8 +312,6 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { return fmt.Errorf("%s: auth. provider must be set for submission endpoint", endp.name) } } - endp.saslAuth.AuthNormalize = endp.authNormalize - endp.saslAuth.AuthMap = endp.authMap if ioDebug { endp.serv.Debug = endp.Log.DebugWriter() diff --git a/tests/multiple_domains_test.go b/tests/multiple_domains_test.go index 971ca91b..df00cf2d 100644 --- a/tests/multiple_domains_test.go +++ b/tests/multiple_domains_test.go @@ -71,26 +71,44 @@ func TestMultipleDomains_SeparateNamespace(tt *testing.T) { t.Run(2) user1 := t.Conn("imap") + defer user1.Close() user1.ExpectPattern(`\* OK *`) user1.Writeln(`. LOGIN user1@test1.maddy.email user1`) user1.ExpectPattern(`. OK *`) user1.Writeln(`. CREATE user1`) user1.ExpectPattern(`. OK *`) + user1SMTP := t.Conn("submission") + defer user1SMTP.Close() + user1SMTP.SMTPNegotation("localhost", []string{"AUTH PLAIN"}, nil) + user1SMTP.SMTPPlainAuth("user1@test1.maddy.email", "user1", true) + user2 := t.Conn("imap") + defer user2.Close() user2.ExpectPattern(`\* OK *`) user2.Writeln(`. LOGIN user2@test1.maddy.email user2`) user2.ExpectPattern(`. OK *`) user2.Writeln(`. CREATE user2`) user2.ExpectPattern(`. OK *`) + user2SMTP := t.Conn("submission") + defer user2SMTP.Close() + user2SMTP.SMTPNegotation("localhost", []string{"AUTH PLAIN"}, nil) + user2SMTP.SMTPPlainAuth("user2@test1.maddy.email", "user2", true) + user3 := t.Conn("imap") + defer user3.Close() user3.ExpectPattern(`\* OK *`) user3.Writeln(`. LOGIN user1@test2.maddy.email user3`) user3.ExpectPattern(`. OK *`) user3.Writeln(`. CREATE user3`) user3.ExpectPattern(`. OK *`) + user3SMTP := t.Conn("submission") + defer user3SMTP.Close() + user3SMTP.SMTPNegotation("localhost", []string{"AUTH PLAIN"}, nil) + user3SMTP.SMTPPlainAuth("user3@test2.maddy.email", "user3", true) + user1.Writeln(`. LIST "" "*"`) user1.Expect(`* LIST (\HasNoChildren) "." INBOX`) user1.Expect(`* LIST (\HasNoChildren) "." "user1"`) @@ -150,26 +168,44 @@ func TestMultipleDomains_SharedCredentials_DistinctMailboxes(tt *testing.T) { t.Run(2) user1 := t.Conn("imap") + defer user1.Close() user1.ExpectPattern(`\* OK *`) user1.Writeln(`. LOGIN user1@test1.maddy.email user1`) user1.ExpectPattern(`. OK *`) user1.Writeln(`. CREATE user1`) user1.ExpectPattern(`. OK *`) + user1SMTP := t.Conn("submission") + defer user1SMTP.Close() + user1SMTP.SMTPNegotation("localhost", []string{"AUTH PLAIN"}, nil) + user1SMTP.SMTPPlainAuth("user1@test1.maddy.email", "user1", true) + user2 := t.Conn("imap") + defer user2.Close() user2.ExpectPattern(`\* OK *`) user2.Writeln(`. LOGIN user2@test1.maddy.email user2`) user2.ExpectPattern(`. OK *`) user2.Writeln(`. CREATE user2`) user2.ExpectPattern(`. OK *`) + user2SMTP := t.Conn("submission") + defer user2SMTP.Close() + user2SMTP.SMTPNegotation("localhost", []string{"AUTH PLAIN"}, nil) + user2SMTP.SMTPPlainAuth("user2@test1.maddy.email", "user2", true) + user3 := t.Conn("imap") + defer user3.Close() user3.ExpectPattern(`\* OK *`) user3.Writeln(`. LOGIN user1@test2.maddy.email user1`) user3.ExpectPattern(`. OK *`) user3.Writeln(`. CREATE user3`) user3.ExpectPattern(`. OK *`) + user3SMTP := t.Conn("submission") + defer user3SMTP.Close() + user3SMTP.SMTPNegotation("localhost", []string{"AUTH PLAIN"}, nil) + user3SMTP.SMTPPlainAuth("user1@test2.maddy.email", "user1", true) + user1.Writeln(`. LIST "" "*"`) user1.Expect(`* LIST (\HasNoChildren) "." INBOX`) user1.Expect(`* LIST (\HasNoChildren) "." "user1"`) @@ -232,30 +268,62 @@ func TestMultipleDomains_SharedCredentials_SharedMailboxes(tt *testing.T) { t.Run(2) user1 := t.Conn("imap") + defer user1.Close() user1.ExpectPattern(`\* OK *`) user1.Writeln(`. LOGIN user1 user1`) user1.ExpectPattern(`. OK *`) user1.Writeln(`. CREATE user1`) user1.ExpectPattern(`. OK *`) + user1SMTP := t.Conn("submission") + defer user1SMTP.Close() + user1SMTP.SMTPNegotation("localhost", []string{"AUTH PLAIN"}, nil) + user1SMTP.SMTPPlainAuth("user1", "user1", true) + user2 := t.Conn("imap") + defer user2.Close() user2.ExpectPattern(`\* OK *`) user2.Writeln(`. LOGIN user2@test1.maddy.email user2`) user2.ExpectPattern(`. OK *`) user2.Writeln(`. CREATE user2`) user2.ExpectPattern(`. OK *`) + user2SMTP := t.Conn("submission") + defer user2SMTP.Close() + user2SMTP.SMTPNegotation("localhost", []string{"AUTH PLAIN"}, nil) + user2SMTP.SMTPPlainAuth("user2", "user2", true) + user12 := t.Conn("imap") + defer user12.Close() user12.ExpectPattern(`\* OK *`) user12.Writeln(`. LOGIN user1@test2.maddy.email user1`) user12.ExpectPattern(`. OK *`) user12.Writeln(`. CREATE user12`) user12.ExpectPattern(`. OK *`) + user13 := t.Conn("imap") + defer user13.Close() + user13.ExpectPattern(`\* OK *`) + user13.Writeln(`. LOGIN user1@test.maddy.email user1`) + user13.ExpectPattern(`. OK *`) + user13.Writeln(`. CREATE user13`) + user13.ExpectPattern(`. OK *`) + + user12SMTP := t.Conn("submission") + defer user12SMTP.Close() + user12SMTP.SMTPNegotation("localhost", []string{"AUTH PLAIN"}, nil) + user12SMTP.SMTPPlainAuth("user1", "user1", true) + + user13SMTP := t.Conn("submission") + defer user13SMTP.Close() + user13SMTP.SMTPNegotation("localhost", []string{"AUTH PLAIN"}, nil) + user13SMTP.SMTPPlainAuth("user1@test.maddy.email", "user1", true) + user1.Writeln(`. LIST "" "*"`) user1.Expect(`* LIST (\HasNoChildren) "." INBOX`) user1.Expect(`* LIST (\HasNoChildren) "." "user1"`) user1.Expect(`* LIST (\HasNoChildren) "." "user12"`) + user1.Expect(`* LIST (\HasNoChildren) "." "user13"`) user1.ExpectPattern(". OK *") user2.Writeln(`. LIST "" "*"`) @@ -267,5 +335,6 @@ func TestMultipleDomains_SharedCredentials_SharedMailboxes(tt *testing.T) { user12.Expect(`* LIST (\HasNoChildren) "." INBOX`) user12.Expect(`* LIST (\HasNoChildren) "." "user1"`) user12.Expect(`* LIST (\HasNoChildren) "." "user12"`) + user12.Expect(`* LIST (\HasNoChildren) "." "user13"`) user12.ExpectPattern(". OK *") } From ef7fa210dca00c2a883f9475c63f9aed4eef579f Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Sat, 1 Feb 2025 19:24:53 +0300 Subject: [PATCH 108/171] Slightly improve debug logging for complex authentication pipelines --- framework/log/orderedjson.go | 7 +++++++ internal/auth/sasl.go | 15 +++++++-------- internal/endpoint/dovecot_sasld/dovecot_sasl.go | 1 + internal/endpoint/imap/imap.go | 2 ++ maddy.go | 10 +++++----- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/framework/log/orderedjson.go b/framework/log/orderedjson.go index 834f3a6c..bbe01c29 100644 --- a/framework/log/orderedjson.go +++ b/framework/log/orderedjson.go @@ -31,6 +31,11 @@ import ( // human-readable when values from multiple messages are lined up to each // other. +type module interface { + Name() string + InstanceName() string +} + func marshalOrderedJSON(output *strings.Builder, m map[string]interface{}) error { order := make([]string, 0, len(m)) for k := range m { @@ -62,6 +67,8 @@ func marshalOrderedJSON(output *strings.Builder, m map[string]interface{}) error val = casted.FormatLog() case fmt.Stringer: val = casted.String() + case module: + val = casted.Name() + "/" + casted.InstanceName() case error: val = casted.Error() } diff --git a/internal/auth/sasl.go b/internal/auth/sasl.go index 21a3d897..545c523e 100644 --- a/internal/auth/sasl.go +++ b/internal/auth/sasl.go @@ -105,12 +105,16 @@ func (s *SASLAuth) AuthPlain(username, password string) error { var lastErr error for _, p := range s.Plain { - username, err := s.usernameForAuth(context.TODO(), username) + mappedUsername, err := s.usernameForAuth(context.TODO(), username) if err != nil { return err } - lastErr = p.AuthPlain(username, password) + s.Log.DebugMsg("attempting authentication", + "mapped_username", mappedUsername, "original_username", username, + "module", p) + + lastErr = p.AuthPlain(mappedUsername, password) if lastErr == nil { return nil } @@ -139,12 +143,7 @@ func (s *SASLAuth) CreateSASL(mech string, remoteAddr net.Addr, successCb func(i return ErrInvalidAuthCred } - username, err := s.usernameForAuth(context.Background(), username) - if err != nil { - return err - } - - err = s.AuthPlain(username, password) + err := s.AuthPlain(username, password) if err != nil { s.Log.Error("authentication failed", err, "username", username, "src_ip", remoteAddr) return ErrInvalidAuthCred diff --git a/internal/endpoint/dovecot_sasld/dovecot_sasl.go b/internal/endpoint/dovecot_sasld/dovecot_sasl.go index 77eedd0e..26796962 100644 --- a/internal/endpoint/dovecot_sasld/dovecot_sasl.go +++ b/internal/endpoint/dovecot_sasld/dovecot_sasl.go @@ -79,6 +79,7 @@ func (endp *Endpoint) Init(cfg *config.Map) error { endp.srv = dovecotsasl.NewServer() endp.srv.Log = stdlog.New(endp.log, "", 0) + endp.saslAuth.Log.Debug = endp.log.Debug for _, mech := range endp.saslAuth.SASLMechanisms() { endp.srv.AddMechanism(mech, mechInfo[mech], func(req *dovecotsasl.AuthReq) sasl.Server { diff --git a/internal/endpoint/imap/imap.go b/internal/endpoint/imap/imap.go index 191d93d2..cece7972 100644 --- a/internal/endpoint/imap/imap.go +++ b/internal/endpoint/imap/imap.go @@ -112,6 +112,8 @@ func (endp *Endpoint) Init(cfg *config.Map) error { } } + endp.saslAuth.Log.Debug = endp.Log.Debug + addresses := make([]config.Endpoint, 0, len(endp.addrs)) for _, addr := range endp.addrs { saddr, err := config.ParseEndpoint(addr) diff --git a/maddy.go b/maddy.go index f838e96e..5439fba1 100644 --- a/maddy.go +++ b/maddy.go @@ -110,15 +110,15 @@ func init() { Value: filepath.Join(ConfigDirectory, "maddy.conf"), }, ) + maddycli.AddGlobalFlag(&cli.BoolFlag{ + Name: "debug", + Usage: "enable debug logging early", + Destination: &log.DefaultLogger.Debug, + }) maddycli.AddSubcommand(&cli.Command{ Name: "run", Usage: "Start the server", Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "debug", - Usage: "enable debug logging early", - Destination: &log.DefaultLogger.Debug, - }, &cli.StringFlag{ Name: "libexec", Value: DefaultLibexecDirectory, From 01c65cfb0e40d3c5898ce831fc45a24f0f261ab9 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Sun, 2 Feb 2025 13:14:11 +0300 Subject: [PATCH 109/171] Finally fix tests Took way too long. --- internal/endpoint/smtp/smtp.go | 3 --- tests/multiple_domains_test.go | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/endpoint/smtp/smtp.go b/internal/endpoint/smtp/smtp.go index 1d38c57b..8f800e52 100644 --- a/internal/endpoint/smtp/smtp.go +++ b/internal/endpoint/smtp/smtp.go @@ -72,9 +72,6 @@ type Endpoint struct { sessionCnt atomic.Int32 - authNormalize authz.NormalizeFunc - authMap module.Table - listenersWg sync.WaitGroup Log log.Logger diff --git a/tests/multiple_domains_test.go b/tests/multiple_domains_test.go index df00cf2d..6b29c3fa 100644 --- a/tests/multiple_domains_test.go +++ b/tests/multiple_domains_test.go @@ -107,7 +107,7 @@ func TestMultipleDomains_SeparateNamespace(tt *testing.T) { user3SMTP := t.Conn("submission") defer user3SMTP.Close() user3SMTP.SMTPNegotation("localhost", []string{"AUTH PLAIN"}, nil) - user3SMTP.SMTPPlainAuth("user3@test2.maddy.email", "user3", true) + user3SMTP.SMTPPlainAuth("user1@test2.maddy.email", "user3", true) user1.Writeln(`. LIST "" "*"`) user1.Expect(`* LIST (\HasNoChildren) "." INBOX`) From 96790bd82fcccaaaf59f1564105df77943c6fcb0 Mon Sep 17 00:00:00 2001 From: Alexey Nurmukhametov Date: Sat, 8 Feb 2025 19:19:49 +0000 Subject: [PATCH 110/171] Build aarch64 binary in release workflow Building in a Docker container is a workaround for the issue of JavaScript-based GitHub Actions not being supported in Alpine containers on the Arm64 platform. Otherwise, we could completely reuse artifact-builder-x86 as a matrix job by running it on an Arm runner. This could be done later when upload-artifact works in an Alpine Arm64 container. --- .github/workflows/release.yml | 47 +++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0b5dfab0..e7155b68 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,8 +11,8 @@ permissions: packages: write jobs: - artifact-builder: - name: "Prepare release artifacts" + artifact-builder-x86: + name: "Prepare release artifacts (x86)" if: github.ref_type == 'tag' runs-on: ubuntu-latest container: @@ -55,6 +55,49 @@ jobs: uses: actions/attest-build-provenance@v2 with: subject-path: '~/maddy-x86_64-linux-musl.tar.zst' + artifact-builder-arm: + name: "Prepare release artifacts (aarch64)" + if: github.ref_type == 'tag' + runs-on: ubuntu-22.04-arm + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + # Building in a Docker container is a workaround for the issue of + # JavaScript-based GitHub Actions not being supported in Alpine + # containers on the Arm64 platform. Otherwise, we could completely reuse + # artifact-builder-x86 as a matrix job by running it on an Arm runner. + - name: Build in Docker container + run: | + # Create Dockerfile for the build + cat> Dockerfile << 'EOF' + FROM alpine:edge + RUN apk add --no-cache gcc go zstd musl-dev scdoc + WORKDIR /build + COPY . . + RUN ./build.sh --builddir /package-output/ --static build && \ + ver=$(cat .version) && \ + if [ "v$ver" != "${{github.ref_name}}" ]; then echo ".version does not match the Git tag"; exit 1; fi && \ + mv /package-output/ /maddy-$ver-aarch64-linux-musl && \ + cd / && \ + tar c ./maddy-$ver-aarch64-linux-musl | zstd> /maddy-aarch64-linux-musl.tar.zst + EOF + # Build the image, create a temporary container and copy the artifact. + docker build -t maddy-builder . + container_id=$(docker create maddy-builder) + docker cp $container_id:/maddy-aarch64-linux-musl.tar.zst . + docker rm $container_id + - name: Upload binary tree + uses: actions/upload-artifact@v4 + with: + name: maddy-binary-aarch64.tar.zst + path: maddy-aarch64-linux-musl.tar.zst + if-no-files-found: error + - name: "Generate artifact attestation" + uses: actions/attest-build-provenance@v2 + with: + subject-path: 'maddy-aarch64-linux-musl.tar.zst' docker-builder: name: "Build & push Docker image" if: github.ref_type == 'tag' From 0e953a824bfc942e4344b2112750840155b2d44c Mon Sep 17 00:00:00 2001 From: Mark Lipscombe Date: 2025年2月10日 08:41:48 -0500 Subject: [PATCH 111/171] fix: make `tls_client` configuration work in `target.smtp` block --- internal/target/smtp/smtp_downstream.go | 6 +++--- internal/target/smtp/smtp_downstream_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/target/smtp/smtp_downstream.go b/internal/target/smtp/smtp_downstream.go index 8880f6cb..294e283e 100644 --- a/internal/target/smtp/smtp_downstream.go +++ b/internal/target/smtp/smtp_downstream.go @@ -57,7 +57,7 @@ type Downstream struct { hostname string endpoints []config.Endpoint saslFactory saslClientFactory - tlsConfig tls.Config + tlsConfig *tls.Config connectTimeout time.Duration commandTimeout time.Duration @@ -229,9 +229,9 @@ func (d *delivery) connect(ctx context.Context) error { for _, endp := range d.u.endpoints { var err error if d.u.lmtp { - _, err = conn.ConnectLMTP(ctx, endp, d.u.starttls, &d.u.tlsConfig) + _, err = conn.ConnectLMTP(ctx, endp, d.u.starttls, d.u.tlsConfig) } else { - _, err = conn.Connect(ctx, endp, d.u.starttls, &d.u.tlsConfig) + _, err = conn.Connect(ctx, endp, d.u.starttls, d.u.tlsConfig) } if err != nil { if len(d.u.endpoints) != 1 { diff --git a/internal/target/smtp/smtp_downstream_test.go b/internal/target/smtp/smtp_downstream_test.go index 31ef2954..f0f58c66 100644 --- a/internal/target/smtp/smtp_downstream_test.go +++ b/internal/target/smtp/smtp_downstream_test.go @@ -221,7 +221,7 @@ func TestDownstreamDelivery_StartTLS(t *testing.T) { Port: testPort, }, }, - tlsConfig: *clientCfg.Clone(), + tlsConfig: clientCfg.Clone(), starttls: true, log: testutils.Logger(t, "target.smtp"), } From 68e3065663620d389ecbf7e936733c3048fbe65b Mon Sep 17 00:00:00 2001 From: Mark Lipscombe Date: 2025年2月10日 08:48:08 -0500 Subject: [PATCH 112/171] fix: correctly announce SASL LOGIN capabilty --- internal/auth/sasl.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/auth/sasl.go b/internal/auth/sasl.go index 545c523e..8510052e 100644 --- a/internal/auth/sasl.go +++ b/internal/auth/sasl.go @@ -62,7 +62,7 @@ func (s *SASLAuth) SASLMechanisms() []string { if len(s.Plain) != 0 { mechs = append(mechs, sasl.Plain) - if s.OnlyFirstID { + if s.EnableLogin { mechs = append(mechs, sasl.Login) } } From 4a5f379832d8c659ac6f4749e6df0400603a1315 Mon Sep 17 00:00:00 2001 From: John Weldon Date: 2025年2月22日 13:47:14 -0700 Subject: [PATCH 113/171] framework/log: Fix Logger usages in tests --- internal/storage/blob/test_blob.go | 3 ++- internal/target/remote/dane_delivery_test.go | 3 ++- internal/target/remote/mxauth_test.go | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/internal/storage/blob/test_blob.go b/internal/storage/blob/test_blob.go index 9672efea..8f3d0bcb 100644 --- a/internal/storage/blob/test_blob.go +++ b/internal/storage/blob/test_blob.go @@ -39,10 +39,11 @@ func TestStore(t *testing.T, newStore func() module.BlobStore, cleanStore func(m prng := rand.New(randSrc) store := newStore() + l := testutils.Logger(t, "imapsql") b, err := imapsql.New("sqlite3", ":memory:", imapsql2.ExtBlobStore{Base: store}, imapsql.Opts{ PRNG: prng, - Log: testutils.Logger(t, "imapsql"), + Log: &l, }, ) if err != nil { diff --git a/internal/target/remote/dane_delivery_test.go b/internal/target/remote/dane_delivery_test.go index 2b921c7d..377f7cfc 100644 --- a/internal/target/remote/dane_delivery_test.go +++ b/internal/target/remote/dane_delivery_test.go @@ -32,7 +32,8 @@ import ( ) func targetWithExtResolver(t *testing.T, zones map[string]mockdns.Zone) (*mockdns.Server, *Target) { - dnsSrv, err := mockdns.NewServerWithLogger(zones, testutils.Logger(t, "mockdns"), false) + l := testutils.Logger(t, "mockdns") + dnsSrv, err := mockdns.NewServerWithLogger(zones, &l, false) if err != nil { t.Fatal(err) } diff --git a/internal/target/remote/mxauth_test.go b/internal/target/remote/mxauth_test.go index e332bdb4..978567c5 100644 --- a/internal/target/remote/mxauth_test.go +++ b/internal/target/remote/mxauth_test.go @@ -303,7 +303,8 @@ func TestRemoteDelivery_AuthMX_DNSSEC(t *testing.T) { }, } - dnsSrv, err := mockdns.NewServerWithLogger(zones, testutils.Logger(t, "mockdns"), false) + l := testutils.Logger(t, "mockdns") + dnsSrv, err := mockdns.NewServerWithLogger(zones, &l, false) if err != nil { t.Fatal(err) } @@ -342,7 +343,8 @@ func TestRemoteDelivery_AuthMX_DNSSEC_Fail(t *testing.T) { }, } - dnsSrv, err := mockdns.NewServerWithLogger(zones, testutils.Logger(t, "mockdns"), false) + l := testutils.Logger(t, "mockdns") + dnsSrv, err := mockdns.NewServerWithLogger(zones, &l, false) if err != nil { t.Fatal(err) } From 63420d1bef3a8902d8902b8a23c47bd96881f260 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Sun, 9 Mar 2025 15:39:22 +0300 Subject: [PATCH 114/171] target/smtp: Fix-up default value for tls_client --- internal/target/smtp/smtp_downstream.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/target/smtp/smtp_downstream.go b/internal/target/smtp/smtp_downstream.go index 294e283e..b85c4ba9 100644 --- a/internal/target/smtp/smtp_downstream.go +++ b/internal/target/smtp/smtp_downstream.go @@ -121,7 +121,7 @@ func (u *Downstream) Init(cfg *config.Map) error { return nil, nil }, saslAuthDirective, &u.saslFactory) cfg.Custom("tls_client", true, false, func() (interface{}, error) { - return tls.Config{}, nil + return &tls.Config{}, nil }, tls2.TLSClientBlock, &u.tlsConfig) cfg.Duration("connect_timeout", false, false, 5*time.Minute, &u.connectTimeout) cfg.Duration("command_timeout", false, false, 5*time.Minute, &u.commandTimeout) From 7cfee986906dac85144b7312913087ab4706ff72 Mon Sep 17 00:00:00 2001 From: spiarh Date: 2025年4月15日 15:25:03 +0200 Subject: [PATCH 115/171] fix: replace typo trasanactions with transactions Signed-off-by: spiarh --- internal/endpoint/smtp/metrics.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/endpoint/smtp/metrics.go b/internal/endpoint/smtp/metrics.go index 509241bc..8c9a7188 100644 --- a/internal/endpoint/smtp/metrics.go +++ b/internal/endpoint/smtp/metrics.go @@ -26,7 +26,7 @@ var ( Namespace: "maddy", Subsystem: "smtp", Name: "started_transactions", - Help: "Amount of SMTP trasanactions started", + Help: "Amount of SMTP transactions started", }, []string{"module"}, ) @@ -35,7 +35,7 @@ var ( Namespace: "maddy", Subsystem: "smtp", Name: "smtp_completed_transactions", - Help: "Amount of SMTP trasanactions successfully completed", + Help: "Amount of SMTP transactions successfully completed", }, []string{"module"}, ) @@ -44,7 +44,7 @@ var ( Namespace: "maddy", Subsystem: "smtp", Name: "aborted_transactions", - Help: "Amount of SMTP trasanactions aborted", + Help: "Amount of SMTP transactions aborted", }, []string{"module"}, ) From ad98777e45587062bf7f1e3fdb4e1546010f2b3c Mon Sep 17 00:00:00 2001 From: spiarh Date: 2025年4月15日 15:26:07 +0200 Subject: [PATCH 116/171] feat: implement maddy_queue_length metric The maddy_queue_length was already registered but never used. The gauge is updated when actions on disks are performed: - Incremented when a message is written to disk or when the queue is read from disk. - Decremented when a message is removed from disk. Signed-off-by: spiarh --- internal/target/queue/queue.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/internal/target/queue/queue.go b/internal/target/queue/queue.go index 264a70fb..4bea9053 100644 --- a/internal/target/queue/queue.go +++ b/internal/target/queue/queue.go @@ -89,7 +89,6 @@ import ( // partialError describes state of partially successful message delivery. type partialError struct { - // Underlying error objects for each recipient. Errs map[string]error @@ -129,7 +128,6 @@ type Queue struct { // Retry delay is calculated using the following formula: // initialRetryTime * retryTimeScale ^ (TriesCount - 1) - initialRetryTime time.Duration retryTimeScale float64 maxTries int @@ -635,6 +633,9 @@ func (q *Queue) removeFromDisk(msgMeta *module.MsgMetadata) { if err := os.Remove(metaPath); err != nil { dl.Error("failed to remove meta-data from disk", err) } + + queuedMsgs.WithLabelValues(q.name, q.location).Dec() + dl.Debugf("removed message from disk") } @@ -704,6 +705,8 @@ func (q *Queue) readDiskQueue() error { ID: id, }) loadedCount++ + + queuedMsgs.WithLabelValues(q.name, q.location).Inc() } if loadedCount != 0 { @@ -762,6 +765,8 @@ func (q *Queue) storeNewMessage(meta *QueueMetadata, header textproto.Header, bo return nil, err } + queuedMsgs.WithLabelValues(q.name, q.location).Inc() + return buffer.FileBuffer{Path: bodyPath, LenHint: body.Len()}, nil } From 14372d6aeb6b3babdb2a291af97e68cadf500edc Mon Sep 17 00:00:00 2001 From: Carl-Friedrich Braun Date: 2025年5月26日 11:52:43 +0200 Subject: [PATCH 117/171] fix: reduce StaleKeyLifetimeSec to 4 minutes RFC 5321 recommends idle time of 5 minutes. Maddy runs pool cleanup once per minute. When a connection is returned to the pool at time 'last cleanup + 10 seconds', it will be closed at 'last cleanup + StaleKeyLifetimeSec + 60 seconds' . With the original value of 5 minutes, the connection will be closed 50 seconds after the recommended timeout. By reducing the lifetime to 4 minutes, connections might be closed a little early, but never late. --- internal/target/remote/remote.go | 2 +- internal/target/remote/remote_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/target/remote/remote.go b/internal/target/remote/remote.go index d4c42ed6..3f848832 100644 --- a/internal/target/remote/remote.go +++ b/internal/target/remote/remote.go @@ -146,7 +146,7 @@ func (rt *Target) Init(cfg *config.Map) error { MaxKeys: 5000, MaxConnsPerKey: 5, // basically, max. amount of idle connections in cache MaxConnLifetimeSec: 150, // 2.5 mins, half of recommended idle time from RFC 5321 - StaleKeyLifetimeSec: 60 * 5, // should be bigger than MaxConnLifetimeSec + StaleKeyLifetimeSec: 60 * 4, // make sure that cleanup runs before recommended idle time from RFC 5321 } cfg.Int("conn_max_idle_count", false, false, 5, &poolCfg.MaxConnsPerKey) cfg.Int64("conn_max_idle_time", false, false, 150, &poolCfg.MaxConnLifetimeSec) diff --git a/internal/target/remote/remote_test.go b/internal/target/remote/remote_test.go index 4998e0c6..c21bb6eb 100644 --- a/internal/target/remote/remote_test.go +++ b/internal/target/remote/remote_test.go @@ -64,7 +64,7 @@ func testTarget(t *testing.T, zones map[string]mockdns.Zone, extResolver *dns.Ex MaxKeys: 5000, MaxConnsPerKey: 5, // basically, max. amount of idle connections in cache MaxConnLifetimeSec: 150, // 2.5 mins, half of recommended idle time from RFC 5321 - StaleKeyLifetimeSec: 60 * 5, // should be bigger than MaxConnLifetimeSec + StaleKeyLifetimeSec: 60 * 4, // make sure that cleanup runs before recommended idle time from RFC 5321 }), } From 0ecc909dd46c4f418dbde3079168674b8da631fa Mon Sep 17 00:00:00 2001 From: Ruiyang Guan Date: 2025年10月25日 11:14:24 +0000 Subject: [PATCH 118/171] Fix typo in TLS certificates section doc --- docs/tutorials/setting-up.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/setting-up.md b/docs/tutorials/setting-up.md index 2ec83519..240caa80 100644 --- a/docs/tutorials/setting-up.md +++ b/docs/tutorials/setting-up.md @@ -105,7 +105,7 @@ $(local_domains) = $(primary_domain) example.com other.example.com ## TLS certificates -One thing that can't be automagically configured is TLS certs. If you already +One thing that can't be automatically configured is TLS certs. If you already have them somewhere - use them, open /etc/maddy/maddy.conf and put the right paths in. You need to make sure maddy can read them while running as unprivileged user (maddy never runs as root, even during start-up), one way to From acedab39e008f62e081e7dd0377bd05cd40ea3ce Mon Sep 17 00:00:00 2001 From: hcl Date: Thu, 8 Jan 2026 11:40:58 +0800 Subject: [PATCH 119/171] Fix typo in SMTP endpoint section doc --- docs/reference/endpoints/smtp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reference/endpoints/smtp.md b/docs/reference/endpoints/smtp.md index 4dfa723a..16f7eea0 100644 --- a/docs/reference/endpoints/smtp.md +++ b/docs/reference/endpoints/smtp.md @@ -25,7 +25,7 @@ smtp tcp://0.0.0.0:25 { endpoint concurrency 500 } - # Example pipeline ocnfiguration. + # Example pipeline configuration. destination example.org { deliver_to &local_mailboxes } From c39c921e61f8a3eeb36ee845035252a63b9d950c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+copilot@users.noreply.github.com> Date: 2026年1月10日 12:48:27 +0000 Subject: [PATCH 120/171] Initial plan From b66f114f3c44dbb205799de8f33203e007690bcc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+copilot@users.noreply.github.com> Date: 2026年1月10日 12:53:57 +0000 Subject: [PATCH 121/171] Implement per-response-code DNSBL scoring with custom messages Co-authored-by: thisisjaymehta <31812582+thisisjaymehta@users.noreply.github.com> --- docs/reference/checks/dnsbl.md | 76 ++++++++ internal/check/dnsbl/common.go | 58 +++++- internal/check/dnsbl/dnsbl.go | 87 ++++++++- internal/check/dnsbl/dnsbl_test.go | 280 +++++++++++++++++++++++++++++ 4 files changed, 496 insertions(+), 5 deletions(-) diff --git a/docs/reference/checks/dnsbl.md b/docs/reference/checks/dnsbl.md index a2d27362..be31a67f 100644 --- a/docs/reference/checks/dnsbl.md +++ b/docs/reference/checks/dnsbl.md @@ -29,6 +29,30 @@ check.dnsbl { mailfrom yes score 1 } + + # Example with per-response-code scoring (new in 0.8) + zen.spamhaus.org { + client_ipv4 yes + client_ipv6 yes + + # SBL - Spamhaus Block List (known spam sources) + response 127.0.0.2 127.0.0.3 { + score 10 + message "Listed in Spamhaus SBL. See https://check.spamhaus.org/" + } + + # XBL - Exploits Block List (compromised hosts) + response 127.0.0.4 127.0.0.5 127.0.0.6 127.0.0.7 { + score 10 + message "Listed in Spamhaus XBL. See https://check.spamhaus.org/" + } + + # PBL - Policy Block List (dynamic IPs) + response 127.0.0.10 127.0.0.11 { + score 5 + message "Listed in Spamhaus PBL. See https://check.spamhaus.org/" + } + } } ``` @@ -171,3 +195,55 @@ will be rejected. It is possible to specify a negative value to make list act like a whitelist and override results of other blocklists. + +**Note:** When using `response` blocks (see below), the score from matching response +rules is used instead of this flat score value. + +--- + +### response _ip..._ + +**New in 0.8** + +Defines per-response-code rules for scoring and custom messages. This is useful +for combined DNSBLs like Spamhaus ZEN that return different codes for different +listing types. + +Each `response` block takes one or more IP addresses or CIDR ranges as arguments +and contains the following directives: + +#### score _integer_ +**Required** + +Score to add when this response code is returned. If multiple response codes +are returned by the DNSBL, scores are summed together. + +#### message _string_ +**Optional** + +Custom rejection or quarantine message to include when this response code +matches. This message is shown to the client or logged when the threshold +is reached. + +**Example:** + +``` +zen.spamhaus.org { + client_ipv4 yes + + # High severity - known spam sources + response 127.0.0.2 127.0.0.3 { + score 10 + message "Listed in Spamhaus SBL" + } + + # Lower severity - dynamic IPs + response 127.0.0.10 127.0.0.11 { + score 5 + message "Listed in Spamhaus PBL" + } +} +``` + +**Backwards compatibility:** When `response` blocks are not used, the legacy +`responses` and `score` directives work as before. diff --git a/internal/check/dnsbl/common.go b/internal/check/dnsbl/common.go index 7b874cb9..2f317097 100644 --- a/internal/check/dnsbl/common.go +++ b/internal/check/dnsbl/common.go @@ -32,9 +32,15 @@ type ListedErr struct { Identity string List string Reason string + Score int + Message string } func (le ListedErr) Fields() map[string]interface{} { + msg := "Client identity listed in the used DNSBL" + if le.Message != "" { + msg = le.Message + } return map[string]interface{}{ "check": "dnsbl", "list": le.List, @@ -42,7 +48,7 @@ func (le ListedErr) Fields() map[string]interface{} { "reason": le.Reason, "smtp_code": 554, "smtp_enchcode": exterrors.EnhancedCode{5, 7, 0}, - "smtp_msg": "Client identity listed in the used DNSBL", + "smtp_msg": msg, } } @@ -113,6 +119,56 @@ func checkIP(ctx context.Context, resolver dns.Resolver, cfg List, ip net.IP) er return err } + // If ResponseRules is configured, use new behavior + if len(cfg.ResponseRules)> 0 { + totalScore := 0 + var matchedMessages []string + var matchedReasons []string + + for _, addr := range addrs { + for _, rule := range cfg.ResponseRules { + for _, respNet := range rule.Networks { + if respNet.Contains(addr.IP) { + totalScore += rule.Score + if rule.Message != "" { + matchedMessages = append(matchedMessages, rule.Message) + } + matchedReasons = append(matchedReasons, addr.IP.String()) + break // Only match once per rule + } + } + } + } + + if totalScore == 0 { + return nil + } + + // Attempt to extract explanation string from TXT records + txts, err := resolver.LookupTXT(ctx, query) + var reason string + if err == nil && len(txts)> 0 { + reason = strings.Join(txts, "; ") + } else { + reason = strings.Join(matchedReasons, "; ") + } + + // Use first matched message if available + message := "" + if len(matchedMessages)> 0 { + message = matchedMessages[0] + } + + return ListedErr{ + Identity: ip.String(), + List: cfg.Zone, + Reason: reason, + Score: totalScore, + Message: message, + } + } + + // Legacy behavior: use flat Responses filter filteredAddrs := make([]net.IPAddr, 0, len(addrs)) addrsLoop: for _, addr := range addrs { diff --git a/internal/check/dnsbl/dnsbl.go b/internal/check/dnsbl/dnsbl.go index 2c91c838..fff87c37 100644 --- a/internal/check/dnsbl/dnsbl.go +++ b/internal/check/dnsbl/dnsbl.go @@ -38,6 +38,12 @@ import ( "golang.org/x/sync/errgroup" ) +type ResponseRule struct { + Networks []net.IPNet + Score int + Message string +} + type List struct { Zone string @@ -49,6 +55,8 @@ type List struct { ScoreAdj int Responses []net.IPNet + + ResponseRules []ResponseRule } var defaultBL = List{ @@ -126,7 +134,9 @@ func (bl *DNSBL) readListCfg(node config.Node) error { cfg.Bool("mailfrom", false, defaultBL.EHLO, &listCfg.MAILFROM) cfg.Int("score", false, false, 1, &listCfg.ScoreAdj) cfg.StringList("responses", false, false, []string{"127.0.0.1/24"}, &responseNets) - if _, err := cfg.Process(); err != nil { + cfg.AllowUnknown() + unknown, err := cfg.Process() + if err != nil { return err } @@ -144,6 +154,19 @@ func (bl *DNSBL) readListCfg(node config.Node) error { listCfg.Responses = append(listCfg.Responses, *ipNet) } + // Parse response blocks + for _, child := range unknown { + if child.Name == "response" { + rule, err := parseResponseRule(child) + if err != nil { + return err + } + listCfg.ResponseRules = append(listCfg.ResponseRules, rule) + } else { + return config.NodeErr(child, "unknown directive: %s", child.Name) + } + } + for _, zone := range append([]string{node.Name}, node.Args...) { zoneCfg := listCfg zoneCfg.Zone = zone @@ -173,6 +196,44 @@ func (bl *DNSBL) readListCfg(node config.Node) error { return nil } +func parseResponseRule(node config.Node) (ResponseRule, error) { + var rule ResponseRule + + if len(node.Args) == 0 { + return rule, config.NodeErr(node, "response block requires at least one IP address or CIDR as argument") + } + + // Parse IP addresses/CIDRs from arguments + for _, arg := range node.Args { + // If there is no / - it is a plain IP address, append '/32' or '/128' + resp := arg + if !strings.Contains(resp, "/") { + // Check if it's IPv6 to determine the mask + if strings.Contains(resp, ":") { + resp += "/128" + } else { + resp += "/32" + } + } + + _, ipNet, err := net.ParseCIDR(resp) + if err != nil { + return rule, config.NodeErr(node, "invalid IP address or CIDR: %s: %v", arg, err) + } + rule.Networks = append(rule.Networks, *ipNet) + } + + // Parse directives within the response block + cfg := config.NewMap(nil, node) + cfg.Int("score", false, true, 0, &rule.Score) + cfg.String("message", false, false, "", &rule.Message) + if _, err := cfg.Process(); err != nil { + return rule, err + } + + return rule, nil +} + func (bl *DNSBL) testList(listCfg List) { // Check RFC 5782 Section 5 requirements. @@ -298,6 +359,7 @@ func (bl *DNSBL) checkLists(ctx context.Context, ip net.IP, ehlo, mailFrom strin score int listedOn []string reasons []string + messages []string ) for _, list := range bl.bls { @@ -313,7 +375,18 @@ func (bl *DNSBL) checkLists(ctx context.Context, ip net.IP, ehlo, mailFrom strin defer lck.Unlock() listedOn = append(listedOn, listErr.List) reasons = append(reasons, listErr.Reason) - score += list.ScoreAdj + + // Use score from ListedErr if set (new behavior), otherwise use legacy ScoreAdj + if listErr.Score != 0 { + score += listErr.Score + } else { + score += list.ScoreAdj + } + + // Collect custom messages if available + if listErr.Message != "" { + messages = append(messages, listErr.Message) + } } return nil }) @@ -334,13 +407,19 @@ func (bl *DNSBL) checkLists(ctx context.Context, ip net.IP, ehlo, mailFrom strin } } + // Use custom message if available, otherwise use default + message := "Client identity is listed in the used DNSBL" + if len(messages)> 0 { + message = strings.Join(messages, "; ") + } + if score>= bl.rejectThres { return module.CheckResult{ Reject: true, Reason: &exterrors.SMTPError{ Code: 554, EnhancedCode: exterrors.EnhancedCode{5, 7, 0}, - Message: "Client identity is listed in the used DNSBL", + Message: message, Err: err, CheckName: "dnsbl", }, @@ -352,7 +431,7 @@ func (bl *DNSBL) checkLists(ctx context.Context, ip net.IP, ehlo, mailFrom strin Reason: &exterrors.SMTPError{ Code: 554, EnhancedCode: exterrors.EnhancedCode{5, 7, 0}, - Message: "Client identity is listed in the used DNSBL", + Message: message, Err: err, CheckName: "dnsbl", }, diff --git a/internal/check/dnsbl/dnsbl_test.go b/internal/check/dnsbl/dnsbl_test.go index 1845aeb7..4dce6c9e 100644 --- a/internal/check/dnsbl/dnsbl_test.go +++ b/internal/check/dnsbl/dnsbl_test.go @@ -211,3 +211,283 @@ func TestCheckLists(t *testing.T) { true, false, ) } + +func TestCheckIPWithResponseRules(t *testing.T) { + test := func(zones map[string]mockdns.Zone, cfg List, ip net.IP, expectedErr error) { + t.Helper() + resolver := mockdns.Resolver{Zones: zones} + err := checkIP(context.Background(), &resolver, cfg, ip) + if expectedErr == nil { + if err != nil { + t.Errorf("expected no error, got '%#v'", err) + } + } else { + if err == nil { + t.Errorf("expected err to be '%#v', got nil", expectedErr) + } else { + expectedLE, okExpected := expectedErr.(ListedErr) + actualLE, okActual := err.(ListedErr) + if !okExpected || !okActual { + t.Errorf("expected err to be '%#v', got '%#v'", expectedErr, err) + } else { + if expectedLE.Identity != actualLE.Identity || + expectedLE.List != actualLE.List || + expectedLE.Score != actualLE.Score || + expectedLE.Message != actualLE.Message { + t.Errorf("expected err to be '%#v', got '%#v'", expectedErr, err) + } + } + } + } + } + + // Test single response code with score and message + test(map[string]mockdns.Zone{ + "4.3.2.1.example.org.": { + A: []string{"127.0.0.2"}, + }, + }, List{ + Zone: "example.org", + ClientIPv4: true, + ResponseRules: []ResponseRule{ + { + Networks: []net.IPNet{ + {IP: net.IPv4(127, 0, 0, 2), Mask: net.IPv4Mask(255, 255, 255, 255)}, + }, + Score: 10, + Message: "Listed in SBL", + }, + }, + }, net.IPv4(1, 2, 3, 4), ListedErr{ + Identity: "1.2.3.4", + List: "example.org", + Score: 10, + Message: "Listed in SBL", + }) + + // Test multiple response codes with different scores - scores should sum + test(map[string]mockdns.Zone{ + "4.3.2.1.example.org.": { + A: []string{"127.0.0.2", "127.0.0.11"}, + }, + }, List{ + Zone: "example.org", + ClientIPv4: true, + ResponseRules: []ResponseRule{ + { + Networks: []net.IPNet{ + {IP: net.IPv4(127, 0, 0, 2), Mask: net.IPv4Mask(255, 255, 255, 255)}, + {IP: net.IPv4(127, 0, 0, 3), Mask: net.IPv4Mask(255, 255, 255, 255)}, + }, + Score: 10, + Message: "Listed in SBL", + }, + { + Networks: []net.IPNet{ + {IP: net.IPv4(127, 0, 0, 10), Mask: net.IPv4Mask(255, 255, 255, 255)}, + {IP: net.IPv4(127, 0, 0, 11), Mask: net.IPv4Mask(255, 255, 255, 255)}, + }, + Score: 5, + Message: "Listed in PBL", + }, + }, + }, net.IPv4(1, 2, 3, 4), ListedErr{ + Identity: "1.2.3.4", + List: "example.org", + Score: 15, // 10 + 5 + Message: "Listed in SBL", + }) + + // Test response code that doesn't match any rule - should return nil + test(map[string]mockdns.Zone{ + "4.3.2.1.example.org.": { + A: []string{"127.0.0.99"}, + }, + }, List{ + Zone: "example.org", + ClientIPv4: true, + ResponseRules: []ResponseRule{ + { + Networks: []net.IPNet{ + {IP: net.IPv4(127, 0, 0, 2), Mask: net.IPv4Mask(255, 255, 255, 255)}, + }, + Score: 10, + Message: "Listed in SBL", + }, + }, + }, net.IPv4(1, 2, 3, 4), nil) + + // Test low severity only - should get score 5 + test(map[string]mockdns.Zone{ + "4.3.2.1.example.org.": { + A: []string{"127.0.0.10"}, + }, + }, List{ + Zone: "example.org", + ClientIPv4: true, + ResponseRules: []ResponseRule{ + { + Networks: []net.IPNet{ + {IP: net.IPv4(127, 0, 0, 2), Mask: net.IPv4Mask(255, 255, 255, 255)}, + }, + Score: 10, + Message: "Listed in SBL", + }, + { + Networks: []net.IPNet{ + {IP: net.IPv4(127, 0, 0, 10), Mask: net.IPv4Mask(255, 255, 255, 255)}, + }, + Score: 5, + Message: "Listed in PBL", + }, + }, + }, net.IPv4(1, 2, 3, 4), ListedErr{ + Identity: "1.2.3.4", + List: "example.org", + Score: 5, + Message: "Listed in PBL", + }) + + // Test high severity - should get score 10 + test(map[string]mockdns.Zone{ + "4.3.2.1.example.org.": { + A: []string{"127.0.0.2"}, + }, + }, List{ + Zone: "example.org", + ClientIPv4: true, + ResponseRules: []ResponseRule{ + { + Networks: []net.IPNet{ + {IP: net.IPv4(127, 0, 0, 2), Mask: net.IPv4Mask(255, 255, 255, 255)}, + }, + Score: 10, + Message: "Listed in SBL", + }, + }, + }, net.IPv4(1, 2, 3, 4), ListedErr{ + Identity: "1.2.3.4", + List: "example.org", + Score: 10, + Message: "Listed in SBL", + }) +} + +func TestCheckListsWithResponseRules(t *testing.T) { + test := func(zones map[string]mockdns.Zone, bls []List, ip net.IP, ehlo, mailFrom string, reject, quarantine bool) { + mod := &DNSBL{ + bls: bls, + resolver: &mockdns.Resolver{Zones: zones}, + log: testutils.Logger(t, "dnsbl"), + quarantineThres: 5, + rejectThres: 10, + } + result := mod.checkLists(context.Background(), ip, ehlo, mailFrom) + + if result.Reject && !reject { + t.Errorf("Expected message to not be rejected") + } + if !result.Reject && reject { + t.Errorf("Expected message to be rejected") + } + if result.Quarantine && !quarantine { + t.Errorf("Expected message to not be quarantined") + } + if !result.Quarantine && quarantine { + t.Errorf("Expected message to be quarantined") + } + } + + // Test: Only low-severity code returned -> quarantine but not reject + test(map[string]mockdns.Zone{ + "4.3.2.1.zen.example.org.": { + A: []string{"127.0.0.11"}, + }, + }, []List{ + { + Zone: "zen.example.org", + ClientIPv4: true, + ResponseRules: []ResponseRule{ + { + Networks: []net.IPNet{ + {IP: net.IPv4(127, 0, 0, 2), Mask: net.IPv4Mask(255, 255, 255, 255)}, + }, + Score: 10, + Message: "Listed in SBL", + }, + { + Networks: []net.IPNet{ + {IP: net.IPv4(127, 0, 0, 10), Mask: net.IPv4Mask(255, 255, 255, 255)}, + {IP: net.IPv4(127, 0, 0, 11), Mask: net.IPv4Mask(255, 255, 255, 255)}, + }, + Score: 5, + Message: "Listed in PBL", + }, + }, + }, + }, net.IPv4(1, 2, 3, 4), "mx.example.com", "foo@example.com", false, true) + + // Test: High-severity code returned -> reject + test(map[string]mockdns.Zone{ + "4.3.2.1.zen.example.org.": { + A: []string{"127.0.0.2"}, + }, + }, []List{ + { + Zone: "zen.example.org", + ClientIPv4: true, + ResponseRules: []ResponseRule{ + { + Networks: []net.IPNet{ + {IP: net.IPv4(127, 0, 0, 2), Mask: net.IPv4Mask(255, 255, 255, 255)}, + }, + Score: 10, + Message: "Listed in SBL", + }, + }, + }, + }, net.IPv4(1, 2, 3, 4), "mx.example.com", "foo@example.com", true, false) + + // Test: Legacy configuration without response blocks -> existing behavior preserved + test(map[string]mockdns.Zone{ + "4.3.2.1.example.org.": { + A: []string{"127.0.0.1"}, + }, + }, []List{ + { + Zone: "example.org", + ClientIPv4: true, + ScoreAdj: 10, + }, + }, net.IPv4(1, 2, 3, 4), "mx.example.com", "foo@example.com", true, false) + + // Test: Mixed configuration (some lists with response blocks, some without) -> both work correctly + test(map[string]mockdns.Zone{ + "4.3.2.1.zen.example.org.": { + A: []string{"127.0.0.11"}, + }, + "4.3.2.1.legacy.example.org.": { + A: []string{"127.0.0.1"}, + }, + }, []List{ + { + Zone: "zen.example.org", + ClientIPv4: true, + ResponseRules: []ResponseRule{ + { + Networks: []net.IPNet{ + {IP: net.IPv4(127, 0, 0, 11), Mask: net.IPv4Mask(255, 255, 255, 255)}, + }, + Score: 5, + Message: "Listed in PBL", + }, + }, + }, + { + Zone: "legacy.example.org", + ClientIPv4: true, + ScoreAdj: 3, + }, + }, net.IPv4(1, 2, 3, 4), "mx.example.com", "foo@example.com", false, true) // 5 + 3 = 8, quarantine but not reject +} + From 3404ef66e6522249e9bb12528fcedcff62419e83 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+copilot@users.noreply.github.com> Date: 2026年1月11日 04:50:36 +0000 Subject: [PATCH 122/171] Fix config parsing to use node.Children instead of AllowUnknown Co-authored-by: thisisjaymehta <31812582+thisisjaymehta@users.noreply.github.com> --- internal/check/dnsbl/dnsbl.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/internal/check/dnsbl/dnsbl.go b/internal/check/dnsbl/dnsbl.go index fff87c37..37b5530a 100644 --- a/internal/check/dnsbl/dnsbl.go +++ b/internal/check/dnsbl/dnsbl.go @@ -134,9 +134,7 @@ func (bl *DNSBL) readListCfg(node config.Node) error { cfg.Bool("mailfrom", false, defaultBL.EHLO, &listCfg.MAILFROM) cfg.Int("score", false, false, 1, &listCfg.ScoreAdj) cfg.StringList("responses", false, false, []string{"127.0.0.1/24"}, &responseNets) - cfg.AllowUnknown() - unknown, err := cfg.Process() - if err != nil { + if _, err := cfg.Process(); err != nil { return err } @@ -154,8 +152,8 @@ func (bl *DNSBL) readListCfg(node config.Node) error { listCfg.Responses = append(listCfg.Responses, *ipNet) } - // Parse response blocks - for _, child := range unknown { + // Parse response blocks from node children + for _, child := range node.Children { if child.Name == "response" { rule, err := parseResponseRule(child) if err != nil { From fe91076f597d496ecceab067b88c6199acb35779 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+copilot@users.noreply.github.com> Date: 2026年1月11日 04:51:43 +0000 Subject: [PATCH 123/171] Fix rule matching to count each rule only once Co-authored-by: thisisjaymehta <31812582+thisisjaymehta@users.noreply.github.com> --- internal/check/dnsbl/common.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/internal/check/dnsbl/common.go b/internal/check/dnsbl/common.go index 2f317097..d11626d4 100644 --- a/internal/check/dnsbl/common.go +++ b/internal/check/dnsbl/common.go @@ -125,8 +125,16 @@ func checkIP(ctx context.Context, resolver dns.Resolver, cfg List, ip net.IP) er var matchedMessages []string var matchedReasons []string + // Track which rules have been matched to avoid counting the same rule multiple times + matchedRules := make(map[int]bool) + for _, addr := range addrs { - for _, rule := range cfg.ResponseRules { + for ruleIdx, rule := range cfg.ResponseRules { + // Skip if this rule has already been matched + if matchedRules[ruleIdx] { + continue + } + for _, respNet := range rule.Networks { if respNet.Contains(addr.IP) { totalScore += rule.Score @@ -134,7 +142,8 @@ func checkIP(ctx context.Context, resolver dns.Resolver, cfg List, ip net.IP) er matchedMessages = append(matchedMessages, rule.Message) } matchedReasons = append(matchedReasons, addr.IP.String()) - break // Only match once per rule + matchedRules[ruleIdx] = true + break // Move to next rule } } } From 11be6679520b23ec1fba38bf0ef37a39cec9e904 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+copilot@users.noreply.github.com> Date: 2026年1月11日 04:52:26 +0000 Subject: [PATCH 124/171] Improve documentation with clearer scoring behavior examples Co-authored-by: thisisjaymehta <31812582+thisisjaymehta@users.noreply.github.com> --- docs/reference/checks/dnsbl.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/reference/checks/dnsbl.md b/docs/reference/checks/dnsbl.md index be31a67f..5766de1a 100644 --- a/docs/reference/checks/dnsbl.md +++ b/docs/reference/checks/dnsbl.md @@ -216,7 +216,9 @@ and contains the following directives: **Required** Score to add when this response code is returned. If multiple response codes -are returned by the DNSBL, scores are summed together. +are returned by the DNSBL, and they match different rules, the scores from +all matched rules are summed together. Each rule is counted only once, even +if multiple returned IPs match networks within that rule. #### message _string_ **Optional** @@ -245,5 +247,11 @@ zen.spamhaus.org { } ``` +**Scoring behavior:** +- If DNSBL returns `127.0.0.2` only → Score: 10 (matches first rule) +- If DNSBL returns `127.0.0.11` only → Score: 5 (matches second rule) +- If DNSBL returns both `127.0.0.2` and `127.0.0.11` → Score: 15 (both rules match, scores sum) +- If DNSBL returns both `127.0.0.2` and `127.0.0.3` → Score: 10 (same rule matches, counted once) + **Backwards compatibility:** When `response` blocks are not used, the legacy `responses` and `score` directives work as before. From 66339ddefb8f13303af3377039f2d48c4cd84153 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+copilot@users.noreply.github.com> Date: 2026年1月11日 05:02:18 +0000 Subject: [PATCH 125/171] Address upstream PR feedback: use cfg.Callback, extract function, remove version annotation Co-authored-by: thisisjaymehta <31812582+thisisjaymehta@users.noreply.github.com> --- docs/reference/checks/dnsbl.md | 2 - internal/check/dnsbl/common.go | 147 +++++++++++++++------------------ internal/check/dnsbl/dnsbl.go | 22 ++--- 3 files changed, 79 insertions(+), 92 deletions(-) diff --git a/docs/reference/checks/dnsbl.md b/docs/reference/checks/dnsbl.md index 5766de1a..0dd62e57 100644 --- a/docs/reference/checks/dnsbl.md +++ b/docs/reference/checks/dnsbl.md @@ -203,8 +203,6 @@ rules is used instead of this flat score value. ### response _ip..._ -**New in 0.8** - Defines per-response-code rules for scoring and custom messages. This is useful for combined DNSBLs like Spamhaus ZEN that return different codes for different listing types. diff --git a/internal/check/dnsbl/common.go b/internal/check/dnsbl/common.go index d11626d4..7541d24f 100644 --- a/internal/check/dnsbl/common.go +++ b/internal/check/dnsbl/common.go @@ -94,6 +94,34 @@ func checkDomain(ctx context.Context, resolver dns.Resolver, cfg List, domain st } } +func matchResponseRules(addrs []net.IPAddr, rules []ResponseRule) (score int, messages []string, reasons []string, matched bool) { + // Track which rules have been matched to avoid counting the same rule multiple times + matchedRules := make(map[int]bool) + + for _, addr := range addrs { + for ruleIdx, rule := range rules { + // Skip if this rule has already been matched + if matchedRules[ruleIdx] { + continue + } + + for _, respNet := range rule.Networks { + if respNet.Contains(addr.IP) { + score += rule.Score + if rule.Message != "" { + messages = append(messages, rule.Message) + } + reasons = append(reasons, addr.IP.String()) + matchedRules[ruleIdx] = true + matched = true + break // Move to next rule + } + } + } + } + return +} + func checkIP(ctx context.Context, resolver dns.Resolver, cfg List, ip net.IP) error { ipv6 := true if ipv4 := ip.To4(); ipv4 != nil { @@ -119,111 +147,72 @@ func checkIP(ctx context.Context, resolver dns.Resolver, cfg List, ip net.IP) er return err } + var filteredAddrs []net.IPAddr + var score int + var customMessage string + // If ResponseRules is configured, use new behavior if len(cfg.ResponseRules)> 0 { - totalScore := 0 - var matchedMessages []string - var matchedReasons []string - - // Track which rules have been matched to avoid counting the same rule multiple times - matchedRules := make(map[int]bool) - - for _, addr := range addrs { - for ruleIdx, rule := range cfg.ResponseRules { - // Skip if this rule has already been matched - if matchedRules[ruleIdx] { - continue - } - - for _, respNet := range rule.Networks { - if respNet.Contains(addr.IP) { - totalScore += rule.Score - if rule.Message != "" { - matchedMessages = append(matchedMessages, rule.Message) - } - matchedReasons = append(matchedReasons, addr.IP.String()) - matchedRules[ruleIdx] = true - break // Move to next rule - } - } - } - } - - if totalScore == 0 { + matchedScore, matchedMessages, matchedReasons, matched := matchResponseRules(addrs, cfg.ResponseRules) + if !matched { return nil } - - // Attempt to extract explanation string from TXT records - txts, err := resolver.LookupTXT(ctx, query) - var reason string - if err == nil && len(txts)> 0 { - reason = strings.Join(txts, "; ") - } else { - reason = strings.Join(matchedReasons, "; ") - } - + score = matchedScore + // Use first matched message if available - message := "" if len(matchedMessages)> 0 { - message = matchedMessages[0] + customMessage = matchedMessages[0] } - - return ListedErr{ - Identity: ip.String(), - List: cfg.Zone, - Reason: reason, - Score: totalScore, - Message: message, + + // Build filteredAddrs from matched reasons for TXT lookup fallback + for _, reason := range matchedReasons { + filteredAddrs = append(filteredAddrs, net.IPAddr{IP: net.ParseIP(reason)}) } - } - - // Legacy behavior: use flat Responses filter - filteredAddrs := make([]net.IPAddr, 0, len(addrs)) -addrsLoop: - for _, addr := range addrs { - // No responses whitelist configured - permit all. - if len(cfg.Responses) == 0 { - filteredAddrs = append(filteredAddrs, addr) - continue - } - - for _, respNet := range cfg.Responses { - if respNet.Contains(addr.IP) { + } else { + // Legacy behavior: use flat Responses filter + filteredAddrs = make([]net.IPAddr, 0, len(addrs)) + addrsLoop: + for _, addr := range addrs { + // No responses whitelist configured - permit all. + if len(cfg.Responses) == 0 { filteredAddrs = append(filteredAddrs, addr) - continue addrsLoop + continue + } + + for _, respNet := range cfg.Responses { + if respNet.Contains(addr.IP) { + filteredAddrs = append(filteredAddrs, addr) + continue addrsLoop + } } } - } - if len(filteredAddrs) == 0 { - return nil + if len(filteredAddrs) == 0 { + return nil + } } - // Attempt to extract explanation string. + // Attempt to extract explanation string from TXT records (shared by both paths) txts, err := resolver.LookupTXT(ctx, query) - if err != nil || len(txts) == 0 { + var reason string + if err == nil && len(txts)> 0 { + reason = strings.Join(txts, "; ") + } else { // Not significant, include addresses as reason. Usually they are // mapped to some predefined 'reasons' by BL. - reasonParts := make([]string, 0, len(filteredAddrs)) for _, addr := range filteredAddrs { reasonParts = append(reasonParts, addr.IP.String()) } - - return ListedErr{ - Identity: ip.String(), - List: cfg.Zone, - Reason: strings.Join(reasonParts, "; "), - } + reason = strings.Join(reasonParts, "; ") } - // Some BLs provide multiple reasons (meta-BLs such as Spamhaus Zen) so - // don't mangle them by joining with "", instead join with "; ". - return ListedErr{ Identity: ip.String(), List: cfg.Zone, - Reason: strings.Join(txts, "; "), + Reason: reason, + Score: score, + Message: customMessage, } } diff --git a/internal/check/dnsbl/dnsbl.go b/internal/check/dnsbl/dnsbl.go index 37b5530a..94cbfe80 100644 --- a/internal/check/dnsbl/dnsbl.go +++ b/internal/check/dnsbl/dnsbl.go @@ -134,6 +134,14 @@ func (bl *DNSBL) readListCfg(node config.Node) error { cfg.Bool("mailfrom", false, defaultBL.EHLO, &listCfg.MAILFROM) cfg.Int("score", false, false, 1, &listCfg.ScoreAdj) cfg.StringList("responses", false, false, []string{"127.0.0.1/24"}, &responseNets) + cfg.Callback("response", func(_ *config.Map, node config.Node) error { + rule, err := parseResponseRule(node) + if err != nil { + return err + } + listCfg.ResponseRules = append(listCfg.ResponseRules, rule) + return nil + }) if _, err := cfg.Process(); err != nil { return err } @@ -152,17 +160,9 @@ func (bl *DNSBL) readListCfg(node config.Node) error { listCfg.Responses = append(listCfg.Responses, *ipNet) } - // Parse response blocks from node children - for _, child := range node.Children { - if child.Name == "response" { - rule, err := parseResponseRule(child) - if err != nil { - return err - } - listCfg.ResponseRules = append(listCfg.ResponseRules, rule) - } else { - return config.NodeErr(child, "unknown directive: %s", child.Name) - } + // Warn if both response and responses are configured + if len(listCfg.ResponseRules)> 0 && len(responseNets)> 0 { + bl.log.Msg("both 'response' blocks and 'responses' directive are specified, 'response' blocks take precedence", "list", node.Name) } for _, zone := range append([]string{node.Name}, node.Args...) { From c29208c95d75e813d4c3bab57a7cf3273a9437c2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+copilot@users.noreply.github.com> Date: 2026年1月11日 05:21:16 +0000 Subject: [PATCH 126/171] Add ResponseRules support for checkDomain (domain-based lookups) Co-authored-by: thisisjaymehta <31812582+thisisjaymehta@users.noreply.github.com> --- docs/reference/checks/dnsbl.md | 3 + internal/check/dnsbl/common.go | 53 +++++++++++---- internal/check/dnsbl/common_test.go | 101 ++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+), 12 deletions(-) diff --git a/docs/reference/checks/dnsbl.md b/docs/reference/checks/dnsbl.md index 0dd62e57..d7bb74cf 100644 --- a/docs/reference/checks/dnsbl.md +++ b/docs/reference/checks/dnsbl.md @@ -207,6 +207,9 @@ Defines per-response-code rules for scoring and custom messages. This is useful for combined DNSBLs like Spamhaus ZEN that return different codes for different listing types. +This works for both IP-based lookups (client_ipv4, client_ipv6) and domain-based +lookups (ehlo, mailfrom). + Each `response` block takes one or more IP addresses or CIDR ranges as arguments and contains the following directives: diff --git a/internal/check/dnsbl/common.go b/internal/check/dnsbl/common.go index 7541d24f..4e90e7e2 100644 --- a/internal/check/dnsbl/common.go +++ b/internal/check/dnsbl/common.go @@ -72,25 +72,54 @@ func checkDomain(ctx context.Context, resolver dns.Resolver, cfg List, domain st return nil } - // Attempt to extract explanation string. - txts, err := resolver.LookupTXT(context.Background(), query) - if err != nil || len(txts) == 0 { - // Not significant, include addresses as reason. Usually they are - // mapped to some predefined 'reasons' by BL. - return ListedErr{ - Identity: domain, - List: cfg.Zone, - Reason: strings.Join(addrs, "; "), + var score int + var customMessage string + var filteredAddrs []string + + // If ResponseRules is configured, use new behavior + if len(cfg.ResponseRules)> 0 { + // Convert string addresses to IPAddr for matching + ipAddrs := make([]net.IPAddr, 0, len(addrs)) + for _, addr := range addrs { + if ip := net.ParseIP(addr); ip != nil { + ipAddrs = append(ipAddrs, net.IPAddr{IP: ip}) + } } + + matchedScore, matchedMessages, matchedReasons, matched := matchResponseRules(ipAddrs, cfg.ResponseRules) + if !matched { + return nil + } + score = matchedScore + + // Use first matched message if available + if len(matchedMessages)> 0 { + customMessage = matchedMessages[0] + } + + filteredAddrs = matchedReasons + } else { + // Legacy behavior: accept all addresses + filteredAddrs = addrs } - // Some BLs provide multiple reasons (meta-BLs such as Spamhaus Zen) so - // don't mangle them by joining with "", instead join with "; ". + // Attempt to extract explanation string from TXT records (shared by both paths) + txts, err := resolver.LookupTXT(ctx, query) + var reason string + if err == nil && len(txts)> 0 { + reason = strings.Join(txts, "; ") + } else { + // Not significant, include addresses as reason. Usually they are + // mapped to some predefined 'reasons' by BL. + reason = strings.Join(filteredAddrs, "; ") + } return ListedErr{ Identity: domain, List: cfg.Zone, - Reason: strings.Join(txts, "; "), + Reason: reason, + Score: score, + Message: customMessage, } } diff --git a/internal/check/dnsbl/common_test.go b/internal/check/dnsbl/common_test.go index caa06575..8d2b24e3 100644 --- a/internal/check/dnsbl/common_test.go +++ b/internal/check/dnsbl/common_test.go @@ -236,3 +236,104 @@ func TestCheckIP(t *testing.T) { Reason: "127.0.0.1", }) } + +func TestCheckDomainWithResponseRules(t *testing.T) { + test := func(zones map[string]mockdns.Zone, cfg List, domain string, expectedErr error) { + t.Helper() + resolver := mockdns.Resolver{Zones: zones} + err := checkDomain(context.Background(), &resolver, cfg, domain) + if expectedErr == nil { + if err != nil { + t.Errorf("expected no error, got '%#v'", err) + } + } else { + if err == nil { + t.Errorf("expected err to be '%#v', got nil", expectedErr) + } else { + expectedLE, okExpected := expectedErr.(ListedErr) + actualLE, okActual := err.(ListedErr) + if !okExpected || !okActual { + t.Errorf("expected err to be '%#v', got '%#v'", expectedErr, err) + } else { + if expectedLE.Identity != actualLE.Identity || + expectedLE.List != actualLE.List || + expectedLE.Score != actualLE.Score || + expectedLE.Message != actualLE.Message { + t.Errorf("expected err to be '%#v', got '%#v'", expectedErr, err) + } + } + } + } + } + + // Test domain with single response code and custom message + test(map[string]mockdns.Zone{ + "spam.example.com.dnsbl.example.org.": { + A: []string{"127.0.0.2"}, + }, + }, List{ + Zone: "dnsbl.example.org", + ResponseRules: []ResponseRule{ + { + Networks: []net.IPNet{ + {IP: net.IPv4(127, 0, 0, 2), Mask: net.IPv4Mask(255, 255, 255, 255)}, + }, + Score: 10, + Message: "Domain listed as spam source", + }, + }, + }, "spam.example.com", ListedErr{ + Identity: "spam.example.com", + List: "dnsbl.example.org", + Score: 10, + Message: "Domain listed as spam source", + }) + + // Test domain with multiple response codes - scores should sum + test(map[string]mockdns.Zone{ + "multi.example.com.dnsbl.example.org.": { + A: []string{"127.0.0.2", "127.0.0.11"}, + }, + }, List{ + Zone: "dnsbl.example.org", + ResponseRules: []ResponseRule{ + { + Networks: []net.IPNet{ + {IP: net.IPv4(127, 0, 0, 2), Mask: net.IPv4Mask(255, 255, 255, 255)}, + }, + Score: 10, + Message: "High severity", + }, + { + Networks: []net.IPNet{ + {IP: net.IPv4(127, 0, 0, 11), Mask: net.IPv4Mask(255, 255, 255, 255)}, + }, + Score: 5, + Message: "Low severity", + }, + }, + }, "multi.example.com", ListedErr{ + Identity: "multi.example.com", + List: "dnsbl.example.org", + Score: 15, // 10 + 5 + Message: "High severity", + }) + + // Test domain with no matching response codes + test(map[string]mockdns.Zone{ + "unknown.example.com.dnsbl.example.org.": { + A: []string{"127.0.0.99"}, + }, + }, List{ + Zone: "dnsbl.example.org", + ResponseRules: []ResponseRule{ + { + Networks: []net.IPNet{ + {IP: net.IPv4(127, 0, 0, 2), Mask: net.IPv4Mask(255, 255, 255, 255)}, + }, + Score: 10, + Message: "Listed", + }, + }, + }, "unknown.example.com", nil) +} From f76fa74e4717494003b0a07b523a9ec031f238ff Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Sun, 9 Mar 2025 15:52:38 +0300 Subject: [PATCH 127/171] docs: Sync docs/index.md with main README See #759. --- docs/index.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/index.md b/docs/index.md index 901f7230..a9b7ee68 100644 --- a/docs/index.md +++ b/docs/index.md @@ -13,10 +13,11 @@ daemon with uniform configuration and minimal maintenance cost. feature-packed implementation you may want to use Dovecot instead. maddy still can handle message delivery business. -[![builds.sr.ht status](https://builds.sr.ht/~emersion/maddy.svg)](https://builds.sr.ht/~emersion/maddy?) -[![License text](https://img.shields.io/github/license/foxcpp/maddy)](https://github.com/foxcpp/maddy/blob/master/LICENSE) -[![Issues tracker](https://img.shields.io/github/issues/foxcpp/maddy)](https://github.com/foxcpp/maddy) +[![CI status](https://img.shields.io/github/actions/workflow/status/foxcpp/maddy/cicd.yml?style=flat-square)](https://github.com/foxcpp/maddy/actions/workflows/cicd.yml) +[![Issues tracker](https://img.shields.io/github/issues/foxcpp/maddy?style=flat-square)](https://github.com/foxcpp/maddy) * [Setup tutorial](https://maddy.email/tutorials/setting-up/) +* [Documentation](https://maddy.email/) + * [IRC channel](https://webchat.oftc.net/?channels=maddy&uio=MT11bmRlZmluZWQb1) * [Mailing list](https://lists.sr.ht/~foxcpp/maddy) From 15b398991eeb0d638538e5e278afa71a4a1ab235 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年1月14日 17:47:13 +0300 Subject: [PATCH 128/171] maddy 0.8.2 --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index 6f4eebdf..100435be 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.8.1 +0.8.2 From 0d68e634dcc77028839744d4720c6721726b8166 Mon Sep 17 00:00:00 2001 From: Jay Mehta Date: 2026年1月17日 18:06:35 +0530 Subject: [PATCH 129/171] Formatted files with goimports --- internal/check/dnsbl/common.go | 6 +++--- internal/check/dnsbl/dnsbl.go | 4 ++-- internal/check/dnsbl/dnsbl_test.go | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/internal/check/dnsbl/common.go b/internal/check/dnsbl/common.go index 4e90e7e2..ae43b23a 100644 --- a/internal/check/dnsbl/common.go +++ b/internal/check/dnsbl/common.go @@ -133,7 +133,7 @@ func matchResponseRules(addrs []net.IPAddr, rules []ResponseRule) (score int, me if matchedRules[ruleIdx] { continue } - + for _, respNet := range rule.Networks { if respNet.Contains(addr.IP) { score += rule.Score @@ -187,12 +187,12 @@ func checkIP(ctx context.Context, resolver dns.Resolver, cfg List, ip net.IP) er return nil } score = matchedScore - + // Use first matched message if available if len(matchedMessages)> 0 { customMessage = matchedMessages[0] } - + // Build filteredAddrs from matched reasons for TXT lookup fallback for _, reason := range matchedReasons { filteredAddrs = append(filteredAddrs, net.IPAddr{IP: net.ParseIP(reason)}) diff --git a/internal/check/dnsbl/dnsbl.go b/internal/check/dnsbl/dnsbl.go index 94cbfe80..800c6ed9 100644 --- a/internal/check/dnsbl/dnsbl.go +++ b/internal/check/dnsbl/dnsbl.go @@ -373,14 +373,14 @@ func (bl *DNSBL) checkLists(ctx context.Context, ip net.IP, ehlo, mailFrom strin defer lck.Unlock() listedOn = append(listedOn, listErr.List) reasons = append(reasons, listErr.Reason) - + // Use score from ListedErr if set (new behavior), otherwise use legacy ScoreAdj if listErr.Score != 0 { score += listErr.Score } else { score += list.ScoreAdj } - + // Collect custom messages if available if listErr.Message != "" { messages = append(messages, listErr.Message) diff --git a/internal/check/dnsbl/dnsbl_test.go b/internal/check/dnsbl/dnsbl_test.go index 4dce6c9e..d4cd7238 100644 --- a/internal/check/dnsbl/dnsbl_test.go +++ b/internal/check/dnsbl/dnsbl_test.go @@ -490,4 +490,3 @@ func TestCheckListsWithResponseRules(t *testing.T) { }, }, net.IPv4(1, 2, 3, 4), "mx.example.com", "foo@example.com", false, true) // 5 + 3 = 8, quarantine but not reject } - From b1dc3ffed23a473c5c80921b4b1675f3b17dc8c6 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年1月20日 23:03:26 +0300 Subject: [PATCH 130/171] maddy 0.9.0 --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index 100435be..ac39a106 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.8.2 +0.9.0 From 1e5e01e742ec60b72b6e8145c516ef9ed3dfbb66 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年1月20日 23:26:17 +0300 Subject: [PATCH 131/171] Fix-up dev merge --- internal/libdns/gcore.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/libdns/gcore.go b/internal/libdns/gcore.go index 01d7afb6..69d0da5e 100644 --- a/internal/libdns/gcore.go +++ b/internal/libdns/gcore.go @@ -1,5 +1,4 @@ //go:build libdns_gcore || !libdns_separate -// +build libdns_gcore !libdns_separate package libdns @@ -12,7 +11,7 @@ import ( ) func init() { - module.Register("libdns.gcore", func(modName, instName string, _, _ []string) (module.Module, error) { + module.Register("libdns.gcore", func(modName, instName string) (module.Module, error) { p := gcore.Provider{} return &ProviderModule{ RecordDeleter: &p, From b2804378c2575fe05cab5002731b0e6fc81f6519 Mon Sep 17 00:00:00 2001 From: Pierre Alexandre SCHEMBRI Date: 2026年2月11日 10:51:39 +0100 Subject: [PATCH 132/171] cli: start/stop storage modules in ctl commands Start LifetimeModule instances when running CLI subcommands that operate on storage/auth DBs, and stop them on Close(). This fixes imap-acct crashes due to uninitialized imapsql backends while preserving ManageableStorage support. --- internal/cli/ctl/moduleinit.go | 54 ++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/internal/cli/ctl/moduleinit.go b/internal/cli/ctl/moduleinit.go index 1129a516..778c3949 100644 --- a/internal/cli/ctl/moduleinit.go +++ b/internal/cli/ctl/moduleinit.go @@ -37,6 +37,36 @@ func closeIfNeeded(i interface{}) { } } +type managedStorage struct { + module.ManageableStorage + started bool +} + +func (m *managedStorage) Close() error { + if !m.started { + return nil + } + if lm, ok := m.ManageableStorage.(module.LifetimeModule); ok { + return lm.Stop() + } + return nil +} + +type managedUserDB struct { + module.PlainUserDB + started bool +} + +func (m *managedUserDB) Close() error { + if !m.started { + return nil + } + if lm, ok := m.PlainUserDB.(module.LifetimeModule); ok { + return lm.Stop() + } + return nil +} + func getCfgBlockModule(ctx *cli.Context) (*container.C, module.Module, error) { cfgPath := ctx.String("config") if cfgPath == "" { @@ -92,6 +122,14 @@ func openStorage(ctx *cli.Context) (module.Storage, error) { return nil, cli.Exit(fmt.Sprintf("Error: configuration block %s is not an IMAP storage", ctx.String("cfg-block")), 2) } + started := false + if lt, ok := storage.(module.LifetimeModule); ok { + if err := lt.Start(); err != nil { + return nil, err + } + started = true + } + if updStore, ok := mod.(updatepipe.Backend); ok { if err := updStore.EnableUpdatePipe(updatepipe.ModePush); err != nil && !errors.Is(err, os.ErrNotExist) { fmt.Fprintf(os.Stderr, "Failed to initialize update pipe, do not remove messages from mailboxes open by clients: %v\n", err) @@ -100,6 +138,11 @@ func openStorage(ctx *cli.Context) (module.Storage, error) { fmt.Fprintf(os.Stderr, "No update pipe support, do not remove messages from mailboxes open by clients\n") } + if started { + if ms, ok := storage.(module.ManageableStorage); ok { + return &managedStorage{ManageableStorage: ms, started: started}, nil + } + } return storage, nil } @@ -114,5 +157,16 @@ func openUserDB(ctx *cli.Context) (module.PlainUserDB, error) { return nil, cli.Exit(fmt.Sprintf("Error: configuration block %s is not a local credentials store", ctx.String("cfg-block")), 2) } + started := false + if lt, ok := userDB.(module.LifetimeModule); ok { + if err := lt.Start(); err != nil { + return nil, err + } + started = true + } + + if started { + return &managedUserDB{PlainUserDB: userDB, started: started}, nil + } return userDB, nil } From 7d94d7772fecfa198242e763b424644e78f295a2 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Tue, 3 Mar 2026 17:45:56 +0300 Subject: [PATCH 133/171] Linters clean-up, fix DNS mock leak in integration tests --- framework/module/lifetime.go | 4 +++- framework/resource/netresource/listen.go | 4 ++-- framework/resource/netresource/tracker.go | 16 +++++++++++---- go.mod | 3 +++ internal/cli/ctl/moduleinit.go | 10 ++++++---- internal/endpoint/imap/imap.go | 4 +++- internal/endpoint/openmetrics/om.go | 4 +++- internal/endpoint/smtp/smtp.go | 8 ++++++-- internal/endpoint/smtp/smtp_test.go | 5 ++++- internal/table/file_test.go | 9 ++++++--- internal/target/queue/queue_test.go | 5 +++-- internal/target/remote/mxauth_test.go | 13 +++++++++--- maddy.go | 24 +++++++++++++++++------ tests/t.go | 24 +++++++++++++---------- 14 files changed, 93 insertions(+), 40 deletions(-) diff --git a/framework/module/lifetime.go b/framework/module/lifetime.go index 5338b01f..c24f19f3 100644 --- a/framework/module/lifetime.go +++ b/framework/module/lifetime.go @@ -60,7 +60,9 @@ func (lt *LifetimeTracker) StartAll() error { } if err := entry.mod.Start(); err != nil { - lt.StopAll() + if err := lt.StopAll(); err != nil { + lt.logger.Error("StopAll failed after Start fail", err) + } return fmt.Errorf("failed to start module %v: %w", entry.mod.InstanceName(), err) } diff --git a/framework/resource/netresource/listen.go b/framework/resource/netresource/listen.go index 6d164fd7..23fea4a5 100644 --- a/framework/resource/netresource/listen.go +++ b/framework/resource/netresource/listen.go @@ -16,8 +16,8 @@ func CloseUnusedListeners() error { return tracker.CloseUnused() } -func CloseAllListeners() { - tracker.Close() +func CloseAllListeners() error { + return tracker.Close() } func ResetListenersUsage() { diff --git a/framework/resource/netresource/tracker.go b/framework/resource/netresource/tracker.go index a1827aa2..2099f7a7 100644 --- a/framework/resource/netresource/tracker.go +++ b/framework/resource/netresource/tracker.go @@ -67,14 +67,22 @@ func (lt *ListenerTracker) ResetUsage() { } func (lt *ListenerTracker) CloseUnused() error { - lt.tcp.CloseUnused(func(key string) bool { return true }) - lt.unix.CloseUnused(func(key string) bool { return true }) + if err := lt.tcp.CloseUnused(func(key string) bool { return true }); err != nil { + lt.logger.Error("CloseUnused for TCP failed", err) + } + if err := lt.unix.CloseUnused(func(key string) bool { return true }); err != nil { + lt.logger.Error("CloseUnused for Unix failed", err) + } return nil } func (lt *ListenerTracker) Close() error { - lt.tcp.Close() - lt.unix.Close() + if err := lt.tcp.Close(); err != nil { + lt.logger.Error("Close for TCP failed", err) + } + if err := lt.unix.Close(); err != nil { + lt.logger.Error("Close for Unix failed", err) + } return nil } diff --git a/go.mod b/go.mod index 668bf27b..cfa00afd 100644 --- a/go.mod +++ b/go.mod @@ -52,6 +52,7 @@ require ( github.com/minio/minio-go/v7 v7.0.84 github.com/netauth/netauth v0.6.2 github.com/prometheus/client_golang v1.20.5 + github.com/stretchr/testify v1.10.0 github.com/urfave/cli/v2 v2.27.5 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.32.0 @@ -87,6 +88,7 @@ require ( github.com/caddyserver/zerossl v0.1.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/digitalocean/godo v1.134.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/color v1.18.0 // indirect @@ -122,6 +124,7 @@ require ( github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pierrec/lz4 v2.6.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect diff --git a/internal/cli/ctl/moduleinit.go b/internal/cli/ctl/moduleinit.go index 778c3949..5519d0e3 100644 --- a/internal/cli/ctl/moduleinit.go +++ b/internal/cli/ctl/moduleinit.go @@ -21,19 +21,21 @@ package ctl import ( "errors" "fmt" - "io" "os" "github.com/foxcpp/maddy" "github.com/foxcpp/maddy/framework/container" + "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/updatepipe" "github.com/urfave/cli/v2" ) -func closeIfNeeded(i interface{}) { - if c, ok := i.(io.Closer); ok { - c.Close() +func closeIfNeeded(i any) { + if c, ok := i.(module.LifetimeModule); ok { + if err := c.Stop(); err != nil { + log.DefaultLogger.Error("failed to stop module", err) + } } } diff --git a/internal/endpoint/imap/imap.go b/internal/endpoint/imap/imap.go index cf345514..b73908d8 100644 --- a/internal/endpoint/imap/imap.go +++ b/internal/endpoint/imap/imap.go @@ -167,7 +167,9 @@ func (endp *Endpoint) Start() error { } if err := endp.setupListeners(endp.endpoints); err != nil { - endp.Stop() + if err := endp.Stop(); err != nil { + endp.Log.Error("failed to stop after setupListeners error", err) + } return err } return nil diff --git a/internal/endpoint/openmetrics/om.go b/internal/endpoint/openmetrics/om.go index 44ac64db..0cff5c1e 100644 --- a/internal/endpoint/openmetrics/om.go +++ b/internal/endpoint/openmetrics/om.go @@ -85,7 +85,9 @@ func (e *Endpoint) Start() error { for _, endp := range e.endpoints { l, err := netresource.Listen(endp.Network(), endp.Address()) if err != nil { - e.Stop() + if err := e.Stop(); err != nil { + + } return fmt.Errorf("%s: %v", modName, err) } diff --git a/internal/endpoint/smtp/smtp.go b/internal/endpoint/smtp/smtp.go index 5f0d5210..eeb29922 100644 --- a/internal/endpoint/smtp/smtp.go +++ b/internal/endpoint/smtp/smtp.go @@ -318,7 +318,9 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { func (endp *Endpoint) Start() error { if err := endp.setupListeners(endp.endpoints); err != nil { - endp.Stop() + if err := endp.Stop(); err != nil { + endp.Log.Error("failed to Stop after setupListeners fail", err) + } return err } return nil @@ -426,7 +428,9 @@ func (endp *Endpoint) Stop() error { ctx, cancel := context.WithTimeout(context.Background(), endp.shutdownTimeout) defer cancel() - endp.serv.Shutdown(ctx) + if err := endp.serv.Shutdown(ctx); err != nil { + return err + } endp.listenersWg.Wait() diff --git a/internal/endpoint/smtp/smtp_test.go b/internal/endpoint/smtp/smtp_test.go index d6de22dc..b7d51320 100644 --- a/internal/endpoint/smtp/smtp_test.go +++ b/internal/endpoint/smtp/smtp_test.go @@ -37,6 +37,7 @@ import ( "github.com/foxcpp/maddy/internal/auth" "github.com/foxcpp/maddy/internal/msgpipeline" "github.com/foxcpp/maddy/internal/testutils" + "github.com/stretchr/testify/assert" ) var testPort string @@ -146,7 +147,9 @@ func submitMsgOpts(t *testing.T, cl *smtp.Client, from string, rcpts []string, o func TestSMTPDelivery(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { diff --git a/internal/table/file_test.go b/internal/table/file_test.go index 5c7389e3..ae8466fa 100644 --- a/internal/table/file_test.go +++ b/internal/table/file_test.go @@ -26,6 +26,7 @@ import ( "github.com/foxcpp/maddy/framework/config" "github.com/foxcpp/maddy/internal/testutils" + "github.com/stretchr/testify/assert" ) func TestReadFile(t *testing.T) { @@ -107,7 +108,9 @@ func TestFileReload(t *testing.T) { t.Fatal(err) } m.log = testutils.Logger(t, "file_map") - defer m.Stop() + defer func() { + assert.NoError(t, m.Stop()) + }() if err := mod.Configure([]string{f.Name()}, &config.Map{Block: config.Node{}}); err != nil { t.Fatal(err) @@ -147,10 +150,10 @@ func TestFileReload_Broken(t *testing.T) { } defer os.Remove(f.Name()) if _, err := f.WriteString(file); err != nil { - f.Close() + assert.NoError(t, f.Close()) t.Fatal(err) } - f.Close() + assert.NoError(t, f.Close()) mod, err := NewFile("", "") if err != nil { diff --git a/internal/target/queue/queue_test.go b/internal/target/queue/queue_test.go index 205ba891..649fe428 100644 --- a/internal/target/queue/queue_test.go +++ b/internal/target/queue/queue_test.go @@ -39,6 +39,7 @@ import ( "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/testutils" + "github.com/stretchr/testify/assert" ) // newTestQueue returns properly initialized Queue object usable for testing. @@ -269,7 +270,7 @@ func TestQueueDelivery_PermanentFail_NonPartial(t *testing.T) { // Queue will abort a delivery if it fails for all recipients. readMsgChanTimeout(t, dt.aborted, 5*time.Second) - q.Stop() + assert.NoError(t, q.Stop()) // Delivery is failed permanently, hence no retry should be rescheduled. checkQueueDir(t, q, []string{}) @@ -296,7 +297,7 @@ func TestQueueDelivery_PermanentFail_Partial(t *testing.T) { // Here delivery fails for recipients too, but this is reported using PartialDelivery. readMsgChanTimeout(t, dt.aborted, 5*time.Second) - q.Stop() + assert.NoError(t, q.Stop()) checkQueueDir(t, q, []string{}) } diff --git a/internal/target/remote/mxauth_test.go b/internal/target/remote/mxauth_test.go index 978567c5..e7d97f5d 100644 --- a/internal/target/remote/mxauth_test.go +++ b/internal/target/remote/mxauth_test.go @@ -32,6 +32,7 @@ import ( "github.com/foxcpp/maddy/framework/dns" "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/testutils" + "github.com/stretchr/testify/assert" ) func TestRemoteDelivery_AuthMX_MTASTS(t *testing.T) { @@ -63,7 +64,9 @@ func TestRemoteDelivery_AuthMX_MTASTS(t *testing.T) { testSTSPolicy(t, zones, mtastsGet), }) tgt.tlsConfig = clientCfg - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -108,7 +111,9 @@ func TestRemoteDelivery_MTASTS_SkipNonMatching(t *testing.T) { &localPolicy{minMXLevel: module.MX_MTASTS}, }) tgt.tlsConfig = clientCfg - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -148,7 +153,9 @@ func TestRemoteDelivery_AuthMX_MTASTS_Fail(t *testing.T) { &localPolicy{minMXLevel: module.MX_MTASTS}, }) tgt.tlsConfig = clientCfg - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { diff --git a/maddy.go b/maddy.go index cabd6c8e..4f1c642d 100644 --- a/maddy.go +++ b/maddy.go @@ -207,7 +207,11 @@ func Run(c *cli.Context) error { defer log.DefaultLogger.Out.Close() defer hooks.RunHooks(hooks.EventShutdown) - hooks.AddHook(hooks.EventShutdown, netresource.CloseAllListeners) + defer func() { + if err := netresource.CloseAllListeners(); err != nil { + log.DefaultLogger.Error("CloseAllListeners failed", err) + } + }() if err := moduleMain(c.Path("config")); err != nil { systemdStatusErr(err) @@ -373,8 +377,8 @@ func moduleStart(c *container.C) error { return c.Lifetime.StartAll() } -func moduleStop(c *container.C) { - c.Lifetime.StopAll() +func moduleStop(c *container.C) error { + return c.Lifetime.StopAll() } func moduleMain(configPath string) error { @@ -412,7 +416,9 @@ func moduleMain(configPath string) error { asyncStopWg.Wait() systemdStatus(SDStopping, "Waiting for current configuration to stop...") - moduleStop(c) + if err := moduleStop(c); err != nil { + c.DefaultLogger.Msg("moduleStop failed", err) + } c.DefaultLogger.Msg("server stopped") return nil @@ -446,10 +452,16 @@ func moduleReload(oldContainer *container.C, configPath string, asyncStopWg *syn asyncStopWg.Add(1) go func() { defer asyncStopWg.Done() - defer netresource.CloseUnusedListeners() + defer func() { + if err := netresource.CloseUnusedListeners(); err != nil { + oldContainer.DefaultLogger.Error("CloseUnusedListeners failed", err) + } + }() oldContainer.DefaultLogger.Msg("stopping old server") - moduleStop(oldContainer) + if err := moduleStop(oldContainer); err != nil { + oldContainer.DefaultLogger.Error("moduleStop failed", err) + } oldContainer.DefaultLogger.Msg("old server stopped") systemdStatus(SDReloading, "Configuration running.") diff --git a/tests/t.go b/tests/t.go index c1c04bc3..81423610 100644 --- a/tests/t.go +++ b/tests/t.go @@ -101,12 +101,25 @@ func (t *T) DNS(zones map[string]mockdns.Zone) { t.dnsServ.Close() } - dnsServ, err := mockdns.NewServer(zones, false) + dnsServ, err := mockdns.NewServerWithLogger(zones, t, false) if err != nil { t.Fatal("Test configuration failed:", err) } dnsServ.Log = t t.dnsServ = dnsServ + + t.Cleanup(func() { + if t.dnsServ == nil { + return + } + + // Shutdown the DNS server after maddy to make sure it will not spend time + // timing out queries. + if err := t.dnsServ.Close(); err != nil { + t.Log("Unable to stop the DNS server:", err) + } + t.dnsServ = nil + }) } // Port allocates the random TCP port for use by test. It will made accessible @@ -141,15 +154,6 @@ func (t *T) ensureCanRun() { // any DNS queries to the real world. t.Log("NOTE: Explicit DNS(nil) is recommended.") t.DNS(nil) - - t.Cleanup(func() { - // Shutdown the DNS server after maddy to make sure it will not spend time - // timing out queries. - if err := t.dnsServ.Close(); err != nil { - t.Log("Unable to stop the DNS server:", err) - } - t.dnsServ = nil - }) } // Setup file system, create statedir, runtimedir, write out config. From d7551cf7ec8ef4f59c3e42a4860146e7fbbfd3a6 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Tue, 3 Mar 2026 18:25:57 +0300 Subject: [PATCH 134/171] auth/dovecot_sasl: Update go-dovecot-sasl for Dovecot 2.4 compatibility Fixes #808 --- go.mod | 2 +- go.sum | 2 + internal/auth/sasl.go | 19 ++++++- internal/endpoint/smtp/session.go | 14 +---- internal/endpoint/smtp/smtp.go | 18 ++++++ tests/dovecot_sasl_test.go | 91 +++++++++++++++++++++++++++---- 6 files changed, 119 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index cfa00afd..242d0e6d 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/emersion/go-msgauth v0.6.8 github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 github.com/emersion/go-smtp v0.21.3 - github.com/foxcpp/go-dovecot-sasl v0.0.0-20200522223722-c4699d7a24bf + github.com/foxcpp/go-dovecot-sasl v0.0.0-20260303144336-f7632c6ec0ba github.com/foxcpp/go-imap-backend-tests v0.0.0-20220105184719-e80aa29a5e16 github.com/foxcpp/go-imap-i18nlevel v0.0.0-20200208001533-d6ec88553005 github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613 diff --git a/go.sum b/go.sum index 4d3eaea7..81c986e0 100644 --- a/go.sum +++ b/go.sum @@ -306,6 +306,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/foxcpp/go-dovecot-sasl v0.0.0-20200522223722-c4699d7a24bf h1:rmBPY5fryjp9zLQYsUmQqqgsYq7qeVfrjtr96Tf9vD8= github.com/foxcpp/go-dovecot-sasl v0.0.0-20200522223722-c4699d7a24bf/go.mod h1:5yZUmwr851vgjyAfN7OEfnrmKOh/qLA5dbGelXYsu1E= +github.com/foxcpp/go-dovecot-sasl v0.0.0-20260303144336-f7632c6ec0ba h1:yxQhqX9RQCvECZKBtqwCZoKy/6CLaozDZeWH9Lvndy0= +github.com/foxcpp/go-dovecot-sasl v0.0.0-20260303144336-f7632c6ec0ba/go.mod h1:5yZUmwr851vgjyAfN7OEfnrmKOh/qLA5dbGelXYsu1E= github.com/foxcpp/go-imap v1.0.0-beta.1.0.20220623182312-df940c324887 h1:qUoaaHyrRpQw85ru6VQcC6JowdhrWl7lSbI1zRX1FTM= github.com/foxcpp/go-imap v1.0.0-beta.1.0.20220623182312-df940c324887/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY= github.com/foxcpp/go-imap-backend-tests v0.0.0-20220105184719-e80aa29a5e16 h1:qheFPDpteiUy7Ym18R68OYenpk85UyKYGkhYTmddSBg= diff --git a/internal/auth/sasl.go b/internal/auth/sasl.go index 8510052e..b29959b0 100644 --- a/internal/auth/sasl.go +++ b/internal/auth/sasl.go @@ -54,6 +54,8 @@ type SASLAuth struct { AuthMap module.Table AuthNormalize authz.NormalizeFunc + ErrorMap func(err error) error + Plain []module.PlainAuth } @@ -132,7 +134,10 @@ type ContextData struct { } // CreateSASL creates the sasl.Server instance for the corresponding mechanism. -func (s *SASLAuth) CreateSASL(mech string, remoteAddr net.Addr, successCb func(identity string, data ContextData) error) sasl.Server { +func (s *SASLAuth) CreateSASL( + mech string, remoteAddr net.Addr, + successCb func(identity string, data ContextData) error, +) sasl.Server { switch mech { case sasl.Plain: return sasl.NewPlainServer(func(identity, username, password string) error { @@ -140,12 +145,18 @@ func (s *SASLAuth) CreateSASL(mech string, remoteAddr net.Addr, successCb func(i identity = username } if identity != username { + if s.ErrorMap != nil { + return s.ErrorMap(ErrInvalidAuthCred) + } return ErrInvalidAuthCred } err := s.AuthPlain(username, password) if err != nil { s.Log.Error("authentication failed", err, "username", username, "src_ip", remoteAddr) + if s.ErrorMap != nil { + return s.ErrorMap(ErrInvalidAuthCred) + } return ErrInvalidAuthCred } @@ -162,12 +173,18 @@ func (s *SASLAuth) CreateSASL(mech string, remoteAddr net.Addr, successCb func(i return sasllogin.NewLoginServer(func(username, password string) error { username, err := s.usernameForAuth(context.Background(), username) if err != nil { + if s.ErrorMap != nil { + return s.ErrorMap(ErrInvalidAuthCred) + } return err } err = s.AuthPlain(username, password) if err != nil { s.Log.Error("authentication failed", err, "username", username, "src_ip", remoteAddr) + if s.ErrorMap != nil { + return s.ErrorMap(ErrInvalidAuthCred) + } return ErrInvalidAuthCred } diff --git a/internal/endpoint/smtp/session.go b/internal/endpoint/smtp/session.go index 89cf54fc..01e36e73 100644 --- a/internal/endpoint/smtp/session.go +++ b/internal/endpoint/smtp/session.go @@ -171,19 +171,7 @@ func (s *Session) AuthPlain(username, password string) error { failedLogins.WithLabelValues(s.endp.name).Inc() - if exterrors.IsTemporary(err) { - return &smtp.SMTPError{ - Code: 454, - EnhancedCode: smtp.EnhancedCode{4, 7, 0}, - Message: "Temporary authentication failure", - } - } - - return &smtp.SMTPError{ - Code: 535, - EnhancedCode: smtp.EnhancedCode{5, 7, 8}, - Message: "Invalid credentials", - } + return s.endp.authErrorMap(err) } s.connState.AuthUser = username diff --git a/internal/endpoint/smtp/smtp.go b/internal/endpoint/smtp/smtp.go index eeb29922..cabde482 100644 --- a/internal/endpoint/smtp/smtp.go +++ b/internal/endpoint/smtp/smtp.go @@ -38,6 +38,7 @@ import ( modconfig "github.com/foxcpp/maddy/framework/config/module" tls2 "github.com/foxcpp/maddy/framework/config/tls" "github.com/foxcpp/maddy/framework/dns" + "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/future" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" @@ -285,6 +286,7 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { } endp.saslAuth.Log.Debug = endp.Log.Debug + endp.saslAuth.ErrorMap = endp.authErrorMap // INTERNATIONALIZATION: See RFC 6531 Section 3.3. endp.serv.Domain, err = idna.ToASCII(hostname) @@ -326,6 +328,22 @@ func (endp *Endpoint) Start() error { return nil } +func (endp *Endpoint) authErrorMap(err error) error { + if exterrors.IsTemporary(err) { + return &smtp.SMTPError{ + Code: 454, + EnhancedCode: smtp.EnhancedCode{4, 7, 0}, + Message: "Temporary authentication failure", + } + } + + return &smtp.SMTPError{ + Code: 535, + EnhancedCode: smtp.EnhancedCode{5, 7, 8}, + Message: "Invalid credentials", + } +} + func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { for _, addr := range addresses { var l net.Listener diff --git a/tests/dovecot_sasl_test.go b/tests/dovecot_sasl_test.go index af424e81..140b1820 100644 --- a/tests/dovecot_sasl_test.go +++ b/tests/dovecot_sasl_test.go @@ -1,10 +1,8 @@ //go:build integration && (darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris) -// +build integration -// +build darwin dragonfly freebsd linux netbsd openbsd solaris /* Maddy Mail Server - Composable all-in-one email server. -Copyright © 2019-2020 Max Mazurov , Maddy Mail Server contributors +Copyright © 2019-2026 Max Mazurov , Maddy Mail Server contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,9 +24,9 @@ package tests_test import ( "bufio" + "bytes" "errors" "flag" - "io/ioutil" "os" "os/exec" "os/user" @@ -47,7 +45,8 @@ func init() { flag.StringVar(&DovecotExecutable, "integration.dovecot", "dovecot", "path to dovecot executable for interop tests") } -const dovecotConf = `base_dir = $ROOT/run/ +const dovecotConf = ` +base_dir = $ROOT/run/ state_dir = $ROOT/lib/ log_path = /dev/stderr ssl = no @@ -56,12 +55,14 @@ default_internal_user = $USER default_internal_group = $GROUP default_login_user = $USER +auth_failure_delay = 0 + passdb { driver = passwd-file args = $ROOT/passwd } -userdb { +userdb file { driver = passwd-file args = $ROOT/passwd } @@ -78,7 +79,7 @@ protocols = imap service imap-login { chroot = inet_listener imap { - address = 127.0.0.1 + listen = 127.0.0.1 port = 0 } } @@ -95,8 +96,64 @@ auth_verbose_passwords = yes mail_debug = yes ` +const dovecotConf24 = `dovecot_config_version = 2.4.0 +dovecot_storage_version = 2.4.0 + +base_dir = $ROOT/run/ +state_dir = $ROOT/lib/ +mail_plugin_dir = $ROOT/lib/ +login_plugin_dir = $ROOT/lib/ +log_path = /dev/stderr +ssl = no + +default_internal_user = $USER +default_internal_group = $GROUP +default_login_user = $USER + +auth_failure_delay = 0 + +passdb file { + driver = passwd-file + passwd_file_path = $ROOT/passwd +} + +userdb file { + driver = passwd-file + passwd_file_path = $ROOT/passwd +} + +service auth { + unix_listener auth { + mode = 0666 + } +} + +# Turn on debugging information, to help troubleshooting issues. +auth_verbose = yes +auth_debug = yes +auth_debug_passwords = yes +auth_verbose_passwords = yes +mail_debug = yes +` + const dovecotPasswd = `tester:{plain}123456:1000:1000::/home/user` +func isDovecot24(t *testing.T, dovecotExec string) bool { + cmd := exec.Command(dovecotExec, "--version") + var stdout bytes.Buffer + cmd.Stdout = &stdout + if err := cmd.Run(); err != nil { + t.Fatal(err) + } + + version, _, _ := strings.Cut(stdout.String(), "-") + t.Log("Dovecot version:", stdout.String()) + + parts := strings.SplitN(version, ".", 3) + + return len(parts)>= 2 && parts[0] == "2" && parts[1]>= "4" +} + func runDovecot(t *testing.T) (string, *exec.Cmd) { dovecotExec, err := exec.LookPath(DovecotExecutable) if err != nil { @@ -117,15 +174,20 @@ func runDovecot(t *testing.T) (string, *exec.Cmd) { t.Fatal(err) } + dovecotConfTemplate := dovecotConf + if isDovecot24(t, dovecotExec) { + dovecotConfTemplate = dovecotConf24 + } + dovecotConf := strings.NewReplacer( "$ROOT", tempDir, "$USER", curUser.Username, - "$GROUP", curGroup.Name).Replace(dovecotConf) - err = ioutil.WriteFile(filepath.Join(tempDir, "dovecot.conf"), []byte(dovecotConf), os.ModePerm) + "$GROUP", curGroup.Name).Replace(dovecotConfTemplate) + err = os.WriteFile(filepath.Join(tempDir, "dovecot.conf"), []byte(dovecotConf), os.ModePerm) if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(filepath.Join(tempDir, "passwd"), []byte(dovecotPasswd), os.ModePerm) + err = os.WriteFile(filepath.Join(tempDir, "passwd"), []byte(dovecotPasswd), os.ModePerm) if err != nil { t.Fatal(err) } @@ -147,9 +209,14 @@ func runDovecot(t *testing.T) (string, *exec.Cmd) { for scnr.Scan() { line := scnr.Text() - // One of messages printed near completing initialization. + // One of messages printed near completing initialization (Dovecot 2.3 or older) if strings.Contains(line, "starting up for imap") { - time.Sleep(500*time.Millisecond) + time.Sleep(500 * time.Millisecond) + ready <- struct{}{} + } + // Dovecot 2.4+ + if strings.Contains(line, "starting up without any protocols") { + time.Sleep(500 * time.Millisecond) ready <- struct{}{} } From 813964dfe9191d863edfd3c0530e0f2fb93b703d Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Sun, 8 Mar 2026 22:37:36 +0300 Subject: [PATCH 135/171] sqlite: Generalize sqlite driver selection for use in sql_query/sql_table Fixes #724, #819. --- internal/{table/sqlite3.go => sqlite/is.go} | 11 +++++------ .../imapsql => sqlite}/modernc_sqlite3.go | 17 +++++++++++++---- .../{storage/imapsql => sqlite}/no_sqlite3.go | 14 ++++++++++---- internal/{storage/imapsql => sqlite}/sqlite3.go | 17 +++++++++++++---- internal/storage/imapsql/imapsql.go | 15 ++++++++------- internal/table/sql_query.go | 5 ++++- 6 files changed, 53 insertions(+), 26 deletions(-) rename internal/{table/sqlite3.go => sqlite/is.go} (80%) rename internal/{storage/imapsql => sqlite}/modernc_sqlite3.go (76%) rename internal/{storage/imapsql => sqlite}/no_sqlite3.go (79%) rename internal/{storage/imapsql => sqlite}/sqlite3.go (76%) diff --git a/internal/table/sqlite3.go b/internal/sqlite/is.go similarity index 80% rename from internal/table/sqlite3.go rename to internal/sqlite/is.go index 8b22794b..953179a1 100644 --- a/internal/table/sqlite3.go +++ b/internal/sqlite/is.go @@ -1,9 +1,6 @@ -//go:build !nosqlite3 && cgo -// +build !nosqlite3,cgo - /* Maddy Mail Server - Composable all-in-one email server. -Copyright © 2019-2020 Max Mazurov , Maddy Mail Server contributors +Copyright © 2019-2026 Max Mazurov , Maddy Mail Server contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,6 +16,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -package table +package sqliteprovider -import _ "github.com/mattn/go-sqlite3" +func IsSqliteDriver(name string) bool { + return name == "sqlite" || name == "sqlite3" +} diff --git a/internal/storage/imapsql/modernc_sqlite3.go b/internal/sqlite/modernc_sqlite3.go similarity index 76% rename from internal/storage/imapsql/modernc_sqlite3.go rename to internal/sqlite/modernc_sqlite3.go index 696b4c0a..694cb94a 100644 --- a/internal/storage/imapsql/modernc_sqlite3.go +++ b/internal/sqlite/modernc_sqlite3.go @@ -1,9 +1,8 @@ //go:build !nosqlite3 && !cgo -// +build !nosqlite3,!cgo /* Maddy Mail Server - Composable all-in-one email server. -Copyright © 2019-2020 Max Mazurov , Maddy Mail Server contributors +Copyright © 2019-2026 Max Mazurov , Maddy Mail Server contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,8 +18,18 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -package imapsql +package sqliteprovider import _ "modernc.org/sqlite" -const sqliteImpl = "modernc" +const ( + IsAvailable = true + IsTranspiled = true +) + +func MapDriverName(n string) string { + if n == "sqlite3" { + return "sqlite" + } + return n +} diff --git a/internal/storage/imapsql/no_sqlite3.go b/internal/sqlite/no_sqlite3.go similarity index 79% rename from internal/storage/imapsql/no_sqlite3.go rename to internal/sqlite/no_sqlite3.go index 525f8e41..17682ae8 100644 --- a/internal/storage/imapsql/no_sqlite3.go +++ b/internal/sqlite/no_sqlite3.go @@ -1,9 +1,8 @@ //go:build nosqlite3 -// +build nosqlite3 /* Maddy Mail Server - Composable all-in-one email server. -Copyright © 2019-2020 Max Mazurov , Maddy Mail Server contributors +Copyright © 2019-2026 Max Mazurov , Maddy Mail Server contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,6 +18,13 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -package imapsql +package sqliteprovider -const sqliteImpl = "missing" +const ( + IsAvailable = false + IsTranspiled = false +) + +func MapDriverName(n string) string { + return n +} diff --git a/internal/storage/imapsql/sqlite3.go b/internal/sqlite/sqlite3.go similarity index 76% rename from internal/storage/imapsql/sqlite3.go rename to internal/sqlite/sqlite3.go index 599f39d7..39aa855b 100644 --- a/internal/storage/imapsql/sqlite3.go +++ b/internal/sqlite/sqlite3.go @@ -1,9 +1,8 @@ //go:build !nosqlite3 && cgo -// +build !nosqlite3,cgo /* Maddy Mail Server - Composable all-in-one email server. -Copyright © 2019-2020 Max Mazurov , Maddy Mail Server contributors +Copyright © 2019-2026 Max Mazurov , Maddy Mail Server contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,8 +18,18 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -package imapsql +package sqliteprovider import _ "github.com/mattn/go-sqlite3" -const sqliteImpl = "cgo" +const ( + IsAvailable = true + IsTranspiled = false +) + +func MapDriverName(n string) string { + if n == "sqlite" { + return "sqlite3" + } + return n +} diff --git a/internal/storage/imapsql/imapsql.go b/internal/storage/imapsql/imapsql.go index ddee1358..89657aaf 100644 --- a/internal/storage/imapsql/imapsql.go +++ b/internal/storage/imapsql/imapsql.go @@ -48,6 +48,7 @@ import ( "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/authz" + sqliteprovider "github.com/foxcpp/maddy/internal/sqlite" "github.com/foxcpp/maddy/internal/updatepipe" "github.com/foxcpp/maddy/internal/updatepipe/pubsub" @@ -172,16 +173,16 @@ func (store *Storage) Configure(inlineArgs []string, cfg *config.Map) error { return errors.New("imapsql: driver is required") } - if driver == "sqlite3" { - if sqliteImpl == "modernc" { - store.Log.Println("using transpiled SQLite (modernc.org/sqlite), this is experimental") - driver = "sqlite" - } else if sqliteImpl == "cgo" { + if sqliteprovider.IsSqliteDriver(driver) { + if sqliteprovider.IsTranspiled { + store.Log.Println("using transpiled SQLite (modernc.org/sqlite)") + } else if sqliteprovider.IsAvailable { store.Log.Debugln("using cgo SQLite") - } else if sqliteImpl == "missing" { + } else { return errors.New("imapsql: SQLite is not supported, recompile without no_sqlite3 tag set") } } + driver = sqliteprovider.MapDriverName(driver) deliveryNormFunc, ok := authz.NormalizeFuncs[deliveryNormalize] if !ok { @@ -301,7 +302,7 @@ func (store *Storage) EnableUpdatePipe(mode updatepipe.BackendMode) error { } switch store.driver { - case "sqlite3": + case "sqlite3", "sqlite": dbId := sha1.Sum([]byte(strings.Join(store.dsn, " "))) sockPath := filepath.Join( config.RuntimeDirectory, diff --git a/internal/table/sql_query.go b/internal/table/sql_query.go index 0fe5c64f..ec2c3d23 100644 --- a/internal/table/sql_query.go +++ b/internal/table/sql_query.go @@ -21,11 +21,13 @@ package table import ( "context" "database/sql" + "errors" "fmt" "strings" "github.com/foxcpp/maddy/framework/config" "github.com/foxcpp/maddy/framework/module" + sqliteprovider "github.com/foxcpp/maddy/internal/sqlite" _ "github.com/lib/pq" ) @@ -89,6 +91,7 @@ func (s *SQL) Configure(inlineArgs []string, cfg *config.Map) error { if driver == "postgres" && s.namedArgs { return config.NodeErr(cfg.Block, "PostgreSQL driver does not support named_args") } + driver = sqliteprovider.MapDriverName(driver) db, err := sql.Open(driver, strings.Join(dsnParts, " ")) if err != nil { @@ -156,7 +159,7 @@ func (s *SQL) Lookup(ctx context.Context, val string) (string, bool, error) { row = s.lookup.QueryRowContext(ctx, val) } if err := row.Scan(&repl); err != nil { - if err == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { return "", false, nil } return "", false, fmt.Errorf("%s: lookup %s: %w", s.modName, val, err) From 53fc029196c1f1163ada5ffa96de6ff63e490457 Mon Sep 17 00:00:00 2001 From: denis Date: 2026年3月13日 14:55:11 +0200 Subject: [PATCH 136/171] Make reject and soft reject have configurable action in rspamd module. --- dist/vim/syntax/maddy-conf.vim | 2 ++ docs/reference/checks/rspamd.md | 16 ++++++++++++++++ internal/check/rspamd/rspamd.go | 19 +++++++++++++++---- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/dist/vim/syntax/maddy-conf.vim b/dist/vim/syntax/maddy-conf.vim index d59e7990..9e56cdd8 100644 --- a/dist/vim/syntax/maddy-conf.vim +++ b/dist/vim/syntax/maddy-conf.vim @@ -183,6 +183,7 @@ syn keyword maddyModDir \ quarantine_threshold \ read_timeout \ reject_threshold + \ reject_action \ relaxed_requiretls \ required_fields \ require_sender_match @@ -198,6 +199,7 @@ syn keyword maddyModDir \ sig_expiry \ sign_fields \ sign_subdomains + \ soft_reject_action \ softfail_action \ SOME_action \ source diff --git a/docs/reference/checks/rspamd.md b/docs/reference/checks/rspamd.md index 90063ae9..f37f5dcc 100644 --- a/docs/reference/checks/rspamd.md +++ b/docs/reference/checks/rspamd.md @@ -14,6 +14,8 @@ check.rspamd { error_resp_action ignore add_header_action quarantine rewrite_subj_action quarantine + reject_action reject + soft_reject_action reject flags pass_all } @@ -90,6 +92,20 @@ X-Spam-Flag and X-Spam-Score are added to the header irregardless of value. --- +### reject_action _action_ +Default: `reject` + +Action to take when rspamd requests to "reject". + +--- + +### soft_reject_action _action_ +Default: `reject` + +Action to take when rspamd requests to "soft reject". + +--- + ### flags _string-list..._ Default: `pass_all` diff --git a/internal/check/rspamd/rspamd.go b/internal/check/rspamd/rspamd.go index 559c94b6..ede0c151 100644 --- a/internal/check/rspamd/rspamd.go +++ b/internal/check/rspamd/rspamd.go @@ -57,6 +57,8 @@ type Check struct { errorRespAction modconfig.FailAction addHdrAction modconfig.FailAction rewriteSubjAction modconfig.FailAction + rejectAction modconfig.FailAction + softRejectAction modconfig.FailAction client *http.Client } @@ -117,6 +119,15 @@ func (c *Check) Configure(inlineArgs []string, cfg *config.Map) error { func() (interface{}, error) { return modconfig.FailAction{Quarantine: true}, nil }, modconfig.FailActionDirective, &c.rewriteSubjAction) + cfg.Custom("reject_action", false, false, + func() (interface{}, error) { + return modconfig.FailAction{Reject: true}, nil + }, modconfig.FailActionDirective, &c.rejectAction) + cfg.Custom("soft_reject_action", false, false, + func() (interface{}, error) { + return modconfig.FailAction{Reject: true}, nil + }, modconfig.FailActionDirective, &c.softRejectAction) + cfg.StringList("flags", false, false, []string{"pass_all"}, &flags) if _, err := cfg.Process(); err != nil { return err @@ -320,7 +331,7 @@ func (s *state) CheckBody(ctx context.Context, hdr textproto.Header, body buffer Header: hdrAdd, }) case "soft reject": - return module.CheckResult{ + return s.c.softRejectAction.Apply(module.CheckResult{ Reject: true, Reason: &exterrors.SMTPError{ Code: 450, @@ -329,9 +340,9 @@ func (s *state) CheckBody(ctx context.Context, hdr textproto.Header, body buffer CheckName: modName, Misc: map[string]interface{}{"action": "soft reject"}, }, - } + }) case "reject": - return module.CheckResult{ + return s.c.rejectAction.Apply(module.CheckResult{ Reject: true, Reason: &exterrors.SMTPError{ Code: 550, @@ -340,7 +351,7 @@ func (s *state) CheckBody(ctx context.Context, hdr textproto.Header, body buffer CheckName: modName, Misc: map[string]interface{}{"action": "reject"}, }, - } + }) } s.log.Msg("unhandled action", "action", respData.Action) From 1dfec2c0c49211bacecab2a293958863f7151e1a Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年3月14日 01:31:17 +0300 Subject: [PATCH 137/171] Improve config reload consistency, add tests --- framework/dns/debugflags.go | 7 + framework/dns/resolver.go | 4 - framework/module/lifetime.go | 46 ++++- internal/sqlite/modernc_sqlite3.go | 2 +- internal/sqlite/sqlite3.go | 2 +- internal/target/queue/queue.go | 52 ++++- internal/target/queue/timewheel.go | 43 ++-- internal/target/queue/timewheel_test.go | 35 ++-- maddy.go | 20 +- tests/build_cover.sh | 2 +- tests/imapsql_test.go | 3 +- tests/reload_non_unix.go | 25 +++ tests/reload_test.go | 255 ++++++++++++++++++++++++ tests/reload_unix.go | 42 ++++ tests/t.go | 54 +++-- 15 files changed, 510 insertions(+), 82 deletions(-) create mode 100644 tests/reload_non_unix.go create mode 100644 tests/reload_test.go create mode 100644 tests/reload_unix.go diff --git a/framework/dns/debugflags.go b/framework/dns/debugflags.go index fde218be..937edc97 100644 --- a/framework/dns/debugflags.go +++ b/framework/dns/debugflags.go @@ -32,5 +32,12 @@ func init() { Usage: "replace the DNS resolver address", Value: "system-default", Destination: &overrideServ, + Action: func(context *cli.Context, s string) error { + if s != "" && s != "system-default" { + override(s) + } + overrideServ = s + return nil + }, }) } diff --git a/framework/dns/resolver.go b/framework/dns/resolver.go index f1393fe6..41687311 100644 --- a/framework/dns/resolver.go +++ b/framework/dns/resolver.go @@ -53,9 +53,5 @@ func LookupAddr(ctx context.Context, r Resolver, ip net.IP) (string, error) { } func DefaultResolver() Resolver { - if overrideServ != "" && overrideServ != "system-default" { - override(overrideServ) - } - return net.DefaultResolver } diff --git a/framework/module/lifetime.go b/framework/module/lifetime.go index c24f19f3..2ccb08d4 100644 --- a/framework/module/lifetime.go +++ b/framework/module/lifetime.go @@ -37,18 +37,27 @@ type ReloadModule interface { Reload() error } +// EarlyStopModule is a LifetimeModule that needs to do some bookkeeping +// before new server instance starts during reload. +type EarlyStopModule interface { + LifetimeModule + EarlyStop() error +} + type LifetimeTracker struct { logger *log.Logger instances []*struct { - mod LifetimeModule - started bool + mod LifetimeModule + started bool + earlyStopped bool } } func (lt *LifetimeTracker) Add(mod LifetimeModule) { lt.instances = append(lt.instances, &struct { - mod LifetimeModule - started bool + mod LifetimeModule + started bool + earlyStopped bool }{mod: mod, started: false}) } @@ -59,6 +68,9 @@ func (lt *LifetimeTracker) StartAll() error { continue } + lt.logger.DebugMsg("starting module", + "mod_name", entry.mod.Name(), "inst_name", entry.mod.InstanceName()) + if err := entry.mod.Start(); err != nil { if err := lt.StopAll(); err != nil { lt.logger.Error("StopAll failed after Start fail", err) @@ -96,6 +108,32 @@ func (lt *LifetimeTracker) ReloadAll() error { return nil } +func (lt *LifetimeTracker) EarlyStopAll() error { + for i := len(lt.instances) - 1; i>= 0; i-- { + entry := lt.instances[i] + + if !entry.started { + continue + } + + rsm, ok := entry.mod.(EarlyStopModule) + if !ok { + continue + } + + if err := rsm.EarlyStop(); err != nil { + lt.logger.Error("module early stop failed", err, + "mod_name", entry.mod.Name(), "inst_name", entry.mod.InstanceName()) + continue + } + lt.logger.DebugMsg("module early stopped", + "mod_name", entry.mod.Name(), "inst_name", entry.mod.InstanceName()) + + entry.earlyStopped = true + } + return nil +} + // StopAll calls Stop for all registered LifetimeModule instances. func (lt *LifetimeTracker) StopAll() error { for i := len(lt.instances) - 1; i>= 0; i-- { diff --git a/internal/sqlite/modernc_sqlite3.go b/internal/sqlite/modernc_sqlite3.go index 694cb94a..6e31cf73 100644 --- a/internal/sqlite/modernc_sqlite3.go +++ b/internal/sqlite/modernc_sqlite3.go @@ -1,4 +1,4 @@ -//go:build !nosqlite3 && !cgo +//go:build (!nosqlite3 && !cgo) || modernc /* Maddy Mail Server - Composable all-in-one email server. diff --git a/internal/sqlite/sqlite3.go b/internal/sqlite/sqlite3.go index 39aa855b..38fb1dee 100644 --- a/internal/sqlite/sqlite3.go +++ b/internal/sqlite/sqlite3.go @@ -1,4 +1,4 @@ -//go:build !nosqlite3 && cgo +//go:build !nosqlite3 && cgo && !modernc /* Maddy Mail Server - Composable all-in-one email server. diff --git a/internal/target/queue/queue.go b/internal/target/queue/queue.go index 6cff2e22..8657110d 100644 --- a/internal/target/queue/queue.go +++ b/internal/target/queue/queue.go @@ -122,7 +122,7 @@ type Queue struct { location string hostname string autogenMsgDomain string - wheel *TimeWheel + wheel *TimeWheel[queueSlot] dsnPipeline module.DeliveryTarget @@ -211,6 +211,9 @@ func (q *Queue) Configure(inlineArgs []string, cfg *config.Map) error { cfg.Bool("debug", true, false, &q.Log.Debug) cfg.Int("max_tries", false, false, 20, &q.maxTries) cfg.Int("max_parallelism", false, false, 16, &q.maxParallelism) + cfg.Duration("post_init_delay", false, false, q.postInitDelay, &q.postInitDelay) + cfg.Duration("initial_retry_time", false, false, q.initialRetryTime, &q.initialRetryTime) + cfg.Float("retry_time_scale", false, false, q.retryTimeScale, &q.retryTimeScale) cfg.String("location", false, false, q.location, &q.location) cfg.Custom("target", false, true, nil, modconfig.DeliveryDirective, &q.Target) cfg.String("hostname", true, true, "", &q.hostname) @@ -249,7 +252,7 @@ func (q *Queue) Start() error { } func (q *Queue) start(maxParallelism int) error { - q.wheel = NewTimeWheel(q.dispatch) + q.wheel = NewTimeWheel[queueSlot](q.dispatch) q.deliverySemaphore = make(chan struct{}, maxParallelism) if err := q.readDiskQueue(); err != nil { @@ -261,13 +264,18 @@ func (q *Queue) start(maxParallelism int) error { return nil } -func (q *Queue) Stop() error { +func (q *Queue) EarlyStop() error { + // We must ensure queue state is consistent on disk before we proceed + // with configuration reload. q.wheel.Close() q.deliveryWg.Wait() - return nil } +func (q *Queue) Stop() error { + return q.EarlyStop() +} + // discardBroken changes the name of metadata file to have .meta_broken // extension. // @@ -283,8 +291,8 @@ func (q *Queue) discardBroken(id string) { } } -func (q *Queue) dispatch(value TimeSlot) { - slot := value.Value.(queueSlot) +func (q *Queue) dispatch(ctx context.Context, value TimeSlot[queueSlot]) { + slot := value.Value q.Log.Debugln("starting delivery for", slot.ID) @@ -329,7 +337,7 @@ func (q *Queue) dispatch(value TimeSlot) { body = slot.Body } - q.tryDelivery(meta, hdr, body) + q.tryDelivery(ctx, meta, hdr, body) }() } @@ -373,10 +381,10 @@ func toSMTPErr(err error) *smtp.SMTPError { return res } -func (q *Queue) tryDelivery(meta *QueueMetadata, header textproto.Header, body buffer.Buffer) { +func (q *Queue) tryDelivery(ctx context.Context, meta *QueueMetadata, header textproto.Header, body buffer.Buffer) { dl := target.DeliveryLogger(q.Log, meta.MsgMeta) - partialErr := q.deliver(meta, header, body) + partialErr := q.deliver(ctx, meta, header, body) dl.Debugf("errors: %v", partialErr.Errs) // While iterating the list of recipients we also pick the smallest tries count @@ -460,7 +468,7 @@ func (q *Queue) tryDelivery(meta *QueueMetadata, header textproto.Header, body b }) } -func (q *Queue) deliver(meta *QueueMetadata, header textproto.Header, body buffer.Buffer) partialError { +func (q *Queue) deliver(ctx context.Context, meta *QueueMetadata, header textproto.Header, body buffer.Buffer) partialError { dl := target.DeliveryLogger(q.Log, meta.MsgMeta) perr := partialError{ Errs: map[string]error{}, @@ -471,7 +479,7 @@ func (q *Queue) deliver(meta *QueueMetadata, header textproto.Header, body buffe msgMeta.ID = msgMeta.ID + "-" + strconv.FormatInt(time.Now().Unix(), 16) dl.Debugf("using message ID = %s", msgMeta.ID) - msgCtx, msgTask := trace.NewTask(context.Background(), "Queue delivery") + msgCtx, msgTask := trace.NewTask(ctx, "Queue delivery") defer msgTask.End() mailCtx, mailTask := trace.NewTask(msgCtx, "MAIL FROM") @@ -486,6 +494,15 @@ func (q *Queue) deliver(meta *QueueMetadata, header textproto.Header, body buffe } dl.Debugf("target.StartDelivery OK") + // Check in case delivery implementation is actually + // context-unaware. + if err := mailCtx.Err(); err != nil { + for _, rcpt := range meta.To { + perr.Errs[rcpt] = err + } + return perr + } + var acceptedRcpts []string for _, rcpt := range meta.To { rcptCtx, rcptTask := trace.NewTask(msgCtx, "RCPT TO") @@ -497,6 +514,15 @@ func (q *Queue) deliver(meta *QueueMetadata, header textproto.Header, body buffe acceptedRcpts = append(acceptedRcpts, rcpt) } rcptTask.End() + + // Check in case delivery implementation is actually + // context-unaware. + if err := mailCtx.Err(); err != nil { + for _, rcpt := range meta.To { + perr.Errs[rcpt] = err + } + return perr + } } if len(acceptedRcpts) == 0 { @@ -513,6 +539,10 @@ func (q *Queue) deliver(meta *QueueMetadata, header textproto.Header, body buffe } } + // At this point, it is too late to abort delivery. We should complete + // it or fail it consistently. + msgCtx = context.WithoutCancel(msgCtx) + bodyCtx, bodyTask := trace.NewTask(msgCtx, "DATA") defer bodyTask.End() diff --git a/internal/target/queue/timewheel.go b/internal/target/queue/timewheel.go index 060804b2..cc385e3c 100644 --- a/internal/target/queue/timewheel.go +++ b/internal/target/queue/timewheel.go @@ -20,17 +20,18 @@ package queue import ( "container/list" + "context" "sync" "sync/atomic" "time" ) -type TimeSlot struct { +type TimeSlot[Value any] struct { Time time.Time - Value interface{} + Value Value } -type TimeWheel struct { +type TimeWheel[Value any] struct { stopped uint32 slots *list.List @@ -38,39 +39,41 @@ type TimeWheel struct { updateNotify chan time.Time stopNotify chan struct{} + tickerCtx context.Context + tickerCancel context.CancelFunc - dispatch func(TimeSlot) + dispatch func(context.Context, TimeSlot[Value]) } -func NewTimeWheel(dispatch func(TimeSlot)) *TimeWheel { - tw := &TimeWheel{ +func NewTimeWheel[Value any](dispatch func(context.Context, TimeSlot[Value])) *TimeWheel[Value] { + ctx, cancel := context.WithCancel(context.Background()) + + tw := &TimeWheel[Value]{ slots: list.New(), stopNotify: make(chan struct{}), + tickerCtx: ctx, + tickerCancel: cancel, updateNotify: make(chan time.Time), dispatch: dispatch, } - go tw.tick() + go tw.tick(context.Background()) return tw } -func (tw *TimeWheel) Add(target time.Time, value interface{}) { +func (tw *TimeWheel[Value]) Add(target time.Time, value Value) { if atomic.LoadUint32(&tw.stopped) == 1 { // Already stopped, ignore. return } - if value == nil { - panic("can't insert nil objects into TimeWheel queue") - } - tw.slotsLock.Lock() - tw.slots.PushBack(TimeSlot{Time: target, Value: value}) + tw.slots.PushBack(TimeSlot[Value]{Time: target, Value: value}) tw.slotsLock.Unlock() tw.updateNotify <- target } -func (tw *TimeWheel) Close() { +func (tw *TimeWheel[Value]) Close() { atomic.StoreUint32(&tw.stopped, 1) // Idempotent Close is convenient sometimes. @@ -78,6 +81,8 @@ func (tw *TimeWheel) Close() { return } + tw.tickerCancel() + tw.stopNotify <- struct{}{} <-tw.stopnotify @@ -86,16 +91,16 @@ func (tw *TimeWheel) Close() { close(tw.updateNotify) } -func (tw *TimeWheel) tick() { +func (tw *TimeWheel[Value]) tick(ctx context.Context) { for { now := time.Now() // Look for list element closest to now. tw.slotsLock.Lock() - var closestSlot TimeSlot + var closestSlot TimeSlot[Value] var closestEl *list.Element for e := tw.slots.Front(); e != nil; e = e.Next() { - slot := e.Value.(TimeSlot) - if slot.Time.Sub(now) < closestSlot.Time.Sub(now) || closestSlot.Value == nil { + slot := e.Value.(TimeSlot[Value]) + if slot.Time.Sub(now) < closestSlot.Time.Sub(now) || closestEl == nil { closestSlot = slot closestEl = e } @@ -124,7 +129,7 @@ func (tw *TimeWheel) tick() { tw.slots.Remove(closestEl) tw.slotsLock.Unlock() - tw.dispatch(closestSlot) + tw.dispatch(ctx, closestSlot) break selectloop case newTarget := <-tw.updatenotify: diff --git a/internal/target/queue/timewheel_test.go b/internal/target/queue/timewheel_test.go index d758b603..9beb171e 100644 --- a/internal/target/queue/timewheel_test.go +++ b/internal/target/queue/timewheel_test.go @@ -19,6 +19,7 @@ along with this program. If not, see . package queue import ( + "context" "testing" "time" ) @@ -26,9 +27,9 @@ import ( func TestTimeWheelAdd(t *testing.T) { t.Parallel() - called := make(chan TimeSlot) + called := make(chan TimeSlot[int]) - w := NewTimeWheel(func(slot TimeSlot) { + w := NewTimeWheel[int](func(ctx context.Context, slot TimeSlot[int]) { called <- slot }) defer w.Close() @@ -36,7 +37,7 @@ func TestTimeWheelAdd(t *testing.T) { w.Add(time.Now().Add(1*time.Second), 1) slot := <-called - if val, _ := slot.Value.(int); val != 1 { + if slot.Value != 1 { t.Errorf("Wrong slot value: %v", slot.Value) } } @@ -44,9 +45,9 @@ func TestTimeWheelAdd(t *testing.T) { func TestTimeWheelAdd_Ordering(t *testing.T) { t.Parallel() - called := make(chan TimeSlot) + called := make(chan TimeSlot[int]) - w := NewTimeWheel(func(slot TimeSlot) { + w := NewTimeWheel[int](func(ctx context.Context, slot TimeSlot[int]) { called <- slot }) defer w.Close() @@ -55,11 +56,11 @@ func TestTimeWheelAdd_Ordering(t *testing.T) { w.Add(time.Now().Add(1250*time.Millisecond), 2) slot := <-called - if val, _ := slot.Value.(int); val != 1 { + if slot.Value != 1 { t.Errorf("Wrong first slot value: %v", slot.Value) } slot = <-called - if val, _ := slot.Value.(int); val != 2 { + if slot.Value != 2 { t.Errorf("Wrong second slot value: %v", slot.Value) } } @@ -67,9 +68,9 @@ func TestTimeWheelAdd_Ordering(t *testing.T) { func TestTimeWheelAdd_Restart(t *testing.T) { t.Parallel() - called := make(chan TimeSlot) + called := make(chan TimeSlot[int]) - w := NewTimeWheel(func(slot TimeSlot) { + w := NewTimeWheel[int](func(ctx context.Context, slot TimeSlot[int]) { called <- slot }) defer w.Close() @@ -78,11 +79,11 @@ func TestTimeWheelAdd_Restart(t *testing.T) { w.Add(time.Now().Add(500*time.Millisecond), 2) slot := <-called - if val, _ := slot.Value.(int); val != 2 { + if slot.Value != 2 { t.Errorf("Wrong first slot value: %v", slot.Value) } slot = <-called - if val, _ := slot.Value.(int); val != 1 { + if slot.Value != 1 { t.Errorf("Wrong second slot value: %v", slot.Value) } } @@ -90,9 +91,9 @@ func TestTimeWheelAdd_Restart(t *testing.T) { func TestTimeWheelAdd_MissingGotoBug(t *testing.T) { t.Parallel() - called := make(chan TimeSlot) + called := make(chan TimeSlot[int]) - w := NewTimeWheel(func(slot TimeSlot) { + w := NewTimeWheel[int](func(ctx context.Context, slot TimeSlot[int]) { called <- slot }) defer w.Close() @@ -101,7 +102,7 @@ func TestTimeWheelAdd_MissingGotoBug(t *testing.T) { w.Add(time.Now().Add(500*time.Millisecond), 2) // should correctly restart slot := <-called - if val, _ := slot.Value.(int); val != 2 { + if slot.Value != 2 { t.Errorf("Wrong first slot value: %v", slot.Value) } } @@ -109,9 +110,9 @@ func TestTimeWheelAdd_MissingGotoBug(t *testing.T) { func TestTimeWheelAdd_EmptyUpdWait(t *testing.T) { t.Parallel() - called := make(chan TimeSlot) + called := make(chan TimeSlot[int]) - w := NewTimeWheel(func(slot TimeSlot) { + w := NewTimeWheel[int](func(ctx context.Context, slot TimeSlot[int]) { called <- slot }) defer w.Close() @@ -121,7 +122,7 @@ func TestTimeWheelAdd_EmptyUpdWait(t *testing.T) { w.Add(time.Now().Add(1*time.Second), 1) slot := <-called - if val, _ := slot.Value.(int); val != 1 { + if slot.Value != 1 { t.Errorf("Wrong slot value: %v", slot.Value) } } diff --git a/maddy.go b/maddy.go index 4f1c642d..23eea317 100644 --- a/maddy.go +++ b/maddy.go @@ -377,7 +377,13 @@ func moduleStart(c *container.C) error { return c.Lifetime.StartAll() } -func moduleStop(c *container.C) error { +func moduleStop(c *container.C, earlyStop bool) error { + if earlyStop { + if err := c.Lifetime.EarlyStopAll(); err != nil { + log.DefaultLogger.Error("early stop failed", err) + } + } + return c.Lifetime.StopAll() } @@ -416,7 +422,7 @@ func moduleMain(configPath string) error { asyncStopWg.Wait() systemdStatus(SDStopping, "Waiting for current configuration to stop...") - if err := moduleStop(c); err != nil { + if err := moduleStop(c, true); err != nil { c.DefaultLogger.Msg("moduleStop failed", err) } c.DefaultLogger.Msg("server stopped") @@ -437,6 +443,12 @@ func moduleReload(oldContainer *container.C, configPath string, asyncStopWg *syn oldContainer.DefaultLogger.Msg("configuration loaded") + if err := oldContainer.Lifetime.EarlyStopAll(); err != nil { + oldContainer.DefaultLogger.Error("failed to early-stop old server", err) + container.Global = oldContainer + return oldContainer + } + netresource.ResetListenersUsage() oldContainer.DefaultLogger.Msg("starting new server") if err := moduleStart(newContainer); err != nil { @@ -445,7 +457,7 @@ func moduleReload(oldContainer *container.C, configPath string, asyncStopWg *syn return oldContainer } - newContainer.DefaultLogger.Msg("server started", "version", Version) + newContainer.DefaultLogger.Msg("new server started", "version", Version) systemdStatus(SDReloading, "New configuration running. Waiting for old connections and transactions to finish...") @@ -459,7 +471,7 @@ func moduleReload(oldContainer *container.C, configPath string, asyncStopWg *syn }() oldContainer.DefaultLogger.Msg("stopping old server") - if err := moduleStop(oldContainer); err != nil { + if err := moduleStop(oldContainer, false); err != nil { oldContainer.DefaultLogger.Error("moduleStop failed", err) } oldContainer.DefaultLogger.Msg("old server stopped") diff --git a/tests/build_cover.sh b/tests/build_cover.sh index 929511c2..b724fd5a 100755 --- a/tests/build_cover.sh +++ b/tests/build_cover.sh @@ -2,4 +2,4 @@ if [ -z "$GO" ]; then GO=go fi -exec $GO test -tags 'cover_main debugflags' -coverpkg 'github.com/foxcpp/maddy,github.com/foxcpp/maddy/pkg/...,github.com/foxcpp/maddy/internal/...' -cover -covermode atomic -c cover_test.go -o maddy.cover +exec $GO test -race -tags 'cover_main debugflags' -coverpkg 'github.com/foxcpp/maddy,github.com/foxcpp/maddy/pkg/...,github.com/foxcpp/maddy/internal/...' -cover -covermode atomic -c cover_test.go -o maddy.cover diff --git a/tests/imapsql_test.go b/tests/imapsql_test.go index 69f9e7ed..776d7423 100644 --- a/tests/imapsql_test.go +++ b/tests/imapsql_test.go @@ -1,5 +1,4 @@ -//go:build integration && cgo && !nosqlite3 -// +build integration,cgo,!nosqlite3 +//go:build integration /* Maddy Mail Server - Composable all-in-one email server. diff --git a/tests/reload_non_unix.go b/tests/reload_non_unix.go new file mode 100644 index 00000000..a7511634 --- /dev/null +++ b/tests/reload_non_unix.go @@ -0,0 +1,25 @@ +//go:build !unix + +/* +Maddy Mail Server - Composable all-in-one email server. +Copyright © 2019-2026 Max Mazurov , Maddy Mail Server contributors + +This program 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. + +This program 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 . +*/ + +package tests + +func (t *T) reloadConfig() { + t.Skip("Tests for config reload are not available") +} diff --git a/tests/reload_test.go b/tests/reload_test.go new file mode 100644 index 00000000..be3a479b --- /dev/null +++ b/tests/reload_test.go @@ -0,0 +1,255 @@ +//go:build unix && integration + +// Can't reload on Windows, yet + +/* +Maddy Mail Server - Composable all-in-one email server. +Copyright © 2019-2026 Max Mazurov , Maddy Mail Server contributors + +This program 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. + +This program 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 . +*/ + +package tests_test + +import ( + "testing" + "time" + + sqliteprovider "github.com/foxcpp/maddy/internal/sqlite" + "github.com/foxcpp/maddy/tests" +) + +func TestSmtpPipelineSwitch(tt *testing.T) { + if !sqliteprovider.IsTranspiled { + tt.Skip("Test is unstable with original SQLite") + } + + tt.Parallel() + t := tests.NewT(tt) + + t.DNS(nil) + t.Port("smtp") + t.Config(` + smtp tcp://127.0.0.1:{env:TEST_PORT_smtp} { + hostname maddy.test + tls off + + reject + } + `) + t.Run(1) + defer t.Close() + + conn1 := t.Conn("smtp") + defer conn1.Close() + conn1.SMTPNegotation("localhost", nil, nil) + conn1.Writeln("MAIL FROM:") + conn1.ExpectPattern("2*") + conn1.Writeln("RCPT TO:") + conn1.ExpectPattern("5*") // REJECTED + conn1.Writeln("RSET") + conn1.ExpectPattern("2*") + + t.Config(` + smtp tcp://127.0.0.1:{env:TEST_PORT_smtp} { + hostname maddy.test + tls off + + deliver_to dummy + } + `) + + conn2 := t.Conn("smtp") + defer conn2.Close() + conn2.SMTPNegotation("localhost", nil, nil) + conn2.Writeln("MAIL FROM:") + conn2.ExpectPattern("2*") + conn2.Writeln("RCPT TO:") + conn2.ExpectPattern("2*") + conn2.Writeln("DATA") + conn2.ExpectPattern("354 *") + conn2.Writeln("From: ") + conn2.Writeln("To: ") + conn2.Writeln("Subject: Hi!") + conn2.Writeln("") + conn2.Writeln("Hi!") + conn2.Writeln(".") + conn2.ExpectPattern("2*") // DISCARDED + + conn1.Writeln("MAIL FROM:") + conn1.ExpectPattern("2*") + conn1.Writeln("RCPT TO:") + conn1.ExpectPattern("5*") // Still REJECTED (running on old server). + conn1.Writeln("RSET") + conn1.ExpectPattern("2*") +} + +func TestImapStorageSwitch(tt *testing.T) { + if !sqliteprovider.IsTranspiled { + tt.Skip("Test is unstable with original SQLite") + } + + tt.Parallel() + t := tests.NewT(tt) + + t.DNS(nil) + t.Port("smtp") + t.Port("imap") + t.Config(` + storage.imapsql test_store { + driver sqlite3 + dsn imapsql.db + } + + imap tcp://127.0.0.1:{env:TEST_PORT_imap} { + tls off + + auth dummy + storage &test_store + } + + smtp tcp://127.0.0.1:{env:TEST_PORT_smtp} { + hostname maddy.test + tls off + + deliver_to &test_store + } + `) + t.Run(1) + defer t.Close() + + imapConn := t.Conn("imap") + defer imapConn.Close() + imapConn.ExpectPattern(`\* OK *`) + imapConn.Writeln(". LOGIN testusr@maddy.test 1234") + imapConn.ExpectPattern(". OK *") + imapConn.Writeln(". SELECT INBOX") + imapConn.ExpectPattern(`\* *`) + imapConn.ExpectPattern(`\* *`) + imapConn.ExpectPattern(`\* *`) + imapConn.ExpectPattern(`\* *`) + imapConn.ExpectPattern(`\* *`) + imapConn.ExpectPattern(`\* *`) + imapConn.ExpectPattern(`. OK *`) + + conn1 := t.Conn("smtp") + defer conn1.Close() + conn1.SMTPNegotation("localhost", nil, nil) + conn1.Writeln("MAIL FROM:") + conn1.ExpectPattern("2*") + conn1.Writeln("RCPT TO:") + conn1.ExpectPattern("2*") + conn1.Writeln("DATA") + conn1.ExpectPattern("354 *") + conn1.Writeln("From: ") + conn1.Writeln("To: ") + conn1.Writeln("Subject: Store 1") + conn1.Writeln("") + conn1.Writeln("Hi!") + conn1.Writeln(".") + conn1.ExpectPattern("2*") // Goes to storage 1 + + t.Config(` + storage.imapsql test_store { + driver sqlite3 + dsn imapsql2.db + } + + imap tcp://127.0.0.1:{env:TEST_PORT_imap} { + tls off + + auth dummy + storage &test_store + } + + smtp tcp://127.0.0.1:{env:TEST_PORT_smtp} { + hostname maddy.test + tls off + + deliver_to &test_store + } + `) + + imapConn2 := t.Conn("imap") + defer imapConn2.Close() + imapConn2.ExpectPattern(`\* OK *`) + imapConn2.Writeln(". LOGIN testusr2@maddy.test 1234") + imapConn2.ExpectPattern(". OK *") + + time.Sleep(500 * time.Millisecond) + + conn2 := t.Conn("smtp") + defer conn2.Close() + conn2.SMTPNegotation("localhost", nil, nil) + conn2.Writeln("MAIL FROM:") + conn2.ExpectPattern("2*") + conn2.Writeln("RCPT TO:") + conn2.ExpectPattern("2*") + conn2.Writeln("DATA") + conn2.ExpectPattern("354 *") + conn2.Writeln("From: ") + conn2.Writeln("To: ") + conn2.Writeln("Subject: Store 2") + conn2.Writeln("") + conn2.Writeln("Hi!") + conn2.Writeln(".") + conn2.ExpectPattern("2*") // Goes to storage 2 + + imapConn.Writeln(". NOOP") + imapConn.ExpectPattern(`\* 1 EXISTS`) + imapConn.ExpectPattern(`\* 1 RECENT`) + imapConn.ExpectPattern(". OK *") + + // Old connection sees message in store 1. + imapConn.Writeln(". FETCH 1 (BODY.PEEK[])") + imapConn.ExpectPattern(`\* 1 FETCH (BODY\[\] {*}*`) + imapConn.Expect(`Delivered-To: testusr@maddy.test`) + imapConn.Expect(`Return-Path: `) + imapConn.ExpectPattern(`Received: from localhost (client.maddy.test \[` + tests.DefaultSourceIP.String() + `\]) by maddy.test`) + imapConn.ExpectPattern(` (envelope-sender ) with ESMTP id *; *`) + imapConn.ExpectPattern(` *`) + imapConn.Expect("From: ") + imapConn.Expect("To: ") + imapConn.Expect("Subject: Store 1") + imapConn.Expect("") + imapConn.Expect("Hi!") + imapConn.Expect(")") + imapConn.ExpectPattern(`. OK *`) + + // New connection sees message in store 2. + imapConn2.Writeln(". SELECT INBOX") + imapConn2.ExpectPattern(`\* *`) + imapConn2.ExpectPattern(`\* *`) + imapConn2.ExpectPattern(`\* *`) + imapConn2.ExpectPattern(`\* *`) + imapConn2.ExpectPattern(`\* *`) + imapConn2.ExpectPattern(`\* *`) + imapConn2.ExpectPattern(`\* *`) + imapConn2.ExpectPattern(`. OK *`) + imapConn2.Writeln(". FETCH 1 (BODY.PEEK[])") + imapConn2.ExpectPattern(`\* 1 FETCH (BODY\[\] {*}*`) + imapConn2.Expect(`Delivered-To: testusr2@maddy.test`) + imapConn2.Expect(`Return-Path: `) + imapConn2.ExpectPattern(`Received: from localhost (client.maddy.test \[` + tests.DefaultSourceIP.String() + `\]) by maddy.test`) + imapConn2.ExpectPattern(` (envelope-sender ) with ESMTP id *; *`) + imapConn2.ExpectPattern(` *`) + imapConn2.Expect("From: ") + imapConn2.Expect("To: ") + imapConn2.Expect("Subject: Store 2") + imapConn2.Expect("") + imapConn2.Expect("Hi!") + imapConn2.Expect(")") + imapConn2.ExpectPattern(`. OK *`) + +} diff --git a/tests/reload_unix.go b/tests/reload_unix.go new file mode 100644 index 00000000..303f4e48 --- /dev/null +++ b/tests/reload_unix.go @@ -0,0 +1,42 @@ +//go:build unix + +/* +Maddy Mail Server - Composable all-in-one email server. +Copyright © 2019-2026 Max Mazurov , Maddy Mail Server contributors + +This program 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. + +This program 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 . +*/ + +package tests + +import ( + "syscall" + "time" +) + +func (t *T) reloadConfig() { + err := t.servProc.Process.Signal(syscall.SIGUSR2) + if err != nil { + t.Fatal("Failed to send SIGUSR2:", err) + } + + t.Log("waiting for server to reload...") + + select { + case <-t.reloadedchan: + case <-time.after(5 * time.Second): + t.killServer() + t.Fatal("Server reload is taking too long, killed") + } +} diff --git a/tests/t.go b/tests/t.go index 81423610..4e8d4fc1 100644 --- a/tests/t.go +++ b/tests/t.go @@ -60,13 +60,16 @@ type T struct { portsRev map[uint16]string servProc *exec.Cmd + + reloadedChan chan struct{} } func NewT(t *testing.T) *T { return &T{ - T: t, - ports: map[string]uint16{}, - portsRev: map[uint16]string{}, + T: t, + ports: map[string]uint16{}, + portsRev: map[uint16]string{}, + reloadedChan: make(chan struct{}, 1), } } @@ -75,11 +78,21 @@ func NewT(t *testing.T) *T { func (t *T) Config(cfg string) { t.Helper() + t.cfg = cfg + if t.servProc != nil { - panic("tests: Config called after Run") - } + t.Log("Reloading configuration for running server...") - t.cfg = cfg + configPreable := "state_dir " + filepath.Join(t.testDir, "statedir") + "\n" + + "runtime_dir " + filepath.Join(t.testDir, "runtimedir") + "\n\n" + + err := os.WriteFile(filepath.Join(t.testDir, "maddy.conf"), []byte(configPreable+t.cfg), os.ModePerm) + if err != nil { + t.Fatal("Test configuration failed:", err) + } + + t.reloadConfig() + } } // DNS sets the DNS zones to emulate for the tested server instance. @@ -303,33 +316,38 @@ func (t *T) Run(waitListeners int) { t.Fatal("Test configuration failed:", err) } - // Log scanning goroutine checks for the "listening" messages and sends 'true' - // on the channel each time. - listeningMsg := make(chan bool) + serverStarted := make(chan bool) go func() { defer logOut.Close() - defer close(listeningMsg) + defer close(serverStarted) scnr := bufio.NewScanner(logOut) for scnr.Scan() { line := scnr.Text() - if strings.Contains(line, "listening on") { - listeningMsg <- true - line += " (test runner>listener wait trigger<)" + t.Log("maddy:", line) + + if strings.HasPrefix(line, "server started") { + serverStarted <- true } - t.Log("maddy:", line) + if strings.HasPrefix(line, "new server started") { + select { + case t.reloadedChan <- struct{}{}: + t.Log("server reload confirmed, continuing test") + default: + t.Log("unexpected reloads detected") + t.Fail() + } + } } if err := scnr.Err(); err != nil { t.Log("stderr I/O error:", err) } }() - for i := 0; i < waitListeners; i++ { - if !<-listeningmsg { - t.Fatal("Log ended before all expected listeners are up. Start-up error?") - } + if !<-serverstarted { + t.Fatal("Log ended before all expected listeners are up. Start-up error?") } t.servProc = cmd From 0300e401938150171ffe93aba2a3b6dee39e585b Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年3月14日 01:50:15 +0300 Subject: [PATCH 138/171] Linter clean-up --- internal/endpoint/openmetrics/om.go | 2 +- internal/endpoint/smtp/smtp_test.go | 96 ++++++++++++++++++++------- internal/table/file_test.go | 68 +++++++++++++++---- internal/target/queue/queue_test.go | 18 ++--- internal/target/remote/mxauth_test.go | 19 ++++-- 5 files changed, 152 insertions(+), 51 deletions(-) diff --git a/internal/endpoint/openmetrics/om.go b/internal/endpoint/openmetrics/om.go index 0cff5c1e..e7e4a791 100644 --- a/internal/endpoint/openmetrics/om.go +++ b/internal/endpoint/openmetrics/om.go @@ -86,7 +86,7 @@ func (e *Endpoint) Start() error { l, err := netresource.Listen(endp.Network(), endp.Address()) if err != nil { if err := e.Stop(); err != nil { - + e.logger.Error("failed to stop after failed listen", err) } return fmt.Errorf("%s: %v", modName, err) } diff --git a/internal/endpoint/smtp/smtp_test.go b/internal/endpoint/smtp/smtp_test.go index b7d51320..5fc79703 100644 --- a/internal/endpoint/smtp/smtp_test.go +++ b/internal/endpoint/smtp/smtp_test.go @@ -155,7 +155,9 @@ func TestSMTPDelivery(t *testing.T) { if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() err = submitMsg(t, cl, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"}, testMsg) if err != nil { @@ -187,7 +189,9 @@ func TestSMTPDelivery(t *testing.T) { func TestSMTPDelivery_rDNSError(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() endp.resolver.(*mockdns.Resolver).Zones["1.0.0.127.in-addr.arpa."] = mockdns.Zone{ Err: &net.DNSError{ @@ -202,7 +206,9 @@ func TestSMTPDelivery_rDNSError(t *testing.T) { if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() err = submitMsg(t, cl, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"}, testMsg) if err != nil { @@ -231,13 +237,17 @@ func TestSMTPDelivery_EarlyCheck_Fail(t *testing.T) { }, }, }, nil) - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() err = cl.Mail("sender@example.org", nil) if err == nil { @@ -271,13 +281,17 @@ func TestSMTPDeliver_CheckError(t *testing.T) { }, }, nil) endp.deferServerReject = false - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() err = cl.Mail("sender@example.org", nil) if err == nil { @@ -310,13 +324,17 @@ func TestSMTPDeliver_CheckError_Deferred(t *testing.T) { }, }, nil) endp.deferServerReject = true - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() err = cl.Mail("sender@example.org", nil) if err != nil { @@ -349,13 +367,17 @@ func TestSMTPDeliver_CheckError_Deferred(t *testing.T) { func TestSMTPDelivery_Multi(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() err = submitMsg(t, cl, "sender1@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"}, testMsg) if err != nil { @@ -387,13 +409,17 @@ func TestSMTPDelivery_Multi(t *testing.T) { func TestSMTPDelivery_AbortData(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + _ = cl.Close() + }() if err := cl.Hello("mx.example.org"); err != nil { t.Fatal(err) @@ -413,7 +439,7 @@ func TestSMTPDelivery_AbortData(t *testing.T) { } // Then.. Suddenly, close the connection without sending the final dot. - cl.Close() + assert.NoError(t, cl.Close()) time.Sleep(250 * time.Millisecond) @@ -425,13 +451,17 @@ func TestSMTPDelivery_AbortData(t *testing.T) { func TestSMTPDelivery_EmptyMessage(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() if err := cl.Hello("mx.example.org"); err != nil { t.Fatal(err) @@ -464,13 +494,17 @@ func TestSMTPDelivery_EmptyMessage(t *testing.T) { func TestSMTPDelivery_AbortLogout(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + _ = cl.Close() + }() if err := cl.Hello("mx.example.org"); err != nil { t.Fatal(err) @@ -483,7 +517,7 @@ func TestSMTPDelivery_AbortLogout(t *testing.T) { } // Then.. Suddenly, close the connection. - cl.Close() + assert.NoError(t, cl.Close()) time.Sleep(250 * time.Millisecond) @@ -495,13 +529,17 @@ func TestSMTPDelivery_AbortLogout(t *testing.T) { func TestSMTPDelivery_Reset(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() if err := cl.Mail("from-garbage@example.org", nil); err != nil { t.Fatal(err) @@ -530,13 +568,17 @@ func TestSMTPDelivery_Reset(t *testing.T) { func TestSMTPDelivery_SubmissionAuthRequire(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "submission", &module.Dummy{}, &tgt, nil, nil) - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() if err := cl.Mail("from-garbage@example.org", nil); err == nil { t.Fatal("Expected an error, got none") @@ -546,13 +588,17 @@ func TestSMTPDelivery_SubmissionAuthRequire(t *testing.T) { func TestSMTPDelivery_SubmissionAuthOK(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "submission", &module.Dummy{}, &tgt, nil, nil) - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() if err := cl.Auth(sasl.NewPlainClient("", "user", "password")); err != nil { t.Fatal(err) diff --git a/internal/table/file_test.go b/internal/table/file_test.go index ae8466fa..50d2e1cc 100644 --- a/internal/table/file_test.go +++ b/internal/table/file_test.go @@ -37,8 +37,18 @@ func TestReadFile(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.Remove(f.Name()) - defer f.Close() + defer func(name string) { + err := os.Remove(name) + if err != nil { + t.Log(err) + } + }(f.Name()) + defer func(f *os.File) { + err := f.Close() + if err != nil { + t.Log(err) + } + }(f) if _, err := f.WriteString(file); err != nil { t.Fatal(err) } @@ -92,12 +102,20 @@ func TestFileReload(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.Remove(f.Name()) + defer func(name string) { + err := os.Remove(name) + if err != nil { + t.Log(err) + } + }(f.Name()) if _, err := f.WriteString(file); err != nil { - f.Close() + _ = f.Close() + t.Fatal(err) + } + err = f.Close() + if err != nil { t.Fatal(err) } - f.Close() mod, err := NewFile("", "") if err != nil { @@ -148,7 +166,12 @@ func TestFileReload_Broken(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.Remove(f.Name()) + defer func(name string) { + err := os.Remove(name) + if err != nil { + t.Fatal(err) + } + }(f.Name()) if _, err := f.WriteString(file); err != nil { assert.NoError(t, f.Close()) t.Fatal(err) @@ -164,7 +187,12 @@ func TestFileReload_Broken(t *testing.T) { t.Fatal(err) } m.log = testutils.Logger(t, FileModName) - defer m.Stop() + defer func(m *File) { + err := m.Stop() + if err != nil { + t.Fatal(err) + } + }(m) if err := mod.Configure([]string{f.Name()}, &config.Map{Block: config.Node{}}); err != nil { t.Fatal(err) @@ -177,7 +205,12 @@ func TestFileReload_Broken(t *testing.T) { if _, err := f2.WriteString(":"); err != nil { t.Fatal(err) } - defer f2.Close() + defer func(f2 *os.File) { + err := f2.Close() + if err != nil { + t.Fatal(err) + } + }(f2) time.Sleep(3 * reloadInterval) @@ -198,10 +231,13 @@ func TestFileReload_Removed(t *testing.T) { t.Fatal(err) } if _, err := f.WriteString(file); err != nil { - f.Close() + _ = f.Close() + t.Fatal(err) + } + err = f.Close() + if err != nil { t.Fatal(err) } - f.Close() mod, err := NewFile("", "") if err != nil { @@ -212,13 +248,21 @@ func TestFileReload_Removed(t *testing.T) { t.Fatal(err) } m.log = testutils.Logger(t, FileModName) - defer m.Stop() + defer func(m *File) { + err := m.Stop() + if err != nil { + t.Fatal(err) + } + }(m) if err := mod.Configure([]string{f.Name()}, &config.Map{Block: config.Node{}}); err != nil { t.Fatal(err) } - os.Remove(f.Name()) + err = os.Remove(f.Name()) + if err != nil { + t.Fatal(err) + } time.Sleep(3 * reloadInterval) diff --git a/internal/target/queue/queue_test.go b/internal/target/queue/queue_test.go index 649fe428..8c5f5269 100644 --- a/internal/target/queue/queue_test.go +++ b/internal/target/queue/queue_test.go @@ -246,7 +246,7 @@ func TestQueueDelivery(t *testing.T) { // Wait for the delivery to complete and stop processing. msg := readMsgChanTimeout(t, dt.committed, 5*time.Second) - q.Stop() + assert.NoError(t, q.Stop()) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester1@example.org", "tester2@example.org"}, "") @@ -356,7 +356,7 @@ func TestQueueDelivery_TemporaryFail_Partial(t *testing.T) { msg = readMsgChanTimeout(t, dt.committed, 5000*time.Second) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester2@example.org"}, "") - q.Stop() + assert.NoError(t, q.Stop()) // No more retries scheduled, queue storage is clear. checkQueueDir(t, q, []string{}) } @@ -396,7 +396,7 @@ func TestQueueDelivery_MultipleAttempts(t *testing.T) { msg = readMsgChanTimeout(t, dt.committed, 5*time.Second) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester2@example.org"}, "") - q.Stop() + assert.NoError(t, q.Stop()) // No more retries should be scheduled. checkQueueDir(t, q, []string{}) } @@ -421,7 +421,7 @@ func TestQueueDelivery_PermanentRcptReject(t *testing.T) { msg := readMsgChanTimeout(t, dt.committed, 5*time.Second) testutils.CheckMsgID(t, msg, "tester@example.org", []string{"tester2@example.org"}, "") - q.Stop() + assert.NoError(t, q.Stop()) // No more retries should be scheduled. checkQueueDir(t, q, []string{}) } @@ -455,7 +455,7 @@ func TestQueueDelivery_TemporaryRcptReject(t *testing.T) { msg = readMsgChanTimeout(t, dt.committed, 5*time.Second) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester1@example.org"}, "") - q.Stop() + assert.NoError(t, q.Stop()) // No more retries should be scheduled. checkQueueDir(t, q, []string{}) } @@ -489,7 +489,7 @@ func TestQueueDelivery_SerializationRoundtrip(t *testing.T) { testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester2@example.org"}, "") // Then stop it. - q.Stop() + assert.NoError(t, q.Stop()) // Make sure it is saved. checkQueueDir(t, q, []string{deliveryID}) @@ -502,7 +502,7 @@ func TestQueueDelivery_SerializationRoundtrip(t *testing.T) { testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester1@example.org"}, "") // Close it again. - q.Stop() + assert.NoError(t, q.Stop()) // No more retries should be scheduled. checkQueueDir(t, q, []string{}) } @@ -536,7 +536,7 @@ func TestQueueDelivery_DeserlizationCleanUp(t *testing.T) { msg := readMsgChanTimeout(t, dt.committed, 5*time.Second) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester2@example.org"}, "") - q.Stop() + assert.NoError(t, q.Stop()) if err := os.Remove(filepath.Join(q.location, deliveryID+fileSuffix)); err != nil { t.Fatal(err) @@ -544,7 +544,7 @@ func TestQueueDelivery_DeserlizationCleanUp(t *testing.T) { // Dangling files should be removed during load. q = newTestQueueDir(t, &dt, q.location) - q.Stop() + assert.NoError(t, q.Stop()) // Nothing should be left. checkQueueDir(t, q, []string{}) diff --git a/internal/target/remote/mxauth_test.go b/internal/target/remote/mxauth_test.go index e7d97f5d..ac47375a 100644 --- a/internal/target/remote/mxauth_test.go +++ b/internal/target/remote/mxauth_test.go @@ -196,7 +196,9 @@ func TestRemoteDelivery_AuthMX_MTASTS_NoTLS(t *testing.T) { testSTSPolicy(t, zones, mtastsGet), &localPolicy{minMXLevel: module.MX_MTASTS}, }) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -237,7 +239,12 @@ func TestRemoteDelivery_AuthMX_MTASTS_RequirePKIX(t *testing.T) { testSTSPolicy(t, zones, mtastsGet), &localPolicy{minMXLevel: module.MX_MTASTS}, }) - defer tgt.Stop() + defer func(tgt *Target) { + err := tgt.Stop() + if err != nil { + t.Fatal(err) + } + }(tgt) _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -287,7 +294,9 @@ func TestRemoteDelivery_AuthMX_MTASTS_NoPolicy(t *testing.T) { testSTSPolicy(t, zones, mtastsGet), &localPolicy{minMXLevel: module.MX_MTASTS}, }) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -330,7 +339,9 @@ func TestRemoteDelivery_AuthMX_DNSSEC(t *testing.T) { extResolver.Cfg.Port = strconv.Itoa(addr.Port) tgt := testTarget(t, zones, extResolver, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) From 7922d24baf4431890bccb3ad5d15aa2fec4bed7d Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年3月14日 02:08:40 +0300 Subject: [PATCH 139/171] Linter clean-up 2 --- framework/dns/override.go | 2 +- internal/endpoint/smtp/smtputf8_test.go | 77 +++++++++++---- internal/endpoint/smtp/submission_test.go | 5 +- internal/target/queue/queue_test.go | 2 +- internal/target/remote/mxauth_test.go | 28 ++++-- internal/target/remote/remote_test.go | 113 ++++++++++++++++------ 6 files changed, 169 insertions(+), 58 deletions(-) diff --git a/framework/dns/override.go b/framework/dns/override.go index 25f0da08..0f073afd 100644 --- a/framework/dns/override.go +++ b/framework/dns/override.go @@ -32,7 +32,7 @@ var overrideServ string // // The server argument is in form of "IP:PORT". It is expected that the server // will be available both using TCP and UDP on the same port. -func override(server string) { +func override(server string) { // nolint: unused // used in debugflags.go net.DefaultResolver.PreferGo = true net.DefaultResolver.Dial = func(ctx context.Context, network, _ string) (net.Conn, error) { dialer := net.Dialer{ diff --git a/internal/endpoint/smtp/smtputf8_test.go b/internal/endpoint/smtp/smtputf8_test.go index 2f157aba..ff16be6c 100644 --- a/internal/endpoint/smtp/smtputf8_test.go +++ b/internal/endpoint/smtp/smtputf8_test.go @@ -27,6 +27,7 @@ import ( "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/testutils" + "github.com/stretchr/testify/assert" ) func TestSMTPUTF8_MangleStatusMessage(t *testing.T) { @@ -43,14 +44,18 @@ func TestSMTPUTF8_MangleStatusMessage(t *testing.T) { }, }, nil) endp.deferServerReject = false - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() err = cl.Mail("sender@example.org", nil) if err == nil { @@ -73,7 +78,9 @@ func TestSMTP_RejectNonASCIIFrom(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) endp.deferServerReject = false - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) @@ -100,14 +107,18 @@ func TestSMTPUTF8_NormalizeCaseFoldFrom(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) endp.deferServerReject = false - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() err = submitMsgOpts(t, cl, "foo@E\u0301.example.org", []string{"rcpt@example.com"}, &smtp.MailOptions{ UTF8: true, @@ -127,14 +138,18 @@ func TestSMTP_RejectNonASCIIRcpt(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) endp.deferServerReject = false - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() err = submitMsg(t, cl, "x@example.org", []string{"ѣ@example.org"}, testMsg) @@ -154,14 +169,18 @@ func TestSMTPUTF8_NormalizeCaseFoldRcpt(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) endp.deferServerReject = false - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() err = submitMsgOpts(t, cl, "x@example.org", []string{"foo@E\u0301.example.org"}, &smtp.MailOptions{ UTF8: true, @@ -191,14 +210,18 @@ func TestSMTPUTF8_NoMangleStatusMessage(t *testing.T) { }, }, nil) endp.deferServerReject = false - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() err = cl.Mail("sender@example.org", &smtp.MailOptions{ UTF8: true, @@ -222,14 +245,18 @@ func TestSMTPUTF8_NoMangleStatusMessage(t *testing.T) { func TestSMTPUTF8_Received_EHLO_ALabel(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() if err := cl.Hello("凱凱.invalid"); err != nil { t.Fatal(err) @@ -256,7 +283,9 @@ func TestSMTPUTF8_Received_EHLO_ALabel(t *testing.T) { func TestSMTPUTF8_Received_rDNS_ALabel(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() defer testutils.WaitForConnsClose(t, endp.serv) endp.resolver.(*mockdns.Resolver).Zones["1.0.0.127.in-addr.arpa."] = mockdns.Zone{ @@ -267,7 +296,9 @@ func TestSMTPUTF8_Received_rDNS_ALabel(t *testing.T) { if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() err = submitMsg(t, cl, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"}, testMsg) if err != nil { @@ -290,7 +321,9 @@ func TestSMTPUTF8_Received_rDNS_ALabel(t *testing.T) { func TestSMTPUTF8_Received_rDNS_ULabel(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() defer testutils.WaitForConnsClose(t, endp.serv) endp.resolver.(*mockdns.Resolver).Zones["1.0.0.127.in-addr.arpa."] = mockdns.Zone{ @@ -301,7 +334,9 @@ func TestSMTPUTF8_Received_rDNS_ULabel(t *testing.T) { if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() err = submitMsgOpts(t, cl, "sender@example.org", []string{"rcpt1@example.com", "rcpt2@example.com"}, &smtp.MailOptions{ UTF8: true, @@ -326,14 +361,18 @@ func TestSMTPUTF8_Received_rDNS_ULabel(t *testing.T) { func TestSMTPUTF8_Received_EHLO_ULabel(t *testing.T) { tgt := testutils.Target{} endp := testEndpoint(t, "smtp", nil, &tgt, nil, nil) - defer endp.Stop() + defer func() { + assert.NoError(t, endp.Stop()) + }() defer testutils.WaitForConnsClose(t, endp.serv) cl, err := smtp.Dial("127.0.0.1:" + testPort) if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + assert.NoError(t, cl.Close()) + }() if err := cl.Hello("凱凱.invalid"); err != nil { t.Fatal(err) diff --git a/internal/endpoint/smtp/submission_test.go b/internal/endpoint/smtp/submission_test.go index beb4cf5e..91f030cf 100644 --- a/internal/endpoint/smtp/submission_test.go +++ b/internal/endpoint/smtp/submission_test.go @@ -26,6 +26,7 @@ import ( "github.com/emersion/go-message/textproto" "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/module" + "github.com/stretchr/testify/assert" ) func init() { @@ -54,9 +55,9 @@ func TestSubmissionPrepare(t *testing.T) { // Synchronize the endpoint initialization. // Otherwise Close will race with Serve called by setupListeners. cl, _ := smtp.Dial("127.0.0.1:" + testPort) - cl.Close() + assert.NoError(t, cl.Close()) - endp.Stop() + assert.NoError(t, endp.Stop()) }() session, err := endp.NewSession(nil) diff --git a/internal/target/queue/queue_test.go b/internal/target/queue/queue_test.go index 8c5f5269..6eb7a101 100644 --- a/internal/target/queue/queue_test.go +++ b/internal/target/queue/queue_test.go @@ -323,7 +323,7 @@ func TestQueueDelivery_TemporaryFail(t *testing.T) { msg := readMsgChanTimeout(t, dt.committed, 5*time.Second) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester1@example.org", "tester2@example.org"}, "") - q.Stop() + assert.NoError(t, q.Stop()) // No more retries scheduled, queue storage is clear. defer checkQueueDir(t, q, []string{}) } diff --git a/internal/target/remote/mxauth_test.go b/internal/target/remote/mxauth_test.go index ac47375a..f979a53d 100644 --- a/internal/target/remote/mxauth_test.go +++ b/internal/target/remote/mxauth_test.go @@ -383,7 +383,9 @@ func TestRemoteDelivery_AuthMX_DNSSEC_Fail(t *testing.T) { tgt := testTarget(t, zones, extResolver, []module.MXAuthPolicy{ &localPolicy{minMXLevel: module.MX_DNSSEC}, }) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() _, err = testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -425,7 +427,9 @@ func TestRemoteDelivery_REQUIRETLS(t *testing.T) { testSTSPolicy(t, zones, mtastsGet), }) tgt.tlsConfig = clientCfg - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDeliveryMeta(t, tgt, "test@example.com", []string{"test@example.invalid"}, &module.MsgMetadata{ OriginalFrom: "test@example.com", @@ -466,7 +470,9 @@ func TestRemoteDelivery_REQUIRETLS_Fail(t *testing.T) { testSTSPolicy(t, zones, mtastsGet), }) tgt.tlsConfig = clientCfg - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() if _, err := testutils.DoTestDeliveryErrMeta(t, tgt, "test@example.com", []string{"test@example.invalid"}, &module.MsgMetadata{ OriginalFrom: "test@example.com", @@ -512,7 +518,9 @@ func TestRemoteDelivery_REQUIRETLS_Relaxed(t *testing.T) { }) tgt.relaxedREQUIRETLS = true tgt.tlsConfig = clientCfg - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDeliveryMeta(t, tgt, "test@example.com", []string{"test@example.invalid"}, &module.MsgMetadata{ OriginalFrom: "test@example.com", @@ -549,7 +557,9 @@ func TestRemoteDelivery_REQUIRETLS_Relaxed_NoMXAuth(t *testing.T) { }) tgt.relaxedREQUIRETLS = true tgt.tlsConfig = clientCfg - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() if _, err := testutils.DoTestDeliveryErrMeta(t, tgt, "test@example.com", []string{"test@example.invalid"}, &module.MsgMetadata{ OriginalFrom: "test@example.com", @@ -595,7 +605,9 @@ func TestRemoteDelivery_REQUIRETLS_Relaxed_NoTLS(t *testing.T) { }) tgt.relaxedREQUIRETLS = true tgt.tlsConfig = nil - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() if _, err := testutils.DoTestDeliveryErrMeta(t, tgt, "test@example.com", []string{"test@example.invalid"}, &module.MsgMetadata{ OriginalFrom: "test@example.com", @@ -646,7 +658,9 @@ func TestRemoteDelivery_REQUIRETLS_Relaxed_TLSFail(t *testing.T) { srv.TLSConfig.MinVersion = tls.VersionTLS11 srv.TLSConfig.MaxVersion = tls.VersionTLS11 tgt.tlsConfig = clientCfg - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() if _, err := testutils.DoTestDeliveryErrMeta(t, tgt, "test@example.com", []string{"test@example.invalid"}, &module.MsgMetadata{ OriginalFrom: "test@example.com", diff --git a/internal/target/remote/remote_test.go b/internal/target/remote/remote_test.go index 795e1634..b21f97e0 100644 --- a/internal/target/remote/remote_test.go +++ b/internal/target/remote/remote_test.go @@ -40,6 +40,7 @@ import ( "github.com/foxcpp/maddy/internal/limits" "github.com/foxcpp/maddy/internal/smtpconn/pool" "github.com/foxcpp/maddy/internal/testutils" + "github.com/stretchr/testify/assert" ) // .invalid TLD is used here to make sure if there is something wrong about @@ -129,7 +130,9 @@ func TestRemoteDelivery(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -146,7 +149,9 @@ func TestRemoteDelivery_NoMXFallback(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { @@ -176,7 +181,9 @@ func TestRemoteDelivery_EmptySender(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDelivery(t, tgt, "", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "", []string{"test@example.invalid"}) @@ -202,7 +209,9 @@ func TestRemoteDelivery_IPLiteral(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@[127.0.0.1]"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@[127.0.0.1]"}) @@ -219,7 +228,9 @@ func TestRemoteDelivery_FallbackMX(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -239,7 +250,9 @@ func TestRemoteDelivery_BodyNonAtomic(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() c := multipleErrs{ errs: map[string]error{}, @@ -267,7 +280,9 @@ func TestRemoteDelivery_Abort(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { @@ -297,7 +312,9 @@ func TestRemoteDelivery_CommitWithoutBody(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { @@ -334,7 +351,9 @@ func TestRemoteDelivery_MAILFROMErr(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { @@ -360,7 +379,9 @@ func TestRemoteDelivery_NoMX(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { @@ -390,7 +411,9 @@ func TestRemoteDelivery_NullMX(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { @@ -419,7 +442,9 @@ func TestRemoteDelivery_Quarantined(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() meta := module.MsgMetadata{ID: "test..."} @@ -467,7 +492,9 @@ func TestRemoteDelivery_MAILFROMErr_Repeated(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { @@ -507,7 +534,9 @@ func TestRemoteDelivery_RcptErr(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { @@ -558,7 +587,9 @@ func TestRemoteDelivery_DownMX(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -581,7 +612,9 @@ func TestRemoteDelivery_AllMXDown(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -612,7 +645,9 @@ func TestRemoteDelivery_Split(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid", "test@example2.invalid"}) @@ -651,7 +686,9 @@ func TestRemoteDelivery_Split_Fail(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { @@ -704,7 +741,9 @@ func TestRemoteDelivery_BodyErr(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { @@ -758,7 +797,9 @@ func TestRemoteDelivery_Split_BodyErr(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { @@ -814,7 +855,9 @@ func TestRemoteDelivery_Split_BodyErr_NonAtomic(t *testing.T) { } tgt := testTarget(t, zones, nil, nil) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() delivery, err := tgt.StartDelivery(context.Background(), &module.MsgMetadata{ID: "test..."}, "test@example.com") if err != nil { @@ -874,7 +917,9 @@ func TestRemoteDelivery_TLSErrFallback(t *testing.T) { tgt := testTarget(t, zones, nil, nil) tgt.tlsConfig = clientCfg - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -896,7 +941,9 @@ func TestRemoteDelivery_RequireTLS_Missing(t *testing.T) { tgt := testTarget(t, zones, nil, []module.MXAuthPolicy{ &localPolicy{minTLSLevel: module.TLSEncrypted}, }) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -921,7 +968,9 @@ func TestRemoteDelivery_RequireTLS_Present(t *testing.T) { &localPolicy{minTLSLevel: module.TLSEncrypted}, }) tgt.tlsConfig = clientCfg - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -950,7 +999,9 @@ func TestRemoteDelivery_RequireTLS_NoErrFallback(t *testing.T) { &localPolicy{minTLSLevel: module.TLSEncrypted}, }) tgt.tlsConfig = clientCfg - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -975,7 +1026,9 @@ func TestRemoteDelivery_TLS_FallbackNoVerify(t *testing.T) { tgt := testTarget(t, zones, nil, []module.MXAuthPolicy{ &localPolicy{minTLSLevel: module.TLSEncrypted}, }) - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -1008,7 +1061,9 @@ func TestRemoteDelivery_TLS_FallbackPlaintext(t *testing.T) { tgt := testTarget(t, zones, nil, nil) tgt.tlsConfig = clientCfg - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -1041,7 +1096,9 @@ func TestRemoteDelivery_ConnReuse(t *testing.T) { tgt := testTarget(t, zones, nil, nil) tgt.connReuseLimit = 5 - defer tgt.Stop() + defer func() { + assert.NoError(t, tgt.Stop()) + }() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) From b2067816f1f1b1a0228a0c6b972cf013a5714ff4 Mon Sep 17 00:00:00 2001 From: denis Date: 2026年3月14日 10:40:44 +0200 Subject: [PATCH 140/171] Append openmetrics endpoints. --- internal/endpoint/openmetrics/om.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/endpoint/openmetrics/om.go b/internal/endpoint/openmetrics/om.go index e7e4a791..4c917835 100644 --- a/internal/endpoint/openmetrics/om.go +++ b/internal/endpoint/openmetrics/om.go @@ -68,6 +68,7 @@ func (e *Endpoint) Configure(inlineArgs []string, cfg *config.Map) error { if endp.IsTLS() { return fmt.Errorf("%s: TLS is not supported yet", modName) } + e.endpoints = append(e.endpoints, endp) } return nil From 77dbb50022d1f9f9fd3c1ffbb28db8665d75f768 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年3月25日 01:34:57 +0300 Subject: [PATCH 141/171] Upgrade to golangci-lint v2, massive linter clean-up --- .github/workflows/test.yml | 25 ++-- .golangci.yml | 14 +- config.go | 4 +- framework/cfgparser/imports.go | 2 +- framework/cfgparser/parse_test.go | 6 +- framework/dns/dnssec_test.go | 9 +- framework/resource/netresource/fd.go | 12 +- framework/resource/singleton.go | 8 +- internal/auth/dovecot_sasl/dovecot_sasl.go | 10 +- internal/auth/ldap/ldap.go | 15 ++- internal/auth/sasl.go | 2 +- internal/auth/shadow/module.go | 4 +- internal/check/rspamd/rspamd.go | 6 +- internal/cli/app.go | 18 ++- internal/cli/ctl/hash.go | 2 +- internal/cli/ctl/users.go | 12 +- internal/dmarc/verifier_test.go | 5 +- internal/dsn/dsn.go | 7 +- .../endpoint/dovecot_sasld/dovecot_sasl.go | 5 +- internal/endpoint/imap/imap.go | 4 +- internal/endpoint/smtp/smtp_test.go | 5 +- internal/endpoint/smtp/smtputf8_test.go | 5 +- internal/modify/dkim/dkim.go | 10 +- internal/modify/dkim/keys.go | 6 +- internal/modify/group.go | 5 +- internal/msgpipeline/check_runner.go | 12 +- internal/msgpipeline/module.go | 2 +- internal/msgpipeline/msgpipeline.go | 24 +++- internal/smtpconn/pool/pool.go | 26 +++- internal/smtpconn/smtpconn.go | 18 ++- internal/smtpconn/smtputf8_test.go | 9 +- internal/storage/blob/fs/fs_test.go | 3 +- internal/storage/blob/s3/s3.go | 4 +- internal/storage/imapsql/imapsql.go | 8 +- internal/table/file_test.go | 5 +- internal/table/sql_query.go | 12 +- internal/target/queue/queue.go | 32 ++++- internal/target/queue/queue_test.go | 26 ++-- internal/target/received.go | 2 +- internal/target/remote/connect.go | 30 +++-- internal/target/remote/dane_delivery_test.go | 98 ++++++++++---- internal/target/remote/mxauth_test.go | 69 +++++++--- internal/target/remote/remote.go | 12 +- internal/target/remote/remote_test.go | 125 +++++++++++++----- internal/target/remote/security.go | 8 +- internal/target/smtp/sasl_test.go | 17 ++- internal/target/smtp/smtp_downstream.go | 31 +++-- internal/target/smtp/smtp_downstream_test.go | 33 +++-- internal/target/smtp/smtputf8_test.go | 5 +- internal/testutils/bench_delivery.go | 2 +- internal/testutils/smtp_server.go | 15 +-- internal/testutils/target.go | 18 ++- internal/updatepipe/pubsub/pq.go | 8 +- internal/updatepipe/unix_pipe.go | 12 +- maddy.go | 27 +++- systemd.go | 12 +- tests/t.go | 12 +- 57 files changed, 656 insertions(+), 262 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 32c02e54..4cf136f1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,24 +17,23 @@ jobs: name: Lint runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 with: go-version-file: 'go.mod' - name: "Install libpam" run: | sudo apt-get update sudo apt-get install -y libpam-dev - - uses: golangci/golangci-lint-action@v6 + - uses: golangci/golangci-lint-action@v9 with: - version: v1.60 - args: "--timeout=30m" + version: v2.11 buildsh: name: "Verify build.sh" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 with: go-version-file: 'go.mod' - name: "Install libpam" @@ -50,8 +49,8 @@ jobs: name: "Build and test" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 with: go-version-file: 'go.mod' - name: "Install libpam" @@ -65,11 +64,3 @@ jobs: run: | cd tests/ ./run.sh - - uses: codecov/codecov-action@v2 - with: - files: ./coverage.out - flags: unit - - uses: codecov/codecov-action@v2 - with: - files: ./tests/coverage.out - flags: integration diff --git a/.golangci.yml b/.golangci.yml index 8617544c..9200934a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,13 +1,11 @@ +version: "2" linters: enable: - - gosimple - errcheck - staticcheck - ineffassign - - typecheck - govet - unused - - goimports - prealloc - unconvert - misspell @@ -15,3 +13,13 @@ linters: - nakedret - dogsled - copyloopvar + - sqlclosecheck + - testifylint + - rowserrcheck + - recvcheck + settings: + errcheck: + disable-default-exclusions: false +formatters: + enable: + - goimports diff --git a/config.go b/config.go index 2f57b472..92053f94 100644 --- a/config.go +++ b/config.go @@ -114,7 +114,9 @@ func reinitLogging() { return } - out.Close() + if err := out.Close(); err != nil { + log.Println("Can't close logger:", err) + } log.DefaultLogger.Out = newOut } diff --git a/framework/cfgparser/imports.go b/framework/cfgparser/imports.go index 8078dd8d..1f9dccd1 100644 --- a/framework/cfgparser/imports.go +++ b/framework/cfgparser/imports.go @@ -169,7 +169,7 @@ func (ctx *parseContext) expandSingleValueMacro(arg string) (string, error) { value = ctx.macros[macroName][0] } - arg = strings.Replace(arg, "$("+macroName+")", value, -1) + arg = strings.ReplaceAll(arg, "$("+macroName+")", value) } return arg, nil diff --git a/framework/cfgparser/parse_test.go b/framework/cfgparser/parse_test.go index 32929b9f..3cf20bae 100644 --- a/framework/cfgparser/parse_test.go +++ b/framework/cfgparser/parse_test.go @@ -23,6 +23,8 @@ import ( "reflect" "strings" "testing" + + "github.com/stretchr/testify/require" ) var cases = []struct { @@ -579,8 +581,8 @@ func printTree(t *testing.T, root Node, indent int) { } func TestRead(t *testing.T) { - os.Setenv("TESTING_VARIABLE", "ABCDEF") - os.Setenv("TESTING_VARIABLE2", "ABC2 DEF2") + require.NoError(t, os.Setenv("TESTING_VARIABLE", "ABCDEF")) + require.NoError(t, os.Setenv("TESTING_VARIABLE2", "ABC2 DEF2")) for _, case_ := range cases { t.Run(case_.name, func(t *testing.T) { diff --git a/framework/dns/dnssec_test.go b/framework/dns/dnssec_test.go index 774897bf..325b14fe 100644 --- a/framework/dns/dnssec_test.go +++ b/framework/dns/dnssec_test.go @@ -11,6 +11,7 @@ import ( "github.com/foxcpp/maddy/framework/log" "github.com/miekg/dns" + "github.com/stretchr/testify/require" ) type TestSrvAction int @@ -55,8 +56,8 @@ func (s *IPAddrTestServer) Run() { go s.udpServ.ActivateAndServe() //nolint:errcheck } -func (s *IPAddrTestServer) Close() { - s.udpServ.PacketConn.Close() +func (s *IPAddrTestServer) Close() error { + return s.udpServ.PacketConn.Close() } func (s *IPAddrTestServer) Addr() *net.UDPAddr { @@ -141,7 +142,9 @@ func TestExtResolver_AuthLookupIPAddr(t *testing.T) { s.aAD = aAD s.aaaaAD = aaaaAD s.Run() - defer s.Close() + defer func() { + require.NoError(t, s.Close()) + }() res := ExtResolver{ cl: new(dns.Client), Cfg: &dns.ClientConfig{ diff --git a/framework/resource/netresource/fd.go b/framework/resource/netresource/fd.go index 395ddbcc..8c2d881a 100644 --- a/framework/resource/netresource/fd.go +++ b/framework/resource/netresource/fd.go @@ -11,7 +11,11 @@ import ( func ListenFD(fd uint) (net.Listener, error) { file := os.NewFile(uintptr(fd), strconv.FormatUint(uint64(fd), 10)) - defer file.Close() + defer func() { + if err := file.Close(); err != nil { + panic(err) + } + }() return net.FileListener(file) } @@ -42,6 +46,10 @@ func ListenFDName(name string) (net.Listener, error) { } file := os.NewFile(3+fd, name) - defer file.Close() + defer func() { + if err := file.Close(); err != nil { + panic(err) + } + }() return net.FileListener(file) } diff --git a/framework/resource/singleton.go b/framework/resource/singleton.go index 45f5b903..33b71db5 100644 --- a/framework/resource/singleton.go +++ b/framework/resource/singleton.go @@ -50,8 +50,10 @@ func (s *Singleton[T]) CloseUnused(isUsed func(key string) bool) error { if isUsed(key) { continue } + if err := res.Close(); err != nil { + s.log.Error("resource close failed", err, "key", key) + } s.log.DebugMsg("resource released", "key", key) - res.Close() delete(s.resources, key) } @@ -63,8 +65,10 @@ func (s *Singleton[T]) Close() error { defer s.lock.Unlock() for key, res := range s.resources { + if err := res.Close(); err != nil { + s.log.Error("resource close failed", err, "key", key) + } s.log.DebugMsg("resource released", "key", key) - res.Close() delete(s.resources, key) } diff --git a/internal/auth/dovecot_sasl/dovecot_sasl.go b/internal/auth/dovecot_sasl/dovecot_sasl.go index cc5dd370..fc0585e6 100644 --- a/internal/auth/dovecot_sasl/dovecot_sasl.go +++ b/internal/auth/dovecot_sasl/dovecot_sasl.go @@ -77,7 +77,9 @@ func (a *Auth) getConn() (*dovecotsasl.Client, error) { } func (a *Auth) returnConn(cl *dovecotsasl.Client) { - cl.Close() + if err := cl.Close(); err != nil { + a.log.Error("connection close failed", err) + } } func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { @@ -113,7 +115,11 @@ func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { return fmt.Errorf("%s: unable to contact server: %v", modName, err) } - defer cl.Close() + defer func() { + if err := cl.Close(); err != nil { + a.log.Error("connection close failed", err) + } + }() a.mechanisms = make(map[string]dovecotsasl.Mechanism, len(cl.ConnInfo().Mechs)) for name, mech := range cl.ConnInfo().Mechs { if mech.Private { diff --git a/internal/auth/ldap/ldap.go b/internal/auth/ldap/ldap.go index a2392d56..88cc0b4d 100644 --- a/internal/auth/ldap/ldap.go +++ b/internal/auth/ldap/ldap.go @@ -181,7 +181,9 @@ func (a *Auth) getConn() (*ldap.Conn, error) { a.conn = conn } if a.conn.IsClosing() { - a.conn.Close() + if err := a.conn.Close(); err != nil { + a.log.Error("Connection close failed", err) + } conn, err := a.newConn() if err != nil { a.connLock.Unlock() @@ -196,11 +198,15 @@ func (a *Auth) returnConn(conn *ldap.Conn) { defer a.connLock.Unlock() if err := a.readBind(conn); err != nil { a.log.Error("failed to rebind for reading", err) - conn.Close() + if err := a.conn.Close(); err != nil { + a.log.Error("Connection close failed", err) + } a.conn = nil } if a.conn != conn { - a.conn.Close() + if err := a.conn.Close(); err != nil { + a.log.Error("Connection close failed", err) + } } a.conn = conn } @@ -285,8 +291,7 @@ func (a *Auth) Start() error { func (a *Auth) Stop() error { a.connLock.Lock() defer a.connLock.Unlock() - a.conn.Close() - return nil + return a.conn.Close() } func init() { diff --git a/internal/auth/sasl.go b/internal/auth/sasl.go index b29959b0..616ad618 100644 --- a/internal/auth/sasl.go +++ b/internal/auth/sasl.go @@ -34,7 +34,7 @@ import ( ) var ( - ErrUnsupportedMech = errors.New("Unsupported SASL mechanism") + ErrUnsupportedMech = errors.New("unsupported SASL mechanism") ErrInvalidAuthCred = errors.New("auth: invalid credentials") ) diff --git a/internal/auth/shadow/module.go b/internal/auth/shadow/module.go index 290d826e..90a65a23 100644 --- a/internal/auth/shadow/module.go +++ b/internal/auth/shadow/module.go @@ -80,7 +80,9 @@ func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { } return fmt.Errorf("shadow: can't read /etc/shadow: %v", err) } - f.Close() + if err := f.Close(); err != nil { + a.Log.Error("can't close /etc/shadow file", err) + } } return nil diff --git a/internal/check/rspamd/rspamd.go b/internal/check/rspamd/rspamd.go index ede0c151..0a737b69 100644 --- a/internal/check/rspamd/rspamd.go +++ b/internal/check/rspamd/rspamd.go @@ -277,7 +277,11 @@ func (s *state) CheckBody(ctx context.Context, hdr textproto.Header, body buffer }, }) } - defer resp.Body.Close() + defer func() { + if err := resp.Body.Close(); err != nil { + s.log.Error("failed to close response body", err) + } + }() var respData response if err := json.NewDecoder(resp.Body).Decode(&respData); err != nil { diff --git a/internal/cli/app.go b/internal/cli/app.go index 59108965..55ab1862 100644 --- a/internal/cli/app.go +++ b/internal/cli/app.go @@ -1,6 +1,7 @@ package maddycli import ( + "errors" "fmt" "os" "strings" @@ -28,7 +29,22 @@ databases used by it (all other subcommands). }, } app.ExitErrHandler = func(c *cli.Context, err error) { - cli.HandleExitCoder(err) + if err == nil { + return + } + + var exitErr cli.ExitCoder + if errors.As(err, &exitErr) { + if err.Error() != "" { + if _, ok := exitErr.(cli.ErrorFormatter); ok { + _, _ = fmt.Fprintf(os.Stderr, "Error: %+v\n", err) + } else { + _, _ = fmt.Fprintln(os.Stderr, "Error:", err) + } + } + cli.OsExiter(exitErr.ExitCode()) + return + } } app.EnableBashCompletion = true app.Commands = []*cli.Command{ diff --git a/internal/cli/ctl/hash.go b/internal/cli/ctl/hash.go index 5effdf7e..c4371780 100644 --- a/internal/cli/ctl/hash.go +++ b/internal/cli/ctl/hash.go @@ -79,7 +79,7 @@ func hashCommand(ctx *cli.Context) error { hashCompute := pass_table.HashCompute[hashFunc] if hashCompute == nil { - var funcs []string + funcs := make([]string, 0, len(pass_table.HashCompute)) for k := range pass_table.HashCompute { funcs = append(funcs, k) } diff --git a/internal/cli/ctl/users.go b/internal/cli/ctl/users.go index 09dc909c..13bccd79 100644 --- a/internal/cli/ctl/users.go +++ b/internal/cli/ctl/users.go @@ -37,14 +37,14 @@ func init() { &cli.Command{ Name: "creds", Usage: "Local credentials management", - Description: `These commands manipulate credential databases used by + Description: `These commands manipulate credential databases used by maddy mail server. Corresponding credential database should be defined in maddy.conf as a top-level config block. By default the block name should be local_authdb ( can be changed using --cfg-block argument for subcommands). -Note that it is not enough to create user credentials in order to grant +Note that it is not enough to create user credentials in order to grant IMAP access - IMAP account should be also created using 'imap-acct create' subcommand. `, Subcommands: []*cli.Command{ @@ -74,7 +74,7 @@ IMAP access - IMAP account should be also created using 'imap-acct create' subco Description: `Reads password from stdin. If configuration block uses auth.pass_table, then hash algorithm can be configured -using command flags. Otherwise, these options cannot be used. +using command flags. Otherwise, these options cannot be used. `, ArgsUsage: "USERNAME", Flags: []cli.Flag{ @@ -213,12 +213,12 @@ func usersCreate(be module.PlainUserDB, ctx *cli.Context) error { func usersRemove(be module.PlainUserDB, ctx *cli.Context) error { username := ctx.Args().First() if username == "" { - return errors.New("Error: USERNAME is required") + return errors.New("error: USERNAME is required") } if !ctx.Bool("yes") { if !clitools2.Confirmation("Are you sure you want to delete this user account?", false) { - return errors.New("Cancelled") + return errors.New("cancelled") } } @@ -228,7 +228,7 @@ func usersRemove(be module.PlainUserDB, ctx *cli.Context) error { func usersPassword(be module.PlainUserDB, ctx *cli.Context) error { username := ctx.Args().First() if username == "" { - return errors.New("Error: USERNAME is required") + return errors.New("error: USERNAME is required") } var pass string diff --git a/internal/dmarc/verifier_test.go b/internal/dmarc/verifier_test.go index 1adfe996..911cdae0 100644 --- a/internal/dmarc/verifier_test.go +++ b/internal/dmarc/verifier_test.go @@ -29,13 +29,16 @@ import ( "github.com/emersion/go-message/textproto" "github.com/emersion/go-msgauth/authres" "github.com/foxcpp/go-mockdns" + "github.com/stretchr/testify/require" ) func TestDMARC(t *testing.T) { test := func(zones map[string]mockdns.Zone, hdr string, authres []authres.Result, policyApplied Policy, dmarcRes authres.ResultValue) { t.Helper() v := NewVerifier(&mockdns.Resolver{Zones: zones}) - defer v.Close() + defer func() { + require.NoError(t, v.Close()) + }() hdrParsed, err := textproto.ReadHeader(bufio.NewReader(strings.NewReader(hdr))) if err != nil { diff --git a/internal/dsn/dsn.go b/internal/dsn/dsn.go index 59707a7f..db2e9c3d 100644 --- a/internal/dsn/dsn.go +++ b/internal/dsn/dsn.go @@ -202,15 +202,16 @@ func GenerateDSN(utf8 bool, envelope Envelope, mtaInfo ReportingMTAInfo, rcptsIn reportHeader.Add("From", envelope.From) reportHeader.Add("Subject", "Undelivered Mail Returned to Sender") - defer partWriter.Close() - if err := writeHumanReadablePart(partWriter, mtaInfo, rcptsInfo); err != nil { return textproto.Header{}, err } if err := writeMachineReadablePart(utf8, partWriter, mtaInfo, rcptsInfo); err != nil { return textproto.Header{}, err } - return reportHeader, writeHeader(utf8, partWriter, failedHeader) + if err := writeHeader(utf8, partWriter, failedHeader); err != nil { + return textproto.Header{}, err + } + return reportHeader, partWriter.Close() } func writeHeader(utf8 bool, w *textproto.MultipartWriter, header textproto.Header) error { diff --git a/internal/endpoint/dovecot_sasld/dovecot_sasl.go b/internal/endpoint/dovecot_sasld/dovecot_sasl.go index 74a452b2..4bb7a942 100644 --- a/internal/endpoint/dovecot_sasld/dovecot_sasl.go +++ b/internal/endpoint/dovecot_sasld/dovecot_sasl.go @@ -128,9 +128,8 @@ func (endp *Endpoint) Start() error { } func (endp *Endpoint) Stop() error { - endp.srv.Close() - endp.listenersWg.Wait() - return nil + defer endp.listenersWg.Wait() + return endp.srv.Close() } func init() { diff --git a/internal/endpoint/imap/imap.go b/internal/endpoint/imap/imap.go index b73908d8..1a64ca3d 100644 --- a/internal/endpoint/imap/imap.go +++ b/internal/endpoint/imap/imap.go @@ -219,7 +219,9 @@ func (endp *Endpoint) InstanceName() string { func (endp *Endpoint) Stop() error { for _, l := range endp.listeners { - l.Close() + if err := l.Close(); err != nil { + endp.Log.Error("failed to close listener", err) + } } if err := endp.serv.Close(); err != nil { return err diff --git a/internal/endpoint/smtp/smtp_test.go b/internal/endpoint/smtp/smtp_test.go index 5fc79703..ee87efee 100644 --- a/internal/endpoint/smtp/smtp_test.go +++ b/internal/endpoint/smtp/smtp_test.go @@ -38,6 +38,7 @@ import ( "github.com/foxcpp/maddy/internal/msgpipeline" "github.com/foxcpp/maddy/internal/testutils" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var testPort string @@ -439,7 +440,7 @@ func TestSMTPDelivery_AbortData(t *testing.T) { } // Then.. Suddenly, close the connection without sending the final dot. - assert.NoError(t, cl.Close()) + require.NoError(t, cl.Close()) time.Sleep(250 * time.Millisecond) @@ -517,7 +518,7 @@ func TestSMTPDelivery_AbortLogout(t *testing.T) { } // Then.. Suddenly, close the connection. - assert.NoError(t, cl.Close()) + require.NoError(t, cl.Close()) time.Sleep(250 * time.Millisecond) diff --git a/internal/endpoint/smtp/smtputf8_test.go b/internal/endpoint/smtp/smtputf8_test.go index ff16be6c..684f07c4 100644 --- a/internal/endpoint/smtp/smtputf8_test.go +++ b/internal/endpoint/smtp/smtputf8_test.go @@ -28,6 +28,7 @@ import ( "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/testutils" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestSMTPUTF8_MangleStatusMessage(t *testing.T) { @@ -87,7 +88,9 @@ func TestSMTP_RejectNonASCIIFrom(t *testing.T) { if err != nil { t.Fatal(err) } - defer cl.Close() + defer func() { + require.NoError(t, cl.Close()) + }() err = submitMsg(t, cl, "ѣ@example.org", []string{"rcpt@example.com"}, testMsg) diff --git a/internal/modify/dkim/dkim.go b/internal/modify/dkim/dkim.go index f0894931..b7864cb2 100644 --- a/internal/modify/dkim/dkim.go +++ b/internal/modify/dkim/dkim.go @@ -270,7 +270,7 @@ func (s *state) RewriteSender(ctx context.Context, mailFrom string) (string, err return mailFrom, nil } -func (s state) RewriteRcpt(ctx context.Context, rcptTo string) ([]string, error) { +func (s *state) RewriteRcpt(ctx context.Context, rcptTo string) ([]string, error) { return []string{rcptTo}, nil } @@ -341,16 +341,16 @@ func (s *state) RewriteBody(ctx context.Context, h *textproto.Header, body buffe return exterrors.WithFields(err, map[string]interface{}{"modifier": "modify.dkim"}) } if err := textproto.WriteHeader(signer, *h); err != nil { - signer.Close() + _ = signer.Close() return exterrors.WithFields(err, map[string]interface{}{"modifier": "modify.dkim"}) } r, err := body.Open() if err != nil { - signer.Close() + _ = signer.Close() return exterrors.WithFields(err, map[string]interface{}{"modifier": "modify.dkim"}) } if _, err := io.Copy(signer, r); err != nil { - signer.Close() + _ = signer.Close() return exterrors.WithFields(err, map[string]interface{}{"modifier": "modify.dkim"}) } @@ -365,7 +365,7 @@ func (s *state) RewriteBody(ctx context.Context, h *textproto.Header, body buffe return nil } -func (s state) Close() error { +func (s *state) Close() error { return nil } diff --git a/internal/modify/dkim/keys.go b/internal/modify/dkim/keys.go index 7c39b76e..2a9b7514 100644 --- a/internal/modify/dkim/keys.go +++ b/internal/modify/dkim/keys.go @@ -42,7 +42,11 @@ func (m *Modifier) loadOrGenerateKey(keyPath, newKeyAlgo string) (pkey crypto.Si } return nil, false, err } - defer f.Close() + defer func() { + if err := f.Close(); err != nil { + m.log.Error("failed to close key file", err) + } + }() pemBlob, err := io.ReadAll(f) if err != nil { diff --git a/internal/modify/group.go b/internal/modify/group.go index 00fbe124..26e80408 100644 --- a/internal/modify/group.go +++ b/internal/modify/group.go @@ -25,6 +25,7 @@ import ( "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" ) @@ -71,7 +72,9 @@ func (g *Group) ModStateForMsg(ctx context.Context, msgMeta *module.MsgMetadata) if err != nil { // Free state objects we initialized already. for _, state := range gs.states { - state.Close() + if err := state.Close(); err != nil { + log.DefaultLogger.Error("failed to close modifier state", err) + } } return nil, err } diff --git a/internal/msgpipeline/check_runner.go b/internal/msgpipeline/check_runner.go index f6df9525..7f3a2cb7 100644 --- a/internal/msgpipeline/check_runner.go +++ b/internal/msgpipeline/check_runner.go @@ -73,7 +73,9 @@ func (cr *checkRunner) checkStates(ctx context.Context, checks []module.Check) ( newStatesMap := make(map[module.Check]module.CheckState, len(checks)) closeStates := func() { for _, state := range states { - state.Close() + if err := state.Close(); err != nil { + cr.log.Error("failed to close check state", err) + } } } @@ -342,8 +344,12 @@ func (cr *checkRunner) applyResults(hostname string, header *textproto.Header) e } func (cr *checkRunner) close() { - cr.dmarcVerify.Close() + if err := cr.dmarcVerify.Close(); err != nil { + cr.log.Error("failed to close dmarc verify state", err) + } for _, state := range cr.states { - state.Close() + if err := state.Close(); err != nil { + cr.log.Error("failed to close check state", err) + } } } diff --git a/internal/msgpipeline/module.go b/internal/msgpipeline/module.go index a951ec88..29cd4a8f 100644 --- a/internal/msgpipeline/module.go +++ b/internal/msgpipeline/module.go @@ -52,7 +52,7 @@ func (m *Module) Configure(inlineArgs []string, cfg *config.Map) error { return err } m.MsgPipeline = p - m.MsgPipeline.Log = m.log + m.Log = m.log return nil } diff --git a/internal/msgpipeline/msgpipeline.go b/internal/msgpipeline/msgpipeline.go index 5caa0899..e15c49d5 100644 --- a/internal/msgpipeline/msgpipeline.go +++ b/internal/msgpipeline/msgpipeline.go @@ -186,7 +186,9 @@ func (dd *msgpipelineDelivery) initRunGlobalModifiers(ctx context.Context, msgMe } mailFrom, err = globalModifiersState.RewriteSender(ctx, mailFrom) if err != nil { - globalModifiersState.Close() + if err := globalModifiersState.Close(); err != nil { + dd.log.Error("failed to close global modifiers state", err) + } return "", err } dd.globalModifiersState = globalModifiersState @@ -333,7 +335,9 @@ func (dd *msgpipelineDelivery) AddRcpt(ctx context.Context, to string, opts smtp newTo, err = rcptModifiersState.RewriteRcpt(ctx, to) if err != nil { - rcptModifiersState.Close() + if err := rcptModifiersState.Close(); err != nil { + dd.log.Error("failed to close rcpt modifiers state", err) + } return wrapErr(err) } dd.log.Debugln("per-rcpt modifiers:", to, "=>", newTo) @@ -501,7 +505,7 @@ func (dd *msgpipelineDelivery) BodyNonAtomic(ctx context.Context, c module.Statu } } -func (dd msgpipelineDelivery) Commit(ctx context.Context) error { +func (dd *msgpipelineDelivery) Commit(ctx context.Context) error { dd.close() for _, delivery := range dd.deliveries { @@ -517,17 +521,23 @@ func (dd *msgpipelineDelivery) close() { dd.checkRunner.close() if dd.globalModifiersState != nil { - dd.globalModifiersState.Close() + if err := dd.globalModifiersState.Close(); err != nil { + dd.log.Error("failed to close global modifiers state", err) + } } if dd.sourceModifiersState != nil { - dd.sourceModifiersState.Close() + if err := dd.sourceModifiersState.Close(); err != nil { + dd.log.Error("failed to close source modifiers state", err) + } } for _, modifiers := range dd.rcptModifiersState { - modifiers.Close() + if err := modifiers.Close(); err != nil { + dd.log.Error("failed to close rcpt modifiers state", err) + } } } -func (dd msgpipelineDelivery) Abort(ctx context.Context) error { +func (dd *msgpipelineDelivery) Abort(ctx context.Context) error { dd.close() var lastErr error diff --git a/internal/smtpconn/pool/pool.go b/internal/smtpconn/pool/pool.go index 4b700ee4..aee82fd7 100644 --- a/internal/smtpconn/pool/pool.go +++ b/internal/smtpconn/pool/pool.go @@ -22,6 +22,8 @@ import ( "context" "sync" "time" + + "github.com/foxcpp/maddy/framework/log" ) type Conn interface { @@ -96,12 +98,18 @@ func (p *P) CleanUp(ctx context.Context) { close(v.c) for conn := range v.c { - go conn.Close() + go p.close(conn) } delete(p.keys, k) } } +func (p *P) close(c Conn) { + if err := c.Close(); err != nil { + log.DefaultLogger.Error("failed to close pooled connection", err) + } +} + func (p *P) Get(ctx context.Context, key string) (Conn, error) { p.keysLock.Lock() @@ -120,7 +128,7 @@ func (p *P) Get(ctx context.Context, key string) (Conn, error) { p.keysLock.Unlock() for conn := range bucket.c { - conn.Close() + p.close(conn) } return p.cfg.New(ctx, key) @@ -141,11 +149,15 @@ func (p *P) Get(ctx context.Context, key string) (Conn, error) { if !conn.Usable() { // Close might take some time, run in parallel. - go conn.Close() + go p.close(conn) continue } if conn.LastUseAt().Add(time.Duration(p.cfg.MaxConnLifetimeSec) * time.Second).Before(time.Now()) { - go conn.Close() + go func() { + if err := conn.Close(); err != nil { + log.DefaultLogger.Error("failed to close pooled connection", err) + } + }() continue } @@ -173,7 +185,7 @@ func (p *P) Return(key string, c Conn) { close(v.c) for conn := range v.c { - conn.Close() + p.close(conn) } } } @@ -190,7 +202,7 @@ func (p *P) Return(key string, c Conn) { bucket.lastUse = time.Now().Unix() default: // Let it go, let it go... - go c.Close() + go p.close(c) } } @@ -203,7 +215,7 @@ func (p *P) Close() { for k, v := range p.keys { close(v.c) for conn := range v.c { - conn.Close() + p.close(conn) } delete(p.keys, k) } diff --git a/internal/smtpconn/smtpconn.go b/internal/smtpconn/smtpconn.go index 3c4b4224..25fd49a5 100644 --- a/internal/smtpconn/smtpconn.go +++ b/internal/smtpconn/smtpconn.go @@ -228,6 +228,12 @@ func (c *C) RemoteAddr() net.Addr { return c.conn.RemoteAddr() } +func (c *C) closeClient(cl *smtp.Client) { + if err := cl.Close(); err != nil { + c.Log.Error("client connection close failed", err) + } +} + func (c *C) attemptConnect(ctx context.Context, lmtp bool, endp config.Endpoint, starttls bool, tlsConfig *tls.Config) (didTLS bool, cl *smtp.Client, conn net.Conn, err error) { dialCtx, cancel := context.WithTimeout(ctx, c.ConnectTimeout) conn, err = c.Dialer(dialCtx, endp.Network(), endp.Address()) @@ -255,7 +261,7 @@ func (c *C) attemptConnect(ctx context.Context, lmtp bool, endp config.Endpoint, // i18n: hostname is already expected to be in A-labels form. if err := cl.Hello(c.Hostname); err != nil { - cl.Close() + c.closeClient(cl) return false, nil, nil, err } @@ -265,7 +271,7 @@ func (c *C) attemptConnect(ctx context.Context, lmtp bool, endp config.Endpoint, if ok, _ := cl.Extension("STARTTLS"); !ok { if err := cl.Quit(); err != nil { - cl.Close() + c.closeClient(cl) } return false, nil, nil, fmt.Errorf("TLS required but unsupported by downstream") } @@ -278,7 +284,7 @@ func (c *C) attemptConnect(ctx context.Context, lmtp bool, endp config.Endpoint, // *after* the handshake (e.g. PKI verification fail), we don't log the error in // this case though. if err := cl.Quit(); err != nil { - cl.Close() + c.closeClient(cl) } return false, nil, nil, TLSError{err} @@ -286,7 +292,7 @@ func (c *C) attemptConnect(ctx context.Context, lmtp bool, endp config.Endpoint, // Re-do HELO using our hostname instead of localhost. if err := cl.Hello(c.Hostname); err != nil { - cl.Close() + c.closeClient(cl) var tlsErr *tls.CertificateVerificationError if errors.As(err, &tlsErr) { @@ -551,8 +557,8 @@ func (c *C) Close() error { // DirectClose closes the underlying connection without sending the QUIT // command. func (c *C) DirectClose() error { - c.cl.Close() + cl := c.cl c.cl = nil c.serverName = "" - return nil + return cl.Close() } diff --git a/internal/smtpconn/smtputf8_test.go b/internal/smtpconn/smtputf8_test.go index dc580d9c..44efe4fd 100644 --- a/internal/smtpconn/smtputf8_test.go +++ b/internal/smtpconn/smtputf8_test.go @@ -28,6 +28,7 @@ import ( "github.com/foxcpp/maddy/framework/config" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/internal/testutils" + "github.com/stretchr/testify/require" ) func doTestDelivery(t *testing.T, conn *C, from string, to []string, opts smtp.MailOptions) error { @@ -65,7 +66,9 @@ func TestSMTPUTF8(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+testPort) srv.EnableSMTPUTF8 = case_.serverUTF8 - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) c := New() @@ -77,7 +80,9 @@ func TestSMTPUTF8(t *testing.T) { }, false, nil); err != nil { t.Fatal(err) } - defer c.Close() + defer func() { + require.NoError(t, c.Close()) + }() err := doTestDelivery(t, c, case_.clientSender, []string{case_.clientRcpt}, smtp.MailOptions{UTF8: true}) diff --git a/internal/storage/blob/fs/fs_test.go b/internal/storage/blob/fs/fs_test.go index 2c8f7664..ec4635e4 100644 --- a/internal/storage/blob/fs/fs_test.go +++ b/internal/storage/blob/fs/fs_test.go @@ -7,6 +7,7 @@ import ( "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/storage/blob" "github.com/foxcpp/maddy/internal/testutils" + "github.com/stretchr/testify/require" ) func TestFS(t *testing.T) { @@ -14,6 +15,6 @@ func TestFS(t *testing.T) { dir := testutils.Dir(t) return &FSStore{instName: "test", root: dir} }, func(store module.BlobStore) { - os.RemoveAll(store.(*FSStore).root) + require.NoError(t, os.RemoveAll(store.(*FSStore).root)) }) } diff --git a/internal/storage/blob/s3/s3.go b/internal/storage/blob/s3/s3.go index 075c8368..b470e2d2 100644 --- a/internal/storage/blob/s3/s3.go +++ b/internal/storage/blob/s3/s3.go @@ -120,7 +120,9 @@ func (b *s3blob) Sync() error { panic("storage.blob.s3: Sync called twice for a blob object") } - b.pw.Close() + if err := b.pw.Close(); err != nil { + return err + } b.didSync = true return <-b.errch } diff --git a/internal/storage/imapsql/imapsql.go b/internal/storage/imapsql/imapsql.go index 89657aaf..2a335b6a 100644 --- a/internal/storage/imapsql/imapsql.go +++ b/internal/storage/imapsql/imapsql.go @@ -428,7 +428,9 @@ func (store *Storage) Lookup(ctx context.Context, key string) (string, bool, err func (store *Storage) Stop() error { // Stop backend from generating new updates. - store.Back.Close() + if err := store.Back.Close(); err != nil { + store.Log.Error("close backend failed", err) + } // Wait for 'updates replicate' goroutine to actually stop so we will send // all updates before shutting down (this is especially important for @@ -437,7 +439,9 @@ func (store *Storage) Stop() error { close(store.outboundUpds) <-store.updpushstop - store.updPipe.Close() + if err := store.updPipe.Close(); err != nil { + store.Log.Error("updatepipe close failed", err) + } } return nil diff --git a/internal/table/file_test.go b/internal/table/file_test.go index 50d2e1cc..abfc81f8 100644 --- a/internal/table/file_test.go +++ b/internal/table/file_test.go @@ -27,6 +27,7 @@ import ( "github.com/foxcpp/maddy/framework/config" "github.com/foxcpp/maddy/internal/testutils" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestReadFile(t *testing.T) { @@ -173,10 +174,10 @@ func TestFileReload_Broken(t *testing.T) { } }(f.Name()) if _, err := f.WriteString(file); err != nil { - assert.NoError(t, f.Close()) + require.NoError(t, f.Close()) t.Fatal(err) } - assert.NoError(t, f.Close()) + require.NoError(t, f.Close()) mod, err := NewFile("", "") if err != nil { diff --git a/internal/table/sql_query.go b/internal/table/sql_query.go index ec2c3d23..6fa0f730 100644 --- a/internal/table/sql_query.go +++ b/internal/table/sql_query.go @@ -26,6 +26,7 @@ import ( "strings" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" sqliteprovider "github.com/foxcpp/maddy/internal/sqlite" _ "github.com/lib/pq" @@ -144,7 +145,9 @@ func (s *SQL) Start() error { } func (s *SQL) Stop() error { - s.lookup.Close() + if err := s.lookup.Close(); err != nil { + log.DefaultLogger.Error("lookup query close failed", err) + } return s.db.Close() } @@ -203,7 +206,9 @@ func (s *SQL) Keys() ([]string, error) { if err != nil { return nil, fmt.Errorf("%s: list: %w", s.modName, err) } - defer rows.Close() + defer func() { + _ = rows.Close() + }() var list []string for rows.Next() { var key string @@ -212,6 +217,9 @@ func (s *SQL) Keys() ([]string, error) { } list = append(list, key) } + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("%s: list: %w", s.modName, err) + } return list, nil } diff --git a/internal/target/queue/queue.go b/internal/target/queue/queue.go index 8657110d..11b79683 100644 --- a/internal/target/queue/queue.go +++ b/internal/target/queue/queue.go @@ -109,7 +109,7 @@ func (pe *partialError) SetStatus(rcptTo string, err error) { pe.Errs[rcptTo] = err } -func (pe partialError) Error() string { +func (pe *partialError) Error() string { return fmt.Sprintf("delivery failed for some recipients: %v", pe.Errs) } @@ -758,7 +758,11 @@ func (q *Queue) storeNewMessage(meta *QueueMetadata, header textproto.Header, bo if err != nil { return nil, err } - defer headerFile.Close() + defer func() { + if err := headerFile.Close(); err != nil { + q.Log.Error("header file close failed", err) + } + }() if err := textproto.WriteHeader(headerFile, header); err != nil { q.tryRemoveDanglingFile(id + ".header") @@ -770,14 +774,22 @@ func (q *Queue) storeNewMessage(meta *QueueMetadata, header textproto.Header, bo q.tryRemoveDanglingFile(id + ".header") return nil, err } - defer bodyReader.Close() + defer func() { + if err := bodyReader.Close(); err != nil { + q.Log.Error("bodyReader close failed", err) + } + }() bodyPath := filepath.Join(q.location, id+".body") bodyFile, err := os.Create(bodyPath) if err != nil { return nil, err } - defer bodyFile.Close() + defer func() { + if err := bodyFile.Close(); err != nil { + q.Log.Error("body file close failed", err) + } + }() if _, err := io.Copy(bodyFile, bodyReader); err != nil { q.tryRemoveDanglingFile(id + ".body") @@ -820,7 +832,11 @@ func (q *Queue) updateMetadataOnDisk(meta *QueueMetadata) error { return err } } - defer file.Close() + defer func() { + if err := file.Close(); err != nil { + q.Log.Error("metadata file close failed", err) + } + }() metaCopy := *meta metaCopy.MsgMeta = meta.MsgMeta.DeepCopy() @@ -849,7 +865,11 @@ func (q *Queue) readMessageMeta(id string) (*QueueMetadata, error) { if err != nil { return nil, err } - defer file.Close() + defer func() { + if err := file.Close(); err != nil { + q.Log.Error("metadata file close failed", err) + } + }() meta := &QueueMetadata{} diff --git a/internal/target/queue/queue_test.go b/internal/target/queue/queue_test.go index 6eb7a101..659770b6 100644 --- a/internal/target/queue/queue_test.go +++ b/internal/target/queue/queue_test.go @@ -39,7 +39,7 @@ import ( "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/testutils" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // newTestQueue returns properly initialized Queue object usable for testing. @@ -246,7 +246,7 @@ func TestQueueDelivery(t *testing.T) { // Wait for the delivery to complete and stop processing. msg := readMsgChanTimeout(t, dt.committed, 5*time.Second) - assert.NoError(t, q.Stop()) + require.NoError(t, q.Stop()) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester1@example.org", "tester2@example.org"}, "") @@ -270,7 +270,7 @@ func TestQueueDelivery_PermanentFail_NonPartial(t *testing.T) { // Queue will abort a delivery if it fails for all recipients. readMsgChanTimeout(t, dt.aborted, 5*time.Second) - assert.NoError(t, q.Stop()) + require.NoError(t, q.Stop()) // Delivery is failed permanently, hence no retry should be rescheduled. checkQueueDir(t, q, []string{}) @@ -297,7 +297,7 @@ func TestQueueDelivery_PermanentFail_Partial(t *testing.T) { // Here delivery fails for recipients too, but this is reported using PartialDelivery. readMsgChanTimeout(t, dt.aborted, 5*time.Second) - assert.NoError(t, q.Stop()) + require.NoError(t, q.Stop()) checkQueueDir(t, q, []string{}) } @@ -323,7 +323,7 @@ func TestQueueDelivery_TemporaryFail(t *testing.T) { msg := readMsgChanTimeout(t, dt.committed, 5*time.Second) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester1@example.org", "tester2@example.org"}, "") - assert.NoError(t, q.Stop()) + require.NoError(t, q.Stop()) // No more retries scheduled, queue storage is clear. defer checkQueueDir(t, q, []string{}) } @@ -356,7 +356,7 @@ func TestQueueDelivery_TemporaryFail_Partial(t *testing.T) { msg = readMsgChanTimeout(t, dt.committed, 5000*time.Second) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester2@example.org"}, "") - assert.NoError(t, q.Stop()) + require.NoError(t, q.Stop()) // No more retries scheduled, queue storage is clear. checkQueueDir(t, q, []string{}) } @@ -396,7 +396,7 @@ func TestQueueDelivery_MultipleAttempts(t *testing.T) { msg = readMsgChanTimeout(t, dt.committed, 5*time.Second) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester2@example.org"}, "") - assert.NoError(t, q.Stop()) + require.NoError(t, q.Stop()) // No more retries should be scheduled. checkQueueDir(t, q, []string{}) } @@ -421,7 +421,7 @@ func TestQueueDelivery_PermanentRcptReject(t *testing.T) { msg := readMsgChanTimeout(t, dt.committed, 5*time.Second) testutils.CheckMsgID(t, msg, "tester@example.org", []string{"tester2@example.org"}, "") - assert.NoError(t, q.Stop()) + require.NoError(t, q.Stop()) // No more retries should be scheduled. checkQueueDir(t, q, []string{}) } @@ -455,7 +455,7 @@ func TestQueueDelivery_TemporaryRcptReject(t *testing.T) { msg = readMsgChanTimeout(t, dt.committed, 5*time.Second) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester1@example.org"}, "") - assert.NoError(t, q.Stop()) + require.NoError(t, q.Stop()) // No more retries should be scheduled. checkQueueDir(t, q, []string{}) } @@ -489,7 +489,7 @@ func TestQueueDelivery_SerializationRoundtrip(t *testing.T) { testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester2@example.org"}, "") // Then stop it. - assert.NoError(t, q.Stop()) + require.NoError(t, q.Stop()) // Make sure it is saved. checkQueueDir(t, q, []string{deliveryID}) @@ -502,7 +502,7 @@ func TestQueueDelivery_SerializationRoundtrip(t *testing.T) { testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester1@example.org"}, "") // Close it again. - assert.NoError(t, q.Stop()) + require.NoError(t, q.Stop()) // No more retries should be scheduled. checkQueueDir(t, q, []string{}) } @@ -536,7 +536,7 @@ func TestQueueDelivery_DeserlizationCleanUp(t *testing.T) { msg := readMsgChanTimeout(t, dt.committed, 5*time.Second) testutils.CheckMsgID(t, msg, "tester@example.com", []string{"tester2@example.org"}, "") - assert.NoError(t, q.Stop()) + require.NoError(t, q.Stop()) if err := os.Remove(filepath.Join(q.location, deliveryID+fileSuffix)); err != nil { t.Fatal(err) @@ -544,7 +544,7 @@ func TestQueueDelivery_DeserlizationCleanUp(t *testing.T) { // Dangling files should be removed during load. q = newTestQueueDir(t, &dt, q.location) - assert.NoError(t, q.Stop()) + require.NoError(t, q.Stop()) // Nothing should be left. checkQueueDir(t, q, []string{}) diff --git a/internal/target/received.go b/internal/target/received.go index 051e5a6d..b60bbd14 100644 --- a/internal/target/received.go +++ b/internal/target/received.go @@ -31,7 +31,7 @@ import ( ) func SanitizeForHeader(raw string) string { - return strings.Replace(raw, "\n", "", -1) + return strings.ReplaceAll(raw, "\n", "") } func GenerateReceived(ctx context.Context, msgMeta *module.MsgMetadata, ourHostname, mailFrom string) (string, error) { diff --git a/internal/target/remote/connect.go b/internal/target/remote/connect.go index f9d317ef..a8c5cb97 100644 --- a/internal/target/remote/connect.go +++ b/internal/target/remote/connect.go @@ -56,7 +56,7 @@ type mxConn struct { } func (c *mxConn) Usable() bool { - if c.C == nil || c.transactions> c.reuseLimit || c.C.Client() == nil || c.errored { + if c.C == nil || c.transactions> c.reuseLimit || c.Client() == nil || c.errored { return false } return c.C.Client().Reset() == nil @@ -109,7 +109,9 @@ retry: // reason - this is either a connection problem or server actively // rejecting STARTTLS (despite advertising STARTTLS). // We err on the caution side here and do not perform any fallbacks. - conn.DirectClose() + if err := conn.DirectClose(); err != nil { + rd.Log.Error("conn.DirectClose failed", err) + } return module.TLSNone, nil, err } @@ -131,7 +133,9 @@ retry: // TODO: Check go-smtp code to make TLS verification errors // non-sticky so we can properly send QUIT in this case. - conn.DirectClose() + if err := conn.DirectClose(); err != nil { + rd.Log.Error("conn.DirectClose failed", err) + } goto retry } @@ -139,7 +143,9 @@ retry: rd.Log.Error("TLS error, trying plaintext", err, "remote_server", host, "domain", conn.domain) tlsCfg = nil tlsLevel = module.TLSNone - conn.DirectClose() + if err := conn.DirectClose(); err != nil { + rd.Log.Error("conn.DirectClose failed", err) + } goto retry } @@ -183,7 +189,7 @@ func (rd *remoteDelivery) attemptMX(ctx context.Context, conn *mxConn, record *n for _, p := range rd.policies { policyLevel, err := p.CheckConn(connCtx, mxLevel, tlsLevel, conn.domain, record.Host, tlsState) if err != nil { - conn.Close() + rd.closeConn(conn) return exterrors.WithFields(err, map[string]interface{}{"tls_err": tlsErr}) } if policyLevel> tlsLevel { @@ -200,6 +206,12 @@ func (rd *remoteDelivery) attemptMX(ctx context.Context, conn *mxConn, record *n return nil } +func (rd *remoteDelivery) closeConn(c *mxConn) { + if err := c.Close(); err != nil { + rd.Log.Error("client connection close failed", err) + } +} + func (rd *remoteDelivery) connectionForDomain(ctx context.Context, domain string) (*mxConn, error) { if c, ok := rd.connections[domain]; ok { return c, nil @@ -228,7 +240,7 @@ func (rd *remoteDelivery) connectionForDomain(ctx context.Context, domain string if rd.msgMeta.SMTPOpts.RequireTLS { if conn.tlsLevel < module.TLSAuthenticated { - conn.Close() + rd.closeConn(conn) return nil, &exterrors.SMTPError{ Code: 550, EnhancedCode: exterrors.EnhancedCode{5, 7, 30}, @@ -239,7 +251,7 @@ func (rd *remoteDelivery) connectionForDomain(ctx context.Context, domain string } } if conn.mxLevel < module.MX_MTASTS { - conn.Close() + rd.closeConn(conn) return nil, &exterrors.SMTPError{ Code: 550, EnhancedCode: exterrors.EnhancedCode{5, 7, 30}, @@ -254,7 +266,7 @@ func (rd *remoteDelivery) connectionForDomain(ctx context.Context, domain string region := trace.StartRegion(ctx, "remote/limits.TakeDest") if err := rd.rt.limits.TakeDest(ctx, domain); err != nil { region.End() - conn.Close() + rd.closeConn(conn) return nil, err } region.End() @@ -272,7 +284,7 @@ func (rd *remoteDelivery) connectionForDomain(ctx context.Context, domain string } if err := conn.Mail(ctx, rd.mailFrom, rd.msgMeta.SMTPOpts); err != nil { - conn.Close() + rd.closeConn(conn) return nil, err } conn.lastUseAt = time.Now() diff --git a/internal/target/remote/dane_delivery_test.go b/internal/target/remote/dane_delivery_test.go index 377f7cfc..8d1cec22 100644 --- a/internal/target/remote/dane_delivery_test.go +++ b/internal/target/remote/dane_delivery_test.go @@ -29,6 +29,8 @@ import ( "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/testutils" miekgdns "github.com/miekg/dns" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func targetWithExtResolver(t *testing.T, zones map[string]mockdns.Zone) (*mockdns.Server, *Target) { @@ -77,7 +79,9 @@ func tlsaRecord(name string, usage, matchType, selector uint8, cert string) map[ func TestRemoteDelivery_DANE_Ok(t *testing.T) { _, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) // RFC 7672, Section 2.2.2. "Non-CNAME" case. @@ -98,7 +102,9 @@ func TestRemoteDelivery_DANE_Ok(t *testing.T) { } dnsSrv, tgt := targetWithExtResolver(t, zones) - defer dnsSrv.Close() + defer func() { + assert.NoError(t, dnsSrv.Close()) + }() tgt.policies = append(tgt.policies, &localPolicy{ minTLSLevel: module.TLSAuthenticated, // Established via DANE instead of PKIX. @@ -111,7 +117,9 @@ func TestRemoteDelivery_DANE_Ok(t *testing.T) { func TestRemoteDelivery_DANE_CNAMEd_1(t *testing.T) { _, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) // RFC 7672, Section 2.2.2. "Secure CNAME" case - TLSA at CNAME matches. @@ -135,7 +143,9 @@ func TestRemoteDelivery_DANE_CNAMEd_1(t *testing.T) { } dnsSrv, tgt := targetWithExtResolver(t, zones) - defer dnsSrv.Close() + defer func() { + assert.NoError(t, dnsSrv.Close()) + }() tgt.policies = append(tgt.policies, &localPolicy{ minTLSLevel: module.TLSAuthenticated, // Established via DANE instead of PKIX. @@ -148,7 +158,9 @@ func TestRemoteDelivery_DANE_CNAMEd_1(t *testing.T) { func TestRemoteDelivery_DANE_CNAMEd_2(t *testing.T) { _, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) // RFC 7672, Section 2.2.2. "Secure CNAME" case - TLSA at initial name matches. @@ -173,7 +185,9 @@ func TestRemoteDelivery_DANE_CNAMEd_2(t *testing.T) { } dnsSrv, tgt := targetWithExtResolver(t, zones) - defer dnsSrv.Close() + defer func() { + assert.NoError(t, dnsSrv.Close()) + }() tgt.policies = append(tgt.policies, &localPolicy{ minTLSLevel: module.TLSAuthenticated, // Established via DANE instead of PKIX. @@ -186,7 +200,9 @@ func TestRemoteDelivery_DANE_CNAMEd_2(t *testing.T) { func TestRemoteDelivery_DANE_InsecureCNAMEDest(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) // RFC 7672, Section 2.2.2. "Insecure CNAME" case - initial name is secure. @@ -217,7 +233,9 @@ func TestRemoteDelivery_DANE_InsecureCNAMEDest(t *testing.T) { } dnsSrv, tgt := targetWithExtResolver(t, zones) - defer dnsSrv.Close() + defer func() { + require.NoError(t, dnsSrv.Close()) + }() tgt.tlsConfig = clientCfg _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) @@ -231,7 +249,9 @@ func TestRemoteDelivery_DANE_InsecureCNAMEDest(t *testing.T) { func TestRemoteDelivery_DANE_NonAD_TLSA_Ignore(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) // RFC 7672, Section 2.2.2. "Non-CNAME" case - initial name is insecure. @@ -250,7 +270,9 @@ func TestRemoteDelivery_DANE_NonAD_TLSA_Ignore(t *testing.T) { } dnsSrv, tgt := targetWithExtResolver(t, zones) - defer dnsSrv.Close() + defer func() { + require.NoError(t, dnsSrv.Close()) + }() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -258,7 +280,9 @@ func TestRemoteDelivery_DANE_NonAD_TLSA_Ignore(t *testing.T) { func TestRemoteDelivery_DANE_NonADIgnore_CNAME(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) // RFC 7672, Section 2.2.2. "Insecure CNAME" case - initial name is insecure. @@ -281,7 +305,9 @@ func TestRemoteDelivery_DANE_NonADIgnore_CNAME(t *testing.T) { } dnsSrv, tgt := targetWithExtResolver(t, zones) - defer dnsSrv.Close() + defer func() { + require.NoError(t, dnsSrv.Close()) + }() testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) be.CheckMsg(t, 0, "test@example.com", []string{"test@example.invalid"}) @@ -289,7 +315,9 @@ func TestRemoteDelivery_DANE_NonADIgnore_CNAME(t *testing.T) { func TestRemoteDelivery_DANE_SkipAUnauth(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ @@ -308,7 +336,9 @@ func TestRemoteDelivery_DANE_SkipAUnauth(t *testing.T) { } dnsSrv, tgt := targetWithExtResolver(t, zones) - defer dnsSrv.Close() + defer func() { + require.NoError(t, dnsSrv.Close()) + }() tgt.tlsConfig = clientCfg testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) @@ -317,7 +347,9 @@ func TestRemoteDelivery_DANE_SkipAUnauth(t *testing.T) { func TestRemoteDelivery_DANE_Mismatch(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ @@ -337,7 +369,9 @@ func TestRemoteDelivery_DANE_Mismatch(t *testing.T) { } dnsSrv, tgt := targetWithExtResolver(t, zones) - defer dnsSrv.Close() + defer func() { + require.NoError(t, dnsSrv.Close()) + }() tgt.tlsConfig = clientCfg _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) @@ -351,7 +385,9 @@ func TestRemoteDelivery_DANE_Mismatch(t *testing.T) { func TestRemoteDelivery_DANE_NoRecord(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ @@ -365,7 +401,9 @@ func TestRemoteDelivery_DANE_NoRecord(t *testing.T) { } dnsSrv, tgt := targetWithExtResolver(t, zones) - defer dnsSrv.Close() + defer func() { + require.NoError(t, dnsSrv.Close()) + }() tgt.tlsConfig = clientCfg testutils.DoTestDelivery(t, tgt, "test@example.com", []string{"test@example.invalid"}) @@ -374,7 +412,9 @@ func TestRemoteDelivery_DANE_NoRecord(t *testing.T) { func TestRemoteDelivery_DANE_LookupErr(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ @@ -391,7 +431,9 @@ func TestRemoteDelivery_DANE_LookupErr(t *testing.T) { } dnsSrv, tgt := targetWithExtResolver(t, zones) - defer dnsSrv.Close() + defer func() { + require.NoError(t, dnsSrv.Close()) + }() tgt.tlsConfig = clientCfg _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) @@ -405,7 +447,9 @@ func TestRemoteDelivery_DANE_LookupErr(t *testing.T) { func TestRemoteDelivery_DANE_NoTLS(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ @@ -424,7 +468,9 @@ func TestRemoteDelivery_DANE_NoTLS(t *testing.T) { }, } dnsSrv, tgt := targetWithExtResolver(t, zones) - defer dnsSrv.Close() + defer func() { + require.NoError(t, dnsSrv.Close()) + }() _, err := testutils.DoTestDeliveryErr(t, tgt, "test@example.com", []string{"test@example.invalid"}) if err == nil { @@ -437,7 +483,9 @@ func TestRemoteDelivery_DANE_NoTLS(t *testing.T) { func TestRemoteDelivery_DANE_TLSError(t *testing.T) { _, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ @@ -457,7 +505,9 @@ func TestRemoteDelivery_DANE_TLSError(t *testing.T) { }, } dnsSrv, tgt := targetWithExtResolver(t, zones) - defer dnsSrv.Close() + defer func() { + require.NoError(t, dnsSrv.Close()) + }() // Cause failure through version incompatibility. tgt.tlsConfig = &tls.Config{ diff --git a/internal/target/remote/mxauth_test.go b/internal/target/remote/mxauth_test.go index f979a53d..5f698554 100644 --- a/internal/target/remote/mxauth_test.go +++ b/internal/target/remote/mxauth_test.go @@ -33,11 +33,14 @@ import ( "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/testutils" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestRemoteDelivery_AuthMX_MTASTS(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -74,11 +77,15 @@ func TestRemoteDelivery_AuthMX_MTASTS(t *testing.T) { func TestRemoteDelivery_MTASTS_SkipNonMatching(t *testing.T) { _, be1, srv1 := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv1.Close() + defer func() { + require.NoError(t, srv1.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv1) clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.2:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -125,7 +132,9 @@ func TestRemoteDelivery_MTASTS_SkipNonMatching(t *testing.T) { func TestRemoteDelivery_AuthMX_MTASTS_Fail(t *testing.T) { clientCfg, be1, srv1 := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv1.Close() + defer func() { + assert.NoError(t, srv1.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv1) zones := map[string]mockdns.Zone{ @@ -169,7 +178,9 @@ func TestRemoteDelivery_AuthMX_MTASTS_Fail(t *testing.T) { func TestRemoteDelivery_AuthMX_MTASTS_NoTLS(t *testing.T) { be1, srv1 := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv1.Close() + defer func() { + assert.NoError(t, srv1.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv1) zones := map[string]mockdns.Zone{ @@ -212,7 +223,9 @@ func TestRemoteDelivery_AuthMX_MTASTS_NoTLS(t *testing.T) { func TestRemoteDelivery_AuthMX_MTASTS_RequirePKIX(t *testing.T) { _, be1, srv1 := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv1.Close() + defer func() { + require.NoError(t, srv1.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv1) zones := map[string]mockdns.Zone{ @@ -271,7 +284,9 @@ func TestRemoteDelivery_AuthMX_MTASTS_NoPolicy(t *testing.T) { // // https://builds.sr.ht/~emersion/job/147975 tarpit := testutils.FailOnConn(t, "127.0.0.1:"+smtpPort) - defer tarpit.Close() + defer func() { + require.NoError(t, tarpit.Close()) + }() zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -306,7 +321,9 @@ func TestRemoteDelivery_AuthMX_MTASTS_NoPolicy(t *testing.T) { func TestRemoteDelivery_AuthMX_DNSSEC(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ @@ -324,7 +341,9 @@ func TestRemoteDelivery_AuthMX_DNSSEC(t *testing.T) { if err != nil { t.Fatal(err) } - defer dnsSrv.Close() + defer func() { + require.NoError(t, dnsSrv.Close()) + }() dialer := net.Dialer{} dialer.Resolver = &net.Resolver{} @@ -349,7 +368,9 @@ func TestRemoteDelivery_AuthMX_DNSSEC(t *testing.T) { func TestRemoteDelivery_AuthMX_DNSSEC_Fail(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ @@ -366,7 +387,9 @@ func TestRemoteDelivery_AuthMX_DNSSEC_Fail(t *testing.T) { if err != nil { t.Fatal(err) } - defer dnsSrv.Close() + defer func() { + require.NoError(t, dnsSrv.Close()) + }() dialer := net.Dialer{} dialer.Resolver = &net.Resolver{} @@ -400,7 +423,9 @@ func TestRemoteDelivery_AuthMX_DNSSEC_Fail(t *testing.T) { func TestRemoteDelivery_REQUIRETLS(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) srv.EnableREQUIRETLS = true - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -443,7 +468,9 @@ func TestRemoteDelivery_REQUIRETLS(t *testing.T) { func TestRemoteDelivery_REQUIRETLS_Fail(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) srv.EnableREQUIRETLS = false /* no REQUIRETLS */ - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -490,7 +517,9 @@ func TestRemoteDelivery_REQUIRETLS_Fail(t *testing.T) { func TestRemoteDelivery_REQUIRETLS_Relaxed(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) srv.EnableREQUIRETLS = false /* no REQUIRETLS */ - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -534,7 +563,9 @@ func TestRemoteDelivery_REQUIRETLS_Relaxed(t *testing.T) { func TestRemoteDelivery_REQUIRETLS_Relaxed_NoMXAuth(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) srv.EnableREQUIRETLS = false /* no REQUIRETLS */ - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -577,7 +608,9 @@ func TestRemoteDelivery_REQUIRETLS_Relaxed_NoMXAuth(t *testing.T) { func TestRemoteDelivery_REQUIRETLS_Relaxed_NoTLS(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) srv.EnableREQUIRETLS = false /* no REQUIRETLS */ - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -625,7 +658,9 @@ func TestRemoteDelivery_REQUIRETLS_Relaxed_NoTLS(t *testing.T) { func TestRemoteDelivery_REQUIRETLS_Relaxed_TLSFail(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) srv.EnableREQUIRETLS = false /* no REQUIRETLS */ - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { diff --git a/internal/target/remote/remote.go b/internal/target/remote/remote.go index e300edd8..b382a88b 100644 --- a/internal/target/remote/remote.go +++ b/internal/target/remote/remote.go @@ -217,7 +217,7 @@ type remoteDelivery struct { func (rt *Target) StartDelivery(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { policies := make([]module.DeliveryMXAuthPolicy, 0, len(rt.policies)) - if !(msgMeta.TLSRequireOverride && rt.allowSecOverride) { + if !msgMeta.TLSRequireOverride || !rt.allowSecOverride { for _, p := range rt.policies { policies = append(policies, p.StartDelivery(msgMeta)) } @@ -422,7 +422,11 @@ func (rd *remoteDelivery) BodyNonAtomic(ctx context.Context, c module.StatusColl } return } - defer bodyR.Close() + defer func() { + if err := bodyR.Close(); err != nil { + rd.Log.Error("failed to close message buffer", err) + } + }() err = conn.Data(ctx, header, bodyR) for _, rcpt := range conn.Rcpts() { @@ -453,8 +457,8 @@ func (rd *remoteDelivery) Close() error { if !conn.Usable() { rd.Log.Debugf("disconnected %v from %s (errored=%v,transactions=%v,disconnected before=%v)", - conn.LocalAddr(), conn.ServerName(), conn.errored, conn.transactions, conn.C.Client() == nil) - conn.Close() + conn.LocalAddr(), conn.ServerName(), conn.errored, conn.transactions, conn.Client() == nil) + rd.closeConn(conn) } else { rd.Log.Debugf("returning connection %v for %s to pool", conn.LocalAddr(), conn.ServerName()) rd.rt.pool.Return(conn.domain, conn) diff --git a/internal/target/remote/remote_test.go b/internal/target/remote/remote_test.go index b21f97e0..1578303b 100644 --- a/internal/target/remote/remote_test.go +++ b/internal/target/remote/remote_test.go @@ -41,6 +41,7 @@ import ( "github.com/foxcpp/maddy/internal/smtpconn/pool" "github.com/foxcpp/maddy/internal/testutils" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // .invalid TLD is used here to make sure if there is something wrong about @@ -118,7 +119,9 @@ func testDANEPolicy(t *testing.T, extR *dns.ExtResolver) *danePolicy { func TestRemoteDelivery(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -140,7 +143,9 @@ func TestRemoteDelivery(t *testing.T) { func TestRemoteDelivery_NoMXFallback(t *testing.T) { tarpit := testutils.FailOnConn(t, "127.0.0.1:"+smtpPort) - defer tarpit.Close() + defer func() { + assert.NoError(t, tarpit.Close()) + }() zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -169,7 +174,9 @@ func TestRemoteDelivery_NoMXFallback(t *testing.T) { func TestRemoteDelivery_EmptySender(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -193,7 +200,9 @@ func TestRemoteDelivery_IPLiteral(t *testing.T) { t.Skip("Support disabled") be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ @@ -219,7 +228,9 @@ func TestRemoteDelivery_IPLiteral(t *testing.T) { func TestRemoteDelivery_FallbackMX(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -238,7 +249,9 @@ func TestRemoteDelivery_FallbackMX(t *testing.T) { func TestRemoteDelivery_BodyNonAtomic(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -268,7 +281,9 @@ func TestRemoteDelivery_BodyNonAtomic(t *testing.T) { func TestRemoteDelivery_Abort(t *testing.T) { _, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -300,7 +315,9 @@ func TestRemoteDelivery_Abort(t *testing.T) { func TestRemoteDelivery_CommitWithoutBody(t *testing.T) { _, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -333,7 +350,9 @@ func TestRemoteDelivery_CommitWithoutBody(t *testing.T) { func TestRemoteDelivery_MAILFROMErr(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -370,7 +389,9 @@ func TestRemoteDelivery_MAILFROMErr(t *testing.T) { func TestRemoteDelivery_NoMX(t *testing.T) { tarpit := testutils.FailOnConn(t, "127.0.0.1:"+smtpPort) - defer tarpit.Close() + defer func() { + require.NoError(t, tarpit.Close()) + }() zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -402,7 +423,9 @@ func TestRemoteDelivery_NullMX(t *testing.T) { // deliver the message. Use of testutils.SMTPServer here // causes weird race conditions. tarpit := testutils.FailOnConn(t, "127.0.0.1:"+smtpPort) - defer tarpit.Close() + defer func() { + require.NoError(t, tarpit.Close()) + }() zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -430,7 +453,9 @@ func TestRemoteDelivery_NullMX(t *testing.T) { func TestRemoteDelivery_Quarantined(t *testing.T) { _, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -474,7 +499,9 @@ func TestRemoteDelivery_Quarantined(t *testing.T) { func TestRemoteDelivery_MAILFROMErr_Repeated(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -514,7 +541,9 @@ func TestRemoteDelivery_MAILFROMErr_Repeated(t *testing.T) { func TestRemoteDelivery_RcptErr(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -569,7 +598,9 @@ func TestRemoteDelivery_RcptErr(t *testing.T) { func TestRemoteDelivery_DownMX(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -624,10 +655,14 @@ func TestRemoteDelivery_AllMXDown(t *testing.T) { func TestRemoteDelivery_Split(t *testing.T) { be1, srv1 := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv1.Close() + defer func() { + assert.NoError(t, srv1.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv1) be2, srv2 := testutils.SMTPServer(t, "127.0.0.2:"+smtpPort) - defer srv2.Close() + defer func() { + assert.NoError(t, srv2.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv2) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -657,10 +692,14 @@ func TestRemoteDelivery_Split(t *testing.T) { func TestRemoteDelivery_Split_Fail(t *testing.T) { be1, srv1 := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv1.Close() + defer func() { + require.NoError(t, srv1.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv1) be2, srv2 := testutils.SMTPServer(t, "127.0.0.2:"+smtpPort) - defer srv2.Close() + defer func() { + require.NoError(t, srv2.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv2) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -723,7 +762,9 @@ func TestRemoteDelivery_Split_Fail(t *testing.T) { func TestRemoteDelivery_BodyErr(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -770,10 +811,14 @@ func TestRemoteDelivery_BodyErr(t *testing.T) { func TestRemoteDelivery_Split_BodyErr(t *testing.T) { be1, srv1 := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv1.Close() + defer func() { + require.NoError(t, srv1.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv1) _, srv2 := testutils.SMTPServer(t, "127.0.0.2:"+smtpPort) - defer srv2.Close() + defer func() { + require.NoError(t, srv2.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv2) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -828,10 +873,14 @@ func TestRemoteDelivery_Split_BodyErr(t *testing.T) { func TestRemoteDelivery_Split_BodyErr_NonAtomic(t *testing.T) { be1, srv1 := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv1.Close() + defer func() { + require.NoError(t, srv1.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv1) _, srv2 := testutils.SMTPServer(t, "127.0.0.2:"+smtpPort) - defer srv2.Close() + defer func() { + require.NoError(t, srv2.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv2) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -898,7 +947,9 @@ func TestRemoteDelivery_Split_BodyErr_NonAtomic(t *testing.T) { func TestRemoteDelivery_TLSErrFallback(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -927,7 +978,9 @@ func TestRemoteDelivery_TLSErrFallback(t *testing.T) { func TestRemoteDelivery_RequireTLS_Missing(t *testing.T) { _, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -953,7 +1006,9 @@ func TestRemoteDelivery_RequireTLS_Missing(t *testing.T) { func TestRemoteDelivery_RequireTLS_Present(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -978,7 +1033,9 @@ func TestRemoteDelivery_RequireTLS_Present(t *testing.T) { func TestRemoteDelivery_RequireTLS_NoErrFallback(t *testing.T) { clientCfg, _, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -1011,7 +1068,9 @@ func TestRemoteDelivery_RequireTLS_NoErrFallback(t *testing.T) { func TestRemoteDelivery_TLS_FallbackNoVerify(t *testing.T) { _, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -1042,7 +1101,9 @@ func TestRemoteDelivery_TLS_FallbackNoVerify(t *testing.T) { func TestRemoteDelivery_TLS_FallbackPlaintext(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { @@ -1083,7 +1144,9 @@ func TestMain(m *testing.M) { func TestRemoteDelivery_ConnReuse(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+smtpPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) zones := map[string]mockdns.Zone{ "example.invalid.": { diff --git a/internal/target/remote/security.go b/internal/target/remote/security.go index 51572b89..efbbb1d2 100644 --- a/internal/target/remote/security.go +++ b/internal/target/remote/security.go @@ -306,19 +306,19 @@ func NewDNSSECPolicy(_, instName string) (module.Module, error) { }, nil } -func (c *dnssecPolicy) Name() string { +func (dnssecPolicy) Name() string { return "mx_auth.dnssec" } -func (c *dnssecPolicy) InstanceName() string { +func (c dnssecPolicy) InstanceName() string { return c.instName } -func (c *dnssecPolicy) Weight() int { +func (dnssecPolicy) Weight() int { return 1 } -func (c *dnssecPolicy) Configure(inlineArgs []string, cfg *config.Map) error { +func (dnssecPolicy) Configure(inlineArgs []string, cfg *config.Map) error { _, err := cfg.Process() // will fail if there is any directive return err } diff --git a/internal/target/smtp/sasl_test.go b/internal/target/smtp/sasl_test.go index 63b52a5e..a1054845 100644 --- a/internal/target/smtp/sasl_test.go +++ b/internal/target/smtp/sasl_test.go @@ -25,6 +25,7 @@ import ( "github.com/foxcpp/maddy/framework/config" "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/testutils" + "github.com/stretchr/testify/require" ) func testSaslFactory(t *testing.T, args ...string) saslClientFactory { @@ -40,7 +41,9 @@ func testSaslFactory(t *testing.T, args ...string) saslClientFactory { func TestSASL_Plain(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+testPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) mod := &Downstream{ @@ -68,7 +71,9 @@ func TestSASL_Plain(t *testing.T) { func TestSASL_Plain_AuthFail(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+testPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) be.AuthErr = &smtp.SMTPError{ @@ -98,7 +103,9 @@ func TestSASL_Plain_AuthFail(t *testing.T) { func TestSASL_Forward(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+testPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) mod := &Downstream{ @@ -131,7 +138,9 @@ func TestSASL_Forward(t *testing.T) { func TestSASL_Forward_NoCreds(t *testing.T) { _, srv := testutils.SMTPServer(t, "127.0.0.1:"+testPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) mod := &Downstream{ diff --git a/internal/target/smtp/smtp_downstream.go b/internal/target/smtp/smtp_downstream.go index d48b56ad..6c678f0a 100644 --- a/internal/target/smtp/smtp_downstream.go +++ b/internal/target/smtp/smtp_downstream.go @@ -87,7 +87,7 @@ func NewDownstream(modName, instName string) (module.Module, error) { func (u *Downstream) Configure(inlineArgs []string, cfg *config.Map) error { var attemptTLS *bool - var targetsArg []string + targetsArg := make([]string, 0, len(inlineArgs)) cfg.Bool("debug", true, false, &u.log.Debug) cfg.Callback("require_tls", func(m *config.Map, node config.Node) error { u.log.Msg("require_tls directive is deprecated and ignored") @@ -195,7 +195,9 @@ func (u *Downstream) StartDelivery(ctx context.Context, msgMeta *module.MsgMetad } if err := d.conn.Mail(ctx, mailFrom, msgMeta.SMTPOpts); err != nil { - d.conn.Close() + if err := d.conn.Close(); err != nil { + u.log.Error("failed to close smtp connection", err) + } return nil, err } @@ -206,6 +208,12 @@ func (u *Downstream) StartDelivery(ctx context.Context, msgMeta *module.MsgMetad return d, nil } +func (d *delivery) closeConn(c *smtpconn.C) { + if err := c.Close(); err != nil { + d.log.Error("failed to close SMTP connection", err) + } +} + func (d *delivery) connect(ctx context.Context) error { // TODO: Review possibility of connection pooling here. var lastErr error @@ -251,12 +259,12 @@ func (d *delivery) connect(ctx context.Context) error { if d.u.saslFactory != nil { saslClient, err := d.u.saslFactory(d.msgMeta) if err != nil { - conn.Close() + d.closeConn(conn) return err } if err := conn.Client().Auth(saslClient); err != nil { - conn.Close() + d.closeConn(conn) return err } } @@ -282,7 +290,11 @@ func (d *delivery) Body(ctx context.Context, header textproto.Header, body buffe return exterrors.WithFields(err, map[string]interface{}{"target": d.u.modName}) } - defer r.Close() + defer func() { + if err := r.Close(); err != nil { + d.log.Msg("failed to close body buffer", err) + } + }() return d.u.moduleError(d.conn.Data(ctx, header, r)) } @@ -294,7 +306,11 @@ func (d *lmtpDelivery) BodyNonAtomic(ctx context.Context, sc module.StatusCollec sc.SetStatus(rcpt, modErr) } } - defer r.Close() + defer func() { + if err := r.Close(); err != nil { + d.log.Msg("failed to close body buffer", err) + } + }() rcptIndx := 0 err = d.conn.LMTPData(ctx, header, r, func(rcpt string, err *smtp.SMTPError) { @@ -320,8 +336,7 @@ func (d *lmtpDelivery) BodyNonAtomic(ctx context.Context, sc module.StatusCollec } func (d *delivery) Abort(ctx context.Context) error { - d.conn.Close() - return nil + return d.conn.Close() } func (d *delivery) Commit(ctx context.Context) error { diff --git a/internal/target/smtp/smtp_downstream_test.go b/internal/target/smtp/smtp_downstream_test.go index f0f58c66..e292f5e3 100644 --- a/internal/target/smtp/smtp_downstream_test.go +++ b/internal/target/smtp/smtp_downstream_test.go @@ -30,17 +30,22 @@ import ( "github.com/foxcpp/maddy/framework/config" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/internal/testutils" + "github.com/stretchr/testify/require" ) var testPort string func TestDownstreamDelivery(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+testPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) tarpit := testutils.FailOnConn(t, "127.0.0.2:"+testPort) - defer tarpit.Close() + defer func() { + require.NoError(t, tarpit.Close()) + }() mod := &Downstream{ hostname: "mx.example.invalid", @@ -74,7 +79,9 @@ func TestDownstreamDelivery_LMTP(t *testing.T) { Message: "nop", }, } - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) mod := &Downstream{ @@ -125,7 +132,9 @@ func TestDownstreamDelivery_LMTP_ErrorCoerce(t *testing.T) { Message: "nop", }, } - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) mod := &Downstream{ @@ -156,7 +165,9 @@ func (sc *statusCollector) SetStatus(rcptTo string, err error) { func TestDownstreamDelivery_Fallback(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.2:"+testPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) mod := &Downstream{ @@ -182,7 +193,9 @@ func TestDownstreamDelivery_Fallback(t *testing.T) { func TestDownstreamDelivery_MAILErr(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+testPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) be.MailErr = &smtp.SMTPError{ @@ -209,7 +222,9 @@ func TestDownstreamDelivery_MAILErr(t *testing.T) { func TestDownstreamDelivery_StartTLS(t *testing.T) { clientCfg, be, srv := testutils.SMTPServerSTARTTLS(t, "127.0.0.1:"+testPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) mod := &Downstream{ @@ -237,7 +252,9 @@ func TestDownstreamDelivery_StartTLS(t *testing.T) { func TestDownstreamDelivery_StartTLS_NoFallback(t *testing.T) { _, srv := testutils.SMTPServer(t, "127.0.0.1:"+testPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) mod := &Downstream{ diff --git a/internal/target/smtp/smtputf8_test.go b/internal/target/smtp/smtputf8_test.go index f47fc778..1f469e8a 100644 --- a/internal/target/smtp/smtputf8_test.go +++ b/internal/target/smtp/smtputf8_test.go @@ -23,11 +23,14 @@ import ( "github.com/foxcpp/maddy/framework/config" "github.com/foxcpp/maddy/internal/testutils" + "github.com/stretchr/testify/require" ) func TestDownstreamDelivery_EHLO_ALabel(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+testPort) - defer srv.Close() + defer func() { + require.NoError(t, srv.Close()) + }() defer testutils.CheckSMTPConnLeak(t, srv) mod, err := NewDownstream("", "") diff --git a/internal/testutils/bench_delivery.go b/internal/testutils/bench_delivery.go index eedd0d2a..85fb80f0 100644 --- a/internal/testutils/bench_delivery.go +++ b/internal/testutils/bench_delivery.go @@ -123,7 +123,7 @@ func BenchDelivery(b *testing.B, target module.DeliveryTarget, sender string, re } for i, rcptTemplate := range recipientTemplates { - rcpt := strings.Replace(rcptTemplate, "X", strconv.Itoa(i), -1) + rcpt := strings.ReplaceAll(rcptTemplate, "X", strconv.Itoa(i)) if err := delivery.AddRcpt(benchCtx, rcpt, smtp.RcptOptions{}); err != nil { b.Fatal(err) diff --git a/internal/testutils/smtp_server.go b/internal/testutils/smtp_server.go index 9af52c4f..214b72db 100644 --- a/internal/testutils/smtp_server.go +++ b/internal/testutils/smtp_server.go @@ -33,6 +33,7 @@ import ( "github.com/emersion/go-sasl" "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/exterrors" + "github.com/stretchr/testify/require" ) type SMTPMessage struct { @@ -227,10 +228,8 @@ func SMTPServer(t *testing.T, addr string, fn ...SMTPServerConfigureFunc) (*SMTP // nil Server.listener (Serve sets it to a non-nil value, so it is racy and // happens only sometimes). testConn, err := net.Dial("tcp", addr) - if err != nil { - t.Fatal(err) - } - testConn.Close() + require.NoError(t, err) + require.NoError(t, testConn.Close()) return be, s } @@ -321,10 +320,8 @@ func SMTPServerSTARTTLS(t *testing.T, addr string, fn ...SMTPServerConfigureFunc // nil Server.listener (Serve sets it to a non-nil value, so it is racy and // happens only sometimes). testConn, err := net.Dial("tcp", addr) - if err != nil { - t.Fatal(err) - } - testConn.Close() + require.NoError(t, err) + require.NoError(t, testConn.Close()) return clientCfg, be, s } @@ -379,7 +376,7 @@ func SMTPServerTLS(t *testing.T, addr string, fn ...SMTPServerConfigureFunc) (*t if err != nil { t.Fatal(err) } - testConn.Close() + require.NoError(t, testConn.Close()) return clientCfg, be, s } diff --git a/internal/testutils/target.go b/internal/testutils/target.go index 19585215..221834ba 100644 --- a/internal/testutils/target.go +++ b/internal/testutils/target.go @@ -62,18 +62,18 @@ type Target struct { module.Module is implemented with dummy functions for logging done by MsgPipeline code. */ -func (dt Target) Configure([]string, *config.Map) error { +func (dt *Target) Configure([]string, *config.Map) error { return nil } -func (dt Target) InstanceName() string { +func (dt *Target) InstanceName() string { if dt.InstName != "" { return dt.InstName } return "test_instance" } -func (dt Target) Name() string { +func (dt *Target) Name() string { return "test_target" } @@ -129,7 +129,11 @@ func (dtd *testTargetDeliveryPartial) BodyNonAtomic(ctx context.Context, c modul } return } - defer body.Close() + defer func() { + if err := body.Close(); err != nil { + panic(err) + } + }() dtd.msg.Body, err = io.ReadAll(body) if err != nil { @@ -153,7 +157,11 @@ func (dtd *testTargetDelivery) Body(ctx context.Context, header textproto.Header if err != nil { return err } - defer body.Close() + defer func() { + if err := body.Close(); err != nil { + panic(err) + } + }() if dtd.tgt.DiscardMessages { // Don't bother. diff --git a/internal/updatepipe/pubsub/pq.go b/internal/updatepipe/pubsub/pq.go index 29f9c525..c084e600 100644 --- a/internal/updatepipe/pubsub/pq.go +++ b/internal/updatepipe/pubsub/pq.go @@ -50,8 +50,12 @@ func NewPQ(dsn string) (*PqPubSub, error) { } func (l *PqPubSub) Close() error { - l.sender.Close() - l.L.Close() + if err := l.sender.Close(); err != nil { + l.Log.Error("failed to close sender socket", err) + } + if err := l.L.Close(); err != nil { + l.Log.Error("failed to close listener", err) + } return nil } diff --git a/internal/updatepipe/unix_pipe.go b/internal/updatepipe/unix_pipe.go index 945c4f95..a2042d62 100644 --- a/internal/updatepipe/unix_pipe.go +++ b/internal/updatepipe/unix_pipe.go @@ -120,11 +120,17 @@ func (usp *UnixSockPipe) Push(upd mess.Update) error { func (usp *UnixSockPipe) Close() error { if usp.sender != nil { - usp.sender.Close() + if err := usp.sender.Close(); err != nil { + usp.Log.Error("failed to close sender socket", err) + } } if usp.listener != nil { - usp.listener.Close() - os.Remove(usp.SockPath) + if err := usp.listener.Close(); err != nil { + usp.Log.Error("failed to close listener", err) + } + if err := os.Remove(usp.SockPath); err != nil { + usp.Log.Error("failed to remove socket", err) + } } return nil } diff --git a/maddy.go b/maddy.go index 23eea317..5c9381d2 100644 --- a/maddy.go +++ b/maddy.go @@ -201,10 +201,18 @@ func Run(c *cli.Context) error { initDebug(c) - os.Setenv("PATH", config.LibexecDirectory+string(filepath.ListSeparator)+os.Getenv("PATH")) + err = os.Setenv("PATH", config.LibexecDirectory+string(filepath.ListSeparator)+os.Getenv("PATH")) + if err != nil { + systemdStatusErr(err) + return cli.Exit(err.Error(), 1) + } hooks.AddHook(hooks.EventLogRotate, reinitLogging) - defer log.DefaultLogger.Out.Close() + defer func() { + if err := log.DefaultLogger.Out.Close(); err != nil { + log.Println("failed to close default logger output:", err) + } + }() defer hooks.RunHooks(hooks.EventShutdown) defer func() { @@ -222,7 +230,10 @@ func Run(c *cli.Context) error { } func VerifyConfig(c *cli.Context) error { - os.Setenv("PATH", config.LibexecDirectory+string(filepath.ListSeparator)+os.Getenv("PATH")) + err := os.Setenv("PATH", config.LibexecDirectory+string(filepath.ListSeparator)+os.Getenv("PATH")) + if err != nil { + return cli.Exit(err.Error(), 1) + } if _, err := moduleConfigure(c.Path("config")); err != nil { return cli.Exit(err.Error(), 2) @@ -308,7 +319,9 @@ func ensureDirectoryWritable(path string) error { if err != nil { return err } - testFile.Close() + if err := testFile.Close(); err != nil { + log.Println("failed to close writeable-test file:", err) + } return os.RemoveAll(testFile.Name()) } @@ -337,7 +350,11 @@ func ReadConfig(path string) ([]config.Node, error) { if err != nil { return nil, err } - defer f.Close() + defer func() { + if err := f.Close(); err != nil { + log.Println("failed to close config file:", err) + } + }() return parser.Read(f, path) } diff --git a/systemd.go b/systemd.go index 649740d3..2eea54e0 100644 --- a/systemd.go +++ b/systemd.go @@ -84,7 +84,11 @@ func systemdStatus(status SDStatus, desc string) { } return } - defer sock.Close() + defer func() { + if err := sock.Close(); err != nil { + log.Println("systemd: failed to close systemd socket:", err) + } + }() if err := setScmPassCred(sock); err != nil { log.Println("systemd: failed to set SCM_PASSCRED on the socket:", err) @@ -111,7 +115,11 @@ func systemdStatusErr(reportedErr error) { } return } - defer sock.Close() + defer func() { + if err := sock.Close(); err != nil { + log.Println("systemd: failed to close systemd socket:", err) + } + }() if err := setScmPassCred(sock); err != nil { log.Println("systemd: failed to set SCM_PASSCRED on the socket:", err) diff --git a/tests/t.go b/tests/t.go index 4e8d4fc1..84592d0a 100644 --- a/tests/t.go +++ b/tests/t.go @@ -40,6 +40,8 @@ import ( "time" "github.com/foxcpp/go-mockdns" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var ( @@ -111,7 +113,7 @@ func (t *T) DNS(zones map[string]mockdns.Zone) { if t.dnsServ != nil { t.Log("NOTE: Multiple DNS calls, replacing the server instance...") - t.dnsServ.Close() + require.NoError(t, t.dnsServ.Close()) } dnsServ, err := mockdns.NewServerWithLogger(zones, t, false) @@ -191,7 +193,7 @@ func (t *T) ensureCanRun() { } t.Log("removing", t.testDir) - os.RemoveAll(t.testDir) + assert.NoError(t, os.RemoveAll(t.testDir)) t.testDir = "" }) } @@ -319,7 +321,9 @@ func (t *T) Run(waitListeners int) { serverStarted := make(chan bool) go func() { - defer logOut.Close() + defer func() { + assert.NoError(t, logOut.Close()) + }() defer close(serverStarted) scnr := bufio.NewScanner(logOut) for scnr.Scan() { @@ -366,7 +370,7 @@ func (t *T) RuntimeDir() string { func (t *T) killServer() { if err := t.servProc.Process.Signal(os.Interrupt); err != nil { t.Log("Unable to kill the server process:", err) - os.RemoveAll(t.testDir) + assert.NoError(t, os.RemoveAll(t.testDir)) return // Return, as now it is pointless to wait for it. } From a9a0ef5a381f8b97875448073d56839d68aef2aa Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年3月25日 01:44:46 +0300 Subject: [PATCH 142/171] tests: No need to close integration test stderr twice --- tests/t.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/t.go b/tests/t.go index 84592d0a..58d021d0 100644 --- a/tests/t.go +++ b/tests/t.go @@ -321,9 +321,6 @@ func (t *T) Run(waitListeners int) { serverStarted := make(chan bool) go func() { - defer func() { - assert.NoError(t, logOut.Close()) - }() defer close(serverStarted) scnr := bufio.NewScanner(logOut) for scnr.Scan() { From 8e834effa480315551ca2a921fd72f062565acbb Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年3月25日 01:53:16 +0300 Subject: [PATCH 143/171] Add AGENTS.md for LLM coding agents Some people use these. File itself is generated using Opus 4.6 and hand-edited later. Possibly incomplete and could be merged with HACKING.md. --- AGENTS.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..ea3ec1f7 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,56 @@ +# AGENTS.md — Maddy Mail Server + +## Architecture + +Maddy is a composable all-in-one mail server (MTA/MX/IMAP) written in Go. The core abstraction is the **module system**: every functional component (auth, storage, checks, targets, endpoints) implements `module.Module` from `framework/module/module.go` and registers itself via `module.Register(name, factory)` in an `init()` function. + +- **`framework/`** — Stable, reusable packages (config parsing, module interfaces, address handling, error types, logging). Interfaces live here to avoid circular imports. +- **`internal/`** — All module implementations. Subdirectories map to module roles: `endpoint/` (protocol listeners), `target/` (delivery destinations), `auth/`, `check/` (message inspectors), `modify/` (header modifiers), `storage/`, `table/` (string→string lookups). +- **`maddy.go`** — Side-effect imports that pull all `internal/` modules into the binary, plus the `Run`/`moduleConfigure`/`RegisterModules` startup sequence. +- **`cmd/maddy/main.go`** — Thin entrypoint; imports root package for module registration, then calls `maddycli.Run()`. + +Modules are wired together at runtime via `maddy.conf` configuration. Top-level blocks are lazily initialized through `module.Registry`. The **message pipeline** (`internal/msgpipeline/`) routes messages from endpoints through checks, modifiers, and to delivery targets based on sender/recipient matching rules. + +## Build & Test + +```sh +# Build (produces ./build/maddy by default): +./build.sh build + +# Build with specific tags (e.g. for Docker): +./build.sh --tags "docker" build + +# Unit tests (standard Go): +go test ./... + +# Integration tests +cd tests && ./run.sh +``` + +The build embeds version via `-ldflags -X github.com/foxcpp/maddy.Version=...`. A C compiler is needed for SQLite support (`mattn/go-sqlite3`). + +## Adding a New Module + +1. Create a package under the appropriate `internal/` subdirectory (e.g. `internal/check/mycheck/`). +2. Implement `module.Module` plus the relevant role interface (`module.Check`, `module.DeliveryTarget`, `module.PlainAuth`, `module.Table`, etc.) from `framework/module/`. +3. Register in `init()`: `module.Register("check.mycheck", NewMyCheck)`. Use naming convention: `check.`, `target.`, `auth.`, `table.`, `modify.` prefixes. +4. Add a blank import `_ "github.com/foxcpp/maddy/internal/check/mycheck"` in `maddy.go`. +5. For checks: use the skeleton at `internal/check/skeleton.go` or `check.RegisterStatelessCheck` (see `internal/check/dns/` for a stateless example). + +## Error Handling + +Use `framework/exterrors` — not bare `fmt.Errorf`. Errors crossing module boundaries must carry: +- SMTP status info via `exterrors.SMTPError{Code, EnhancedCode, Message, CheckName/TargetName}` +- Temporary flag via `exterrors.WithTemporary` +- Module name field + +Keep SMTP error messages generic (no server config details). Use `exterrors.WithFields` for unexpected errors. See `HACKING.md` for full guidelines. + +## Key Conventions + +- **No shared state between messages** — check/modifier code runs in parallel across messages. +- **Panic recovery** — any goroutine you spawn must recover panics to avoid crashing the server. +- **Address normalization** — domain parts must be U-labels with NFC normalization and case-folding. Use `framework/address.CleanDomain`. +- **Configuration parsing** — modules receive config via `config.Map` in their `Configure` method. See `framework/config/` and existing modules for the pattern. +- **Logging** — use `framework/log.Logger`, not `log` stdlib. Per-delivery loggers via `target.DeliveryLogger(...)`. + From a90af91150296e9d29c991a2bbb1de0b6e6e51b5 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年3月25日 15:06:23 +0300 Subject: [PATCH 144/171] auth/ldap, check/rspamd: Fix tls_client directive definition See #824. --- internal/auth/ldap/ldap.go | 2 +- internal/check/rspamd/rspamd.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/auth/ldap/ldap.go b/internal/auth/ldap/ldap.go index 88cc0b4d..d1dac8b2 100644 --- a/internal/auth/ldap/ldap.go +++ b/internal/auth/ldap/ldap.go @@ -25,7 +25,7 @@ type Auth struct { urls []string readBind func(*ldap.Conn) error startls bool - tlsCfg tls.Config + tlsCfg *tls.Config dialer *net.Dialer requestTimeout time.Duration diff --git a/internal/check/rspamd/rspamd.go b/internal/check/rspamd/rspamd.go index 0a737b69..f487a103 100644 --- a/internal/check/rspamd/rspamd.go +++ b/internal/check/rspamd/rspamd.go @@ -92,7 +92,7 @@ func (c *Check) Configure(inlineArgs []string, cfg *config.Map) error { } var ( - tlsConfig tls.Config + tlsConfig *tls.Config flags []string ) @@ -135,7 +135,7 @@ func (c *Check) Configure(inlineArgs []string, cfg *config.Map) error { c.client = &http.Client{ Transport: &http.Transport{ - TLSClientConfig: &tlsConfig, + TLSClientConfig: tlsConfig, }, } c.flags = strings.Join(flags, ",") From 46ebd46cc8a658966742a5c9b0357e337372c8a0 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年3月25日 15:17:38 +0300 Subject: [PATCH 145/171] proxy_protocol: Fix compatibility with go-imap v1 See #783. --- internal/proxy_protocol/proxy_protocol.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/proxy_protocol/proxy_protocol.go b/internal/proxy_protocol/proxy_protocol.go index 1a3a7873..e48c2fa3 100644 --- a/internal/proxy_protocol/proxy_protocol.go +++ b/internal/proxy_protocol/proxy_protocol.go @@ -72,11 +72,12 @@ func NewListener(inner net.Listener, p *ProxyProtocol, logger log.Logger) net.Li return false, nil } - listener = proxyprotocol.NewDefaultListener(inner). + proxyListener := proxyprotocol.NewDefaultListener(inner). WithLogger(proxyprotocol.LoggerFunc(func(format string, v ...interface{}) { logger.Debugf("proxy_protocol: "+format, v...) })). WithSourceChecker(sourceChecker) + listener = &proxyListener if p.tlsConfig != nil { listener = tls.NewListener(listener, p.tlsConfig) From 1712a7abb72688ffcf67f8256d7017784cbf1da1 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年3月25日 21:24:59 +0300 Subject: [PATCH 146/171] libdns: Deprecate libdns providers not updated for libdns 1.x --- internal/libdns/hetzner.go | 2 ++ internal/libdns/leaseweb.go | 2 ++ internal/libdns/namedotcom.go | 2 ++ internal/libdns/vultr.go | 2 ++ 4 files changed, 8 insertions(+) diff --git a/internal/libdns/hetzner.go b/internal/libdns/hetzner.go index 06620c46..d9a39def 100644 --- a/internal/libdns/hetzner.go +++ b/internal/libdns/hetzner.go @@ -5,6 +5,7 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" "github.com/libdns/hetzner" ) @@ -16,6 +17,7 @@ func init() { RecordDeleter: &p, RecordAppender: &p, setConfig: func(c *config.Map) { + log.DefaultLogger.Println("WARNING: maddy 0.10.0 will require new DNS API, see https://github.com/foxcpp/maddy/issues/807 for details") c.String("api_token", false, false, "", &p.AuthAPIToken) }, instName: instName, diff --git a/internal/libdns/leaseweb.go b/internal/libdns/leaseweb.go index 888d0870..013e8b6e 100644 --- a/internal/libdns/leaseweb.go +++ b/internal/libdns/leaseweb.go @@ -5,6 +5,7 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" "github.com/libdns/leaseweb" ) @@ -16,6 +17,7 @@ func init() { RecordDeleter: &p, RecordAppender: &p, setConfig: func(c *config.Map) { + log.DefaultLogger.Println("WARNING: maddy 0.10.0 will drop libdns.leaseweb, see https://github.com/foxcpp/maddy/issues/807 for details") c.String("api_key", false, false, "", &p.APIKey) }, instName: instName, diff --git a/internal/libdns/namedotcom.go b/internal/libdns/namedotcom.go index 0a5c9934..0b4ab608 100644 --- a/internal/libdns/namedotcom.go +++ b/internal/libdns/namedotcom.go @@ -5,6 +5,7 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" "github.com/libdns/namedotcom" ) @@ -18,6 +19,7 @@ func init() { RecordDeleter: &p, RecordAppender: &p, setConfig: func(c *config.Map) { + log.DefaultLogger.Println("WARNING: maddy 0.10.0 will drop libdns.namedotcom, see https://github.com/foxcpp/maddy/issues/807 for details") c.String("user", false, false, "", &p.User) c.String("token", false, false, "", &p.Token) }, diff --git a/internal/libdns/vultr.go b/internal/libdns/vultr.go index e94a2869..35ba38c2 100644 --- a/internal/libdns/vultr.go +++ b/internal/libdns/vultr.go @@ -5,6 +5,7 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" "github.com/libdns/vultr" ) @@ -16,6 +17,7 @@ func init() { RecordDeleter: &p, RecordAppender: &p, setConfig: func(c *config.Map) { + log.DefaultLogger.Println("WARNING: maddy 0.10.0 will drop libdns.vultr, see https://github.com/foxcpp/maddy/issues/807 for details") c.String("api_token", false, false, "", &p.APIToken) }, instName: instName, From 9c5e85dd66bed55e737662c8360f79789dd882b0 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年3月26日 22:14:38 +0300 Subject: [PATCH 147/171] maddy 0.9.1 --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index ac39a106..f374f666 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.9.0 +0.9.1 From 2ab067c8b2b42bd2d07f7c56d9c2a430ea30df7c Mon Sep 17 00:00:00 2001 From: oidq Date: 2026年3月26日 20:56:40 +0100 Subject: [PATCH 148/171] rspamd: fix panic on unspecified tls_client --- internal/check/rspamd/rspamd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/check/rspamd/rspamd.go b/internal/check/rspamd/rspamd.go index f487a103..4f32edf7 100644 --- a/internal/check/rspamd/rspamd.go +++ b/internal/check/rspamd/rspamd.go @@ -97,7 +97,7 @@ func (c *Check) Configure(inlineArgs []string, cfg *config.Map) error { ) cfg.Custom("tls_client", true, false, func() (interface{}, error) { - return tls.Config{}, nil + return &tls.Config{}, nil }, tls2.TLSClientBlock, &tlsConfig) cfg.String("api_path", false, false, c.apiPath, &c.apiPath) cfg.String("settings_id", false, false, "", &c.settingsID) From 979ab37ad6eab91a29909a91c6aaef666ca1acfb Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年3月27日 02:06:54 +0300 Subject: [PATCH 149/171] maddy 0.9.2 --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index f374f666..2003b639 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.9.1 +0.9.2 From da6bcb1751c5ab3f07bf9468633107da2879a3c7 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年3月27日 02:14:24 +0300 Subject: [PATCH 150/171] auth/ldap: Also fix-up LDAP --- cmd/maddy/maddy.conf | 13 +++++++++++++ internal/auth/ldap/ldap.go | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 cmd/maddy/maddy.conf diff --git a/cmd/maddy/maddy.conf b/cmd/maddy/maddy.conf new file mode 100644 index 00000000..83f93f21 --- /dev/null +++ b/cmd/maddy/maddy.conf @@ -0,0 +1,13 @@ + +state_dir /tmp +runtime_dir /tmp + +smtp tcp://127.0.0.1:1234 { + tls off + auth ldap { + base_dn "1" + filter "2 " + } + hostname test + deliver_to dummy +} diff --git a/internal/auth/ldap/ldap.go b/internal/auth/ldap/ldap.go index d1dac8b2..1ac9e774 100644 --- a/internal/auth/ldap/ldap.go +++ b/internal/auth/ldap/ldap.go @@ -54,7 +54,7 @@ func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { cfg.Bool("debug", true, false, &a.log.Debug) cfg.Custom("tls_client", true, false, func() (interface{}, error) { - return tls.Config{}, nil + return &tls.Config{}, nil }, tls2.TLSClientBlock, &a.tlsCfg) cfg.Callback("urls", func(m *config.Map, node config.Node) error { a.urls = append(a.urls, node.Args...) From 66ec658d85e7e599fa857eddc18028378305c684 Mon Sep 17 00:00:00 2001 From: Karel Balej Date: 2026年3月30日 21:46:05 +0200 Subject: [PATCH 151/171] docs: mention rDNS in the setup tutorial --- docs/tutorials/setting-up.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/tutorials/setting-up.md b/docs/tutorials/setting-up.md index 240caa80..04de75fd 100644 --- a/docs/tutorials/setting-up.md +++ b/docs/tutorials/setting-up.md @@ -103,6 +103,11 @@ one as "primary". Add all other domains to the `local_domains` line: $(local_domains) = $(primary_domain) example.com other.example.com ``` +Do not forget to set a suitable rDNS (PTR) record for your server's IP address +to reduce the chances of outgoing mails getting marked as spam or being +downright rejected. Ideally, the PTR record should match whatever you specified +in `$(hostname)`. + ## TLS certificates One thing that can't be automatically configured is TLS certs. If you already From a52ebc8fa35356d58bd4847b41fb33ae8d512d4b Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Wed, 1 Apr 2026 02:21:44 +0300 Subject: [PATCH 152/171] module: Break dependency cycles when loading config correctly See #832 --- framework/module/registry.go | 2 +- tests/modules_test.go | 63 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 tests/modules_test.go diff --git a/framework/module/registry.go b/framework/module/registry.go index ce133402..acb0b190 100644 --- a/framework/module/registry.go +++ b/framework/module/registry.go @@ -105,11 +105,11 @@ func (r *Registry) ensureInitialized(name string, entry *registryEntry) error { r.logger.DebugMsg("module configure", "mod_name", entry.Mod.Name(), "inst_name", entry.Mod.InstanceName()) + r.initialized[name] = struct{}{} err := entry.LazyInit() if err != nil { return err } - r.initialized[name] = struct{}{} return nil } diff --git a/tests/modules_test.go b/tests/modules_test.go new file mode 100644 index 00000000..39046d83 --- /dev/null +++ b/tests/modules_test.go @@ -0,0 +1,63 @@ +//go:build integration + +/* +Maddy Mail Server - Composable all-in-one email server. +Copyright © 2019-2020 Max Mazurov , Maddy Mail Server contributors + +This program 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. + +This program 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 . +*/ + +package tests_test + +import ( + "testing" + + "github.com/foxcpp/maddy/tests" +) + +func TestConfigCycle(tt *testing.T) { + tt.Parallel() + + t := tests.NewT(tt) + t.DNS(nil) + t.Config(` + hostname mx.maddy.test + + msgpipeline local_routing { + destination maddy.test { + deliver_to dummy + } + default_destination { + deliver_to &outbound_queue + } + } + + target.queue outbound_queue { + target dummy + autogenerated_msg_domain maddy.test + bounce { + deliver_to &local_routing + } + } + + smtp tcp://127.0.0.1:1443 { + tls off + + deliver_to &local_routing + } + `) + t.Run(1) + + t.Close() +} From b7316d9a588d763df58d0098635465dbb5d01511 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Wed, 1 Apr 2026 22:38:06 +0300 Subject: [PATCH 153/171] docs: Update wording for NetAuth configuration (auth_map/storage_map use) --- docs/reference/auth/netauth.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/reference/auth/netauth.md b/docs/reference/auth/netauth.md index 2664d41f..4c268108 100644 --- a/docs/reference/auth/netauth.md +++ b/docs/reference/auth/netauth.md @@ -7,10 +7,12 @@ maddy needs to know the Entity ID to use for authentication. It must match the string the user provides for the Local Atom part of their mail address. -Note that storage backends conventionally use email addresses. Since -NetAuth recommends *nix compatible usernames, you will need to map the -email identifiers to NetAuth Entity IDs using `auth_map` (see -documentation page for used storage backend). +Note that storage backends conventionally use email addresses. Since NetAuth +recommends *nix compatible usernames. You will need to either map email +identifiers specified by user to NetAuth Entity IDs using `auth_map` in +endpoint.smtp/imap configuration (recommended) or you would need to use +`storage_map` in storage backend configuration to map NetAuth Entity ID +specified by user back to appropriate storage backend account names. auth.netauth also can be used as a table module. This way you can check whether the account exists. From 6a06337eb41fa87a35697366bcb71c3c962c44ba Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年4月10日 02:36:39 +0300 Subject: [PATCH 154/171] auth/ldap: Fix GHSA-5835-4gvc-32pc Add proper escaping when building filter expression, add proper escaping when building DN from DN template. --- docs/reference/auth/netauth.md | 10 +- go.mod | 1 + go.sum | 2 + internal/auth/ldap/ldap.go | 6 +- tests/conn.go | 9 +- tests/ghsa_5835_4gvc_32pc_test.go | 178 ++++++++++++++++++++++++++++++ 6 files changed, 198 insertions(+), 8 deletions(-) create mode 100644 tests/ghsa_5835_4gvc_32pc_test.go diff --git a/docs/reference/auth/netauth.md b/docs/reference/auth/netauth.md index 2664d41f..4c268108 100644 --- a/docs/reference/auth/netauth.md +++ b/docs/reference/auth/netauth.md @@ -7,10 +7,12 @@ maddy needs to know the Entity ID to use for authentication. It must match the string the user provides for the Local Atom part of their mail address. -Note that storage backends conventionally use email addresses. Since -NetAuth recommends *nix compatible usernames, you will need to map the -email identifiers to NetAuth Entity IDs using `auth_map` (see -documentation page for used storage backend). +Note that storage backends conventionally use email addresses. Since NetAuth +recommends *nix compatible usernames. You will need to either map email +identifiers specified by user to NetAuth Entity IDs using `auth_map` in +endpoint.smtp/imap configuration (recommended) or you would need to use +`storage_map` in storage backend configuration to map NetAuth Entity ID +specified by user back to appropriate storage backend account names. auth.netauth also can be used as a table module. This way you can check whether the account exists. diff --git a/go.mod b/go.mod index 242d0e6d..37227d4d 100644 --- a/go.mod +++ b/go.mod @@ -107,6 +107,7 @@ require ( github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/jimlambrt/gldap v0.1.14 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/klauspost/compress v1.17.11 // indirect diff --git a/go.sum b/go.sum index 81c986e0..ea270b65 100644 --- a/go.sum +++ b/go.sum @@ -488,6 +488,8 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6 github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/jimlambrt/gldap v0.1.14 h1:InG9kldhIu6OoQK0hvfkW1Lqpc5eLJhxiiDTNmRnrDM= +github.com/jimlambrt/gldap v0.1.14/go.mod h1:yobW9JIAmqe23dVNOaMWewPaff6jGaHgYjspPIIgYmg= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= diff --git a/internal/auth/ldap/ldap.go b/internal/auth/ldap/ldap.go index 1ac9e774..06890fd5 100644 --- a/internal/auth/ldap/ldap.go +++ b/internal/auth/ldap/ldap.go @@ -225,7 +225,7 @@ func (a *Auth) Lookup(_ context.Context, username string) (string, bool, error) req := ldap.NewSearchRequest( a.baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 2, 0, false, - strings.ReplaceAll(a.filterTemplate, "{username}", username), + strings.ReplaceAll(a.filterTemplate, "{username}", ldap.EscapeFilter(username)), []string{"dn"}, nil) res, err := conn.Search(req) if err != nil { @@ -252,12 +252,12 @@ func (a *Auth) AuthPlain(username, password string) error { var userDN string if a.dnTemplate != "" { - userDN = strings.ReplaceAll(a.dnTemplate, "{username}", username) + userDN = strings.ReplaceAll(a.dnTemplate, "{username}", ldap.EscapeDN(username)) } else { req := ldap.NewSearchRequest( a.baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 2, 0, false, - strings.ReplaceAll(a.filterTemplate, "{username}", username), + strings.ReplaceAll(a.filterTemplate, "{username}", ldap.EscapeFilter(username)), []string{"dn"}, nil) res, err := conn.Search(req) if err != nil { diff --git a/tests/conn.go b/tests/conn.go index 9ff86e8b..2d597e86 100644 --- a/tests/conn.go +++ b/tests/conn.go @@ -211,7 +211,7 @@ func (c *Conn) SMTPPlainAuth(username, password string, expectOk bool) { if expectOk { c.ExpectPattern("235 *") } else { - c.ExpectPattern("*") + c.ExpectPattern("5*") } } @@ -282,6 +282,13 @@ func (c *Conn) Close() error { return c.Conn.Close() } +func (c *Conn) MustClose() { + c.T.Helper() + if err := c.Close(); err != nil { + c.fatal("Close: %v", err) + } +} + func (c *Conn) Rebind(subtest *T) *Conn { cpy := *c cpy.T = subtest diff --git a/tests/ghsa_5835_4gvc_32pc_test.go b/tests/ghsa_5835_4gvc_32pc_test.go new file mode 100644 index 00000000..b7e0108f --- /dev/null +++ b/tests/ghsa_5835_4gvc_32pc_test.go @@ -0,0 +1,178 @@ +//go:build integration + +package tests_test + +import ( + "strconv" + "testing" + "time" + + "github.com/foxcpp/maddy/tests" + "github.com/jimlambrt/gldap" + "github.com/stretchr/testify/require" +) + +type searchEntry struct { + dn string + options []gldap.Option +} + +type MockLDAP struct { + T *testing.T + SearchEntries map[string][]searchEntry + AllowedBinds map[string]string +} + +func (ml *MockLDAP) HandleBind(w *gldap.ResponseWriter, r *gldap.Request) { + resp := r.NewBindResponse( + gldap.WithResponseCode(gldap.ResultInvalidCredentials), + ) + + m, err := r.GetSimpleBindMessage() + if err != nil { + require.NoError(ml.T, w.Write(resp)) + return + } + + pass, ok := ml.AllowedBinds[m.UserName] + if ok && pass == string(m.Password) { + resp.SetResultCode(gldap.ResultSuccess) + require.NoError(ml.T, w.Write(resp)) + } + + require.NoError(ml.T, w.Write(resp)) +} + +func (ml *MockLDAP) HandleSearch(w *gldap.ResponseWriter, r *gldap.Request) { + resp := r.NewSearchDoneResponse() + m, err := r.GetSearchMessage() + if err != nil { + ml.T.Logf("not a search message: %s", err) + require.NoError(ml.T, w.Write(resp)) + return + } + ml.T.Logf("search base dn: %s", m.BaseDN) + ml.T.Logf("search scope: %d", m.Scope) + ml.T.Logf("search filter: %s", m.Filter) + + entries := ml.SearchEntries[m.Filter] + for _, entry := range entries { + ldapEntry := r.NewSearchResponseEntry(entry.dn, entry.options...) + require.NoError(ml.T, w.Write(ldapEntry)) + } + + resp.SetResultCode(gldap.ResultSuccess) + require.NoError(ml.T, w.Write(resp)) +} + +func (ml *MockLDAP) Run(address string) { + s, err := gldap.NewServer() + if err != nil { + ml.T.Fatalf("unable to create server: %s", err.Error()) + } + + // create a router and add a bind handler + r, err := gldap.NewMux() + if err != nil { + ml.T.Fatalf("unable to create router: %s", err.Error()) + } + require.NoError(ml.T, r.Bind(ml.HandleBind)) + require.NoError(ml.T, r.Search(ml.HandleSearch)) + require.NoError(ml.T, s.Router(r)) + go func() { + require.NoError(ml.T, s.Run(address)) + }() + ml.T.Cleanup(func() { + require.NoError(ml.T, s.Stop()) + }) + + for !s.Ready() { + ml.T.Log("Waiting for server to start") + time.Sleep(100 * time.Millisecond) + } +} + +func TestLDAPInjectionFilter(tt *testing.T) { + tt.Parallel() + t := tests.NewT(tt) + + ldapPort := t.Port("ldap") + + ldapSrv := &MockLDAP{ + T: tt, + AllowedBinds: map[string]string{ + "DC=com,CN=bob": "bob_pass", + "DC=com,CN=alice": "alice_pass", + }, + SearchEntries: map[string][]searchEntry{ + "(&(objectClass=inetOrgPerson)(uid=alice))": { + { + dn: "DC=com,CN=alice", + options: []gldap.Option{ + gldap.WithAttributes(map[string][]string{ + "objectClass": {"inetOrgPerson"}, + "uid": {"alice"}, + "description": {"prefix_test"}, + }), + }, + }, + }, + "(&(objectClass=inetOrgPerson)(uid=bob))": { + { + dn: "DC=com,CN=bob", + options: []gldap.Option{ + gldap.WithAttributes(map[string][]string{ + "objectClass": {"inetOrgPerson"}, + "uid": {"bob"}, + "description": {"prefix_test"}, + }), + }, + }, + }, + "(&(objectClass=inetOrgPerson)(uid=bob)(description=prefix*))": { + { + dn: "DC=com,CN=bob", + options: []gldap.Option{ + gldap.WithAttributes(map[string][]string{ + "objectClass": {"inetOrgPerson"}, + "uid": {"bob"}, + "description": {"prefix_test"}, + }), + }, + }, + }, + }, + } + ldapSrv.Run(":" + strconv.Itoa(int(ldapPort))) + + t.Port("smtp") + t.DNS(nil) + t.Config(` + hostname mx.maddy.test + tls off + + auth.ldap ldap_auth { + urls ldap://127.0.0.1:{env:TEST_PORT_ldap} + bind plain "DC=com,CN=bob" "bob_pass" + base_dn "DC=com" + filter "(&(objectClass=inetOrgPerson)(uid={username}))" + } + + submission tcp://0.0.0.0:{env:TEST_PORT_smtp} { + auth &ldap_auth + deliver_to dummy + } + `) + t.Run(1) + defer t.Close() + + smtpConn := t.Conn("smtp") + defer smtpConn.MustClose() + smtpConn.SMTPNegotation("clieht.maddy.test", nil, nil) + smtpConn.SMTPPlainAuth("alice", "alice_pass", true) + + smtpConn2 := t.Conn("smtp") + defer smtpConn2.MustClose() + smtpConn2.SMTPNegotation("clieht.maddy.test", nil, nil) + smtpConn2.SMTPPlainAuth("bob)(description=prefix*", "bob_pass", false) +} From ec821b7d1197cc437255c36a159ead1b51099876 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年4月12日 14:25:04 +0300 Subject: [PATCH 155/171] maddy 0.9.3 --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index 2003b639..965065db 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.9.2 +0.9.3 From 2f6a4cf83a38b58b50cf0d2a59e7b63bd63ade86 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年4月12日 16:33:25 +0300 Subject: [PATCH 156/171] storage/imapsql: Upgrade go-imap-sql to fix SQLITE_BUSY issues Might fix #786 --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 37227d4d..128bb20b 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/foxcpp/go-imap-i18nlevel v0.0.0-20200208001533-d6ec88553005 github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613 github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed - github.com/foxcpp/go-imap-sql v0.5.1-0.20250124140007-8da5567429d5 + github.com/foxcpp/go-imap-sql v0.5.1-0.20260412133145-20097edd35ec github.com/foxcpp/go-mockdns v1.1.0 github.com/foxcpp/go-mtasts v0.0.0-20240130093538-1438da2e5932 github.com/go-ldap/ldap/v3 v3.4.10 diff --git a/go.sum b/go.sum index ea270b65..b843d206 100644 --- a/go.sum +++ b/go.sum @@ -320,6 +320,8 @@ github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed h1:1Jo7ge github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed/go.mod h1:Shows1vmkBWO40ChOClaUe6DUnZrsP1UPAuoWzIUdgQ= github.com/foxcpp/go-imap-sql v0.5.1-0.20250124140007-8da5567429d5 h1:jMxhw9qmwqg70qfMDWq0ImRHAduQjkTZOC9vBs5t2ug= github.com/foxcpp/go-imap-sql v0.5.1-0.20250124140007-8da5567429d5/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= +github.com/foxcpp/go-imap-sql v0.5.1-0.20260412133145-20097edd35ec h1:Jm71K60qrrnyISeLXMYKzSZe0RVco+aO/RJugJvafIM= +github.com/foxcpp/go-imap-sql v0.5.1-0.20260412133145-20097edd35ec/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= github.com/foxcpp/go-mockdns v0.0.0-20191216195825-5eabd8dbfe1f/go.mod h1:tPg4cp4nseejPd+UKxtCVQ2hUxNTZ7qQZJa7CLriIeo= github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= From 9db7e6150a39b1cdb2fb16b81fdfd0171b4544c5 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年4月12日 22:09:19 +0300 Subject: [PATCH 157/171] storage/imapsql: Fix handling of serialization errors --- internal/storage/imapsql/delivery.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/internal/storage/imapsql/delivery.go b/internal/storage/imapsql/delivery.go index a9ce32e8..55dc36b3 100644 --- a/internal/storage/imapsql/delivery.go +++ b/internal/storage/imapsql/delivery.go @@ -20,6 +20,7 @@ package imapsql import ( "context" + "errors" "runtime/trace" "github.com/emersion/go-imap" @@ -78,10 +79,11 @@ func (d *delivery) AddRcpt(ctx context.Context, rcptTo string, _ smtp.RcptOption userHeader.Add("Delivered-To", accountName) if err := d.d.AddRcpt(accountName, userHeader); err != nil { - if err == imapsql.ErrUserDoesntExists || err == backend.ErrNoSuchMailbox { + if errors.Is(err, imapsql.ErrUserDoesntExists) || errors.Is(err, backend.ErrNoSuchMailbox) { return userDoesNotExist(err) } - if _, ok := err.(imapsql.SerializationError); ok { + var serializationError imapsql.SerializationError + if errors.As(err, &serializationError) { return &exterrors.SMTPError{ Code: 453, EnhancedCode: exterrors.EnhancedCode{4, 3, 2}, @@ -115,11 +117,12 @@ func (d *delivery) Body(ctx context.Context, header textproto.Header, body buffe if d.msgMeta.Quarantine { if err := d.d.SpecialMailbox(imap.JunkAttr, d.store.junkMbox); err != nil { - if _, ok := err.(imapsql.SerializationError); ok { + var serializationError imapsql.SerializationError + if errors.As(err, &serializationError) { return &exterrors.SMTPError{ Code: 453, EnhancedCode: exterrors.EnhancedCode{4, 3, 2}, - Message: "Storage access serialiation problem, try again later", + Message: "Internal server error, try again later", TargetName: "imapsql", Err: err, } @@ -131,11 +134,12 @@ func (d *delivery) Body(ctx context.Context, header textproto.Header, body buffe header = header.Copy() header.Add("Return-Path", "<"+target.sanitizeforheader(d.mailfrom)+">") err := d.d.BodyParsed(header, body.Len(), body) - if _, ok := err.(imapsql.SerializationError); ok { + var serializationError imapsql.SerializationError + if errors.As(err, &serializationError) { return &exterrors.SMTPError{ Code: 453, EnhancedCode: exterrors.EnhancedCode{4, 3, 2}, - Message: "Storage access serialiation problem, try again later", + Message: "Internal server error, try again later", TargetName: "imapsql", Err: err, } From d74f5119a2a1a46fd4ed70ca66112c7848c0a393 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年4月12日 22:10:02 +0300 Subject: [PATCH 158/171] storage/imapsql: Upgrade go-imap-sql to disable Repeatable Read by default --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 128bb20b..d3d6e997 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/foxcpp/go-imap-i18nlevel v0.0.0-20200208001533-d6ec88553005 github.com/foxcpp/go-imap-mess v0.0.0-20230108134257-b7ec3a649613 github.com/foxcpp/go-imap-namespace v0.0.0-20200802091432-08496dd8e0ed - github.com/foxcpp/go-imap-sql v0.5.1-0.20260412133145-20097edd35ec + github.com/foxcpp/go-imap-sql v0.5.1-0.20260412184517-b5e85e90f14d github.com/foxcpp/go-mockdns v1.1.0 github.com/foxcpp/go-mtasts v0.0.0-20240130093538-1438da2e5932 github.com/go-ldap/ldap/v3 v3.4.10 diff --git a/go.sum b/go.sum index b843d206..e5bf2edb 100644 --- a/go.sum +++ b/go.sum @@ -322,6 +322,8 @@ github.com/foxcpp/go-imap-sql v0.5.1-0.20250124140007-8da5567429d5 h1:jMxhw9qmwq github.com/foxcpp/go-imap-sql v0.5.1-0.20250124140007-8da5567429d5/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= github.com/foxcpp/go-imap-sql v0.5.1-0.20260412133145-20097edd35ec h1:Jm71K60qrrnyISeLXMYKzSZe0RVco+aO/RJugJvafIM= github.com/foxcpp/go-imap-sql v0.5.1-0.20260412133145-20097edd35ec/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= +github.com/foxcpp/go-imap-sql v0.5.1-0.20260412184517-b5e85e90f14d h1:oiq5MLSSqd3sl4VNHKTlrwszWTHIx8+x8y/olInMJRo= +github.com/foxcpp/go-imap-sql v0.5.1-0.20260412184517-b5e85e90f14d/go.mod h1:LMlfyNkVs7v2zE6OVeGe9qWPmKFdXDmLNddPLodPVIw= github.com/foxcpp/go-mockdns v0.0.0-20191216195825-5eabd8dbfe1f/go.mod h1:tPg4cp4nseejPd+UKxtCVQ2hUxNTZ7qQZJa7CLriIeo= github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= From fb2eb3771c2494a4eb50b263658063410828d8fd Mon Sep 17 00:00:00 2001 From: Sean van Osnabrugge <18647660+osnabrugge@users.noreply.github.com> Date: 2026年4月16日 18:24:10 -0400 Subject: [PATCH 159/171] smtp: add LOGIN SASL auth directive --- internal/target/smtp/sasl.go | 10 ++++++++-- internal/target/smtp/sasl_test.go | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/internal/target/smtp/sasl.go b/internal/target/smtp/sasl.go index 75f5d4e5..96861721 100644 --- a/internal/target/smtp/sasl.go +++ b/internal/target/smtp/sasl.go @@ -57,12 +57,18 @@ func saslAuthDirective(_ *config.Map, node config.Node) (interface{}, error) { } return sasl.NewPlainClient("", msgMeta.Conn.AuthUser, msgMeta.Conn.AuthPassword), nil }, nil - case "plain": + case "plain", "login": if len(node.Args) != 3 { return nil, config.NodeErr(node, "two additional arguments are required (username, password)") } return func(*module.MsgMetadata) (sasl.Client, error) { - return sasl.NewPlainClient("", node.Args[1], node.Args[2]), nil + if node.Args[0] == "plain" { + return sasl.NewPlainClient("", node.Args[1], node.Args[2]), nil + } + if node.Args[0] == "login" { + return sasl.NewLoginClient(node.Args[1], node.Args[2]), nil + } + return nil, config.NodeErr(node, "unknown authentication mechanism: %s", node.Args[0]) }, nil case "external": if len(node.Args)> 1 { diff --git a/internal/target/smtp/sasl_test.go b/internal/target/smtp/sasl_test.go index a1054845..c28ec7e4 100644 --- a/internal/target/smtp/sasl_test.go +++ b/internal/target/smtp/sasl_test.go @@ -101,6 +101,23 @@ func TestSASL_Plain_AuthFail(t *testing.T) { } } +func TestSASL_Login_Directive(t *testing.T) { + factory := testSaslFactory(t, "login", "test", "testpass") + client, err := factory(nil) + if err != nil { + t.Fatal(err) + } + + mech, _, err := client.Start() + if err != nil { + t.Fatal(err) + } + + if mech != "LOGIN" { + t.Fatalf("expected LOGIN mechanism, got %q", mech) + } +} + func TestSASL_Forward(t *testing.T) { be, srv := testutils.SMTPServer(t, "127.0.0.1:"+testPort) defer func() { From b418bd94bd7fd9007a910f94a32608824627ed54 Mon Sep 17 00:00:00 2001 From: Denis Girko Date: 2026年4月23日 20:27:20 +0300 Subject: [PATCH 160/171] Fixed limiters group configuration Fixed typo in limiters group configuration --- internal/limits/limits.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/limits/limits.go b/internal/limits/limits.go index 7c1a39b0..34161f7a 100644 --- a/internal/limits/limits.go +++ b/internal/limits/limits.go @@ -118,8 +118,8 @@ func (g *Group) Configure(inlineArgs []string, cfg *config.Map) error { } if len(destL) != 0 { g.dest = limiters.NewBucketSet(func() limiters.L { - l := make([]limiters.L, 0, len(sourceL)) - for _, ctor := range sourceL { + l := make([]limiters.L, 0, len(destL)) + for _, ctor := range destL { l = append(l, ctor()) } return &limiters.MultiLimit{Wrapped: l} From 5841e95b47a7d7c607974d66b9016e9861f8965b Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Fri, 1 May 2026 00:41:46 +0300 Subject: [PATCH 161/171] log: Refactor to define proper loggers tree *container.C is also now passed to module constructor allowing access to container logger for sublogger initialization. This in turn required some package movement to break import cycles. --- cmd/maddy/maddy.conf | 13 ---- config.go | 6 +- framework/config/module/modconfig.go | 15 +++-- framework/container/container.go | 14 ++-- framework/{module => container}/lifetime.go | 7 +- framework/{module => container}/registry.go | 13 ++-- framework/log/log.go | 40 ++++++++---- framework/module/module.go | 22 ------- framework/module/{ => modules}/dummy.go | 12 ++-- framework/module/{ => modules}/modules.go | 34 +++++++++- internal/auth/dovecot_sasl/dovecot_sasl.go | 10 +-- internal/auth/external/externalauth.go | 16 +++-- internal/auth/ldap/ldap.go | 12 ++-- internal/auth/netauth/netauth.go | 15 +++-- internal/auth/pam/module.go | 10 +-- internal/auth/pass_table/table.go | 6 +- internal/auth/pass_table/table_test.go | 3 +- .../auth/plain_separate/plain_separate.go | 12 ++-- internal/auth/sasl.go | 2 +- internal/auth/shadow/module.go | 14 ++-- .../authorize_sender/authorize_sender.go | 11 ++-- internal/check/command/command.go | 15 +++-- internal/check/dkim/dkim.go | 12 ++-- internal/check/dkim/dkim_test.go | 3 +- internal/check/dnsbl/dnsbl.go | 12 ++-- internal/check/milter/milter.go | 16 +++-- internal/check/rspamd/rspamd.go | 16 +++-- internal/check/skeleton.go | 3 +- internal/check/spf/spf.go | 12 ++-- internal/check/stateless_check.go | 10 +-- internal/cli/ctl/moduleinit.go | 17 +++-- .../endpoint/dovecot_sasld/dovecot_sasl.go | 16 +++-- internal/endpoint/imap/imap.go | 49 +++++++------- internal/endpoint/openmetrics/om.go | 11 ++-- internal/endpoint/smtp/session.go | 12 ++-- internal/endpoint/smtp/smtp.go | 45 +++++++------ internal/endpoint/smtp/smtp_test.go | 10 +-- internal/endpoint/smtp/submission_test.go | 3 +- internal/imap_filter/command/command.go | 14 ++-- internal/imap_filter/group.go | 10 +-- internal/libdns/acmedns.go | 4 +- internal/libdns/alidns.go | 4 +- internal/libdns/cloudflare.go | 4 +- internal/libdns/digitalocean.go | 4 +- internal/libdns/gandi.go | 4 +- internal/libdns/gcore.go | 4 +- internal/libdns/googleclouddns.go | 4 +- internal/libdns/hetzner.go | 4 +- internal/libdns/leaseweb.go | 4 +- internal/libdns/metaname.go | 4 +- internal/libdns/namecheap.go | 4 +- internal/libdns/namedotcom.go | 4 +- internal/libdns/rfc2136.go | 4 +- internal/libdns/route53.go | 4 +- internal/libdns/vultr.go | 4 +- internal/limits/limits.go | 6 +- internal/modify/dkim/dkim.go | 12 ++-- internal/modify/dkim/dkim_test.go | 3 +- internal/modify/group.go | 4 +- internal/modify/replace_addr.go | 8 ++- internal/modify/replace_addr_test.go | 3 +- internal/msgpipeline/check_group.go | 4 +- internal/msgpipeline/check_runner.go | 4 +- internal/msgpipeline/module.go | 10 +-- internal/msgpipeline/msgpipeline.go | 4 +- internal/proxy_protocol/proxy_protocol.go | 8 +-- internal/smtpconn/smtpconn.go | 2 +- internal/storage/blob/fs/fs.go | 6 +- internal/storage/blob/s3/s3.go | 10 +-- internal/storage/blob/test_blob.go | 2 +- internal/storage/imapsql/delivery.go | 2 +- internal/storage/imapsql/imapsql.go | 56 ++++++++-------- internal/table/chain.go | 6 +- internal/table/email_localpart.go | 8 ++- internal/table/email_with_domain.go | 10 +-- internal/table/file.go | 10 +-- internal/table/file_test.go | 7 +- internal/table/identity.go | 6 +- internal/table/regexp.go | 6 +- internal/table/sql_query.go | 6 +- internal/table/sql_query_test.go | 3 +- internal/table/sql_table.go | 6 +- internal/table/static.go | 6 +- internal/target/delivery.go | 3 +- internal/target/queue/queue.go | 62 +++++++++--------- internal/target/queue/queue_test.go | 7 +- internal/target/remote/connect.go | 22 +++---- internal/target/remote/dane_delivery_test.go | 2 +- internal/target/remote/mxauth_test.go | 4 +- internal/target/remote/policy_group.go | 4 +- internal/target/remote/remote.go | 26 ++++---- internal/target/remote/remote_test.go | 7 +- internal/target/remote/security.go | 36 ++++++----- internal/target/skeleton.go | 3 +- internal/target/smtp/smtp_downstream.go | 16 +++-- internal/target/smtp/smtputf8_test.go | 3 +- internal/testutils/check.go | 4 +- internal/testutils/logger.go | 14 ++-- internal/testutils/modifier.go | 4 +- internal/tls/acme/acme.go | 10 +-- internal/tls/file.go | 10 +-- internal/tls/self_signed.go | 6 +- internal/updatepipe/pubsub/pq.go | 4 +- internal/updatepipe/pubsub_pipe.go | 2 +- internal/updatepipe/unix_pipe.go | 2 +- maddy.go | 64 +++++++++++++------ 106 files changed, 679 insertions(+), 487 deletions(-) delete mode 100644 cmd/maddy/maddy.conf rename framework/{module => container}/lifetime.go (97%) rename framework/{module => container}/registry.go (90%) rename framework/module/{ => modules}/dummy.go (86%) rename framework/module/{ => modules}/modules.go (69%) diff --git a/cmd/maddy/maddy.conf b/cmd/maddy/maddy.conf deleted file mode 100644 index 83f93f21..00000000 --- a/cmd/maddy/maddy.conf +++ /dev/null @@ -1,13 +0,0 @@ - -state_dir /tmp -runtime_dir /tmp - -smtp tcp://127.0.0.1:1234 { - tls off - auth ldap { - base_dn "1" - filter "2 " - } - hostname test - deliver_to dummy -} diff --git a/config.go b/config.go index 92053f94..8b5044f2 100644 --- a/config.go +++ b/config.go @@ -71,7 +71,7 @@ func LogOutputOption(args []string) (log.Output, error) { } return log.NopOutput{}, nil default: - // Log file paths are converted to absolute to make sure + // log file paths are converted to absolute to make sure // we will be able to recreate them in right location // after changing working directory to the state dir. absPath, err := filepath.Abs(arg) @@ -98,7 +98,7 @@ func LogOutputOption(args []string) (log.Output, error) { } func defaultLogOutput() (interface{}, error) { - return log.DefaultLogger.Out, nil + return nil, nil } func reinitLogging() { @@ -115,7 +115,7 @@ func reinitLogging() { } if err := out.Close(); err != nil { - log.Println("Can't close logger:", err) + log.Println("Can't close old logger:", err) } log.DefaultLogger.Out = newOut diff --git a/framework/config/module/modconfig.go b/framework/config/module/modconfig.go index 443bfebd..2b4eab6c 100644 --- a/framework/config/module/modconfig.go +++ b/framework/config/module/modconfig.go @@ -36,23 +36,24 @@ import ( "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) // createInlineModule is a helper function for config matchers that can create inline modules. -func createInlineModule(preferredNamespace, modName string) (module.Module, error) { - var newMod module.FuncNewModule +func createInlineModule(c *container.C, preferredNamespace, modName string) (module.Module, error) { + var newMod modules.FuncNewModule originalModName := modName // First try to extend the name with preferred namespace unless the name // already contains it. if !strings.Contains(modName, ".") && preferredNamespace != "" { modName = preferredNamespace + "." + modName - newMod = module.Get(modName) + newMod = modules.Get(modName) } // Then try global namespace for compatibility and complex modules. if newMod == nil { - newMod = module.Get(originalModName) + newMod = modules.Get(originalModName) } // Bail if both failed. @@ -60,7 +61,7 @@ func createInlineModule(preferredNamespace, modName string) (module.Module, erro return nil, fmt.Errorf("unknown module: %s (namespace: %s)", originalModName, preferredNamespace) } - return newMod(modName, "") + return newMod(c, modName, "") } // configureInlineModule constructs "faked" config tree and passes it to module @@ -73,7 +74,7 @@ func configureInlineModule(modObj module.Module, args []string, globals map[stri return err } - if li, ok := modObj.(module.LifetimeModule); ok { + if li, ok := modObj.(container.LifetimeModule); ok { container.Global.Lifetime.Add(li) } @@ -115,7 +116,7 @@ func ModuleFromNode(preferredNamespace string, args []string, inlineCfg config.N log.Debugf("%s:%d: reference %s", inlineCfg.File, inlineCfg.Line, args[0]) } else { log.Debugf("%s:%d: new module %s %v", inlineCfg.File, inlineCfg.Line, args[0], args[1:]) - modObj, err = createInlineModule(preferredNamespace, args[0]) + modObj, err = createInlineModule(container.Global, preferredNamespace, args[0]) } if err != nil { return err diff --git a/framework/container/container.go b/framework/container/container.go index dc1e0124..25690522 100644 --- a/framework/container/container.go +++ b/framework/container/container.go @@ -20,7 +20,6 @@ package container import ( "github.com/foxcpp/maddy/framework/log" - "github.com/foxcpp/maddy/framework/module" ) type GlobalConfig struct { @@ -53,16 +52,17 @@ type GlobalConfig struct { type C struct { Config GlobalConfig - DefaultLogger log.Logger - Modules *module.Registry - Lifetime *module.LifetimeTracker + DefaultLogger *log.Logger + Modules *Registry + Lifetime *LifetimeTracker } func New() *C { + rootLog := log.DefaultLogger.Sublogger("") return &C{ - DefaultLogger: log.DefaultLogger, - Modules: module.NewRegistry(log.DefaultLogger.Sublogger("registry")), - Lifetime: module.NewLifetime(log.DefaultLogger.Sublogger("lifetime")), + DefaultLogger: rootLog, + Modules: NewRegistry(rootLog.Sublogger("registry")), + Lifetime: NewLifetime(rootLog.Sublogger("lifetime")), } } diff --git a/framework/module/lifetime.go b/framework/container/lifetime.go similarity index 97% rename from framework/module/lifetime.go rename to framework/container/lifetime.go index 2ccb08d4..993f6d94 100644 --- a/framework/module/lifetime.go +++ b/framework/container/lifetime.go @@ -16,24 +16,25 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -package module +package container import ( "fmt" "github.com/foxcpp/maddy/framework/log" + "github.com/foxcpp/maddy/framework/module" ) // LifetimeModule is a stateful module that needs to have post-configuration // startup and graceful shutdown functionality. type LifetimeModule interface { - Module + module.Module Start() error Stop() error } type ReloadModule interface { - Module + module.Module Reload() error } diff --git a/framework/module/registry.go b/framework/container/registry.go similarity index 90% rename from framework/module/registry.go rename to framework/container/registry.go index acb0b190..5cdf9f00 100644 --- a/framework/module/registry.go +++ b/framework/container/registry.go @@ -16,12 +16,13 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -package module +package container import ( "errors" "github.com/foxcpp/maddy/framework/log" + "github.com/foxcpp/maddy/framework/module" ) var ( @@ -30,7 +31,7 @@ var ( ) type registryEntry struct { - Mod Module + Mod module.Module LazyInit func() error } @@ -56,7 +57,7 @@ func NewRegistry(log *log.Logger) *Registry { // // lazyInit function will be called on first request to get the module from // registry. -func (r *Registry) Register(mod Module, lazyInit func() error) error { +func (r *Registry) Register(mod module.Module, lazyInit func() error) error { instName := mod.InstanceName() if instName == "" { panic("module with empty instance name cannot be added to the registry") @@ -114,7 +115,7 @@ func (r *Registry) ensureInitialized(name string, entry *registryEntry) error { return nil } -func (r *Registry) Get(name string) (Module, error) { +func (r *Registry) Get(name string) (module.Module, error) { if name == "" { panic("cannot get module with empty name") } @@ -135,8 +136,8 @@ func (r *Registry) Get(name string) (Module, error) { return mod.Mod, nil } -func (r *Registry) NotInitialized() []Module { - notinit := make([]Module, 0, len(r.instances)-len(r.initialized)) +func (r *Registry) NotInitialized() []module.Module { + notinit := make([]module.Module, 0, len(r.instances)-len(r.initialized)) for name, mod := range r.instances { if _, ok := r.initialized[name]; ok { continue diff --git a/framework/log/log.go b/framework/log/log.go index 366cf156..3ad291a6 100644 --- a/framework/log/log.go +++ b/framework/log/log.go @@ -59,10 +59,7 @@ func (l *Logger) Zap() *zap.Logger { } func (l *Logger) IsDebug() bool { - if l.Parent == nil { - return l.Debug - } - return l.Debug || l.Parent.IsDebug() + return l.Debug || (l.Parent != nil && l.Parent.IsDebug()) } func (l *Logger) Debugf(format string, val ...interface{}) { @@ -217,19 +214,30 @@ func (l *Logger) DebugWriter() io.Writer { return l2 } +func (l *Logger) output() Output { + if l.Out != nil { + return l.Out + } + if l.Parent != nil { + return l.Parent.output() + } + + if DefaultLogger.Out == nil { + panic("DefaultLogger.Out is not set") + } + if l.Parent == nil && l != &DefaultLogger { + DefaultLogger.Out.Write(time.Now(), true, "logger "+l.Name+" has no parent, this is a bug") + } + return DefaultLogger.Out +} + func (l *Logger) log(debug bool, s string) { if l.Name != "" { s = l.Name + ": " + s } - if l.Out != nil { - l.Out.Write(time.Now(), debug, s) - return - } - if DefaultLogger.Out != nil { - DefaultLogger.Out.Write(time.Now(), debug, s) - return - } + out := l.output() + out.Write(time.Now(), debug, s) // Logging is disabled - do nothing. } @@ -240,9 +248,7 @@ func (l *Logger) Sublogger(name string) *Logger { } return &Logger{ Parent: l, - Out: l.Out, Name: name, - Debug: l.Debug, } } @@ -253,6 +259,12 @@ func (l *Logger) Sublogger(name string) *Logger { // however underlying log.Output may provide necessary serialization. var DefaultLogger = Logger{Out: WriterOutput(os.Stderr, false)} +// NopLogger is the logger that discards all messages written to it. +var NopLogger = Logger{ + Parent: &DefaultLogger, + Out: NopOutput{}, +} + func Debugf(format string, val ...interface{}) { DefaultLogger.Debugf(format, val...) } func Debugln(val ...interface{}) { DefaultLogger.Debugln(val...) } func Printf(format string, val ...interface{}) { DefaultLogger.Printf(format, val...) } diff --git a/framework/module/module.go b/framework/module/module.go index 2f949c14..2af39990 100644 --- a/framework/module/module.go +++ b/framework/module/module.go @@ -50,25 +50,3 @@ type Module interface { // string if module instance is unnamed. InstanceName() string } - -// FuncNewModule is function that creates new instance of module with specified name. -// -// Module.InstanceName() of the returned module object should return instName. -// If module is defined inline, instName will be empty. -// -// Returned Module may additionally implement LifetimeModule. -type FuncNewModule func(modName, instName string) (Module, error) - -// FuncNewEndpoint is a function that creates new instance of endpoint -// module. -// -// Compared to regular modules, endpoint module instances are: -// - Not registered in the global registry. -// - Can't be defined inline. -// - Don't have an unique name -// - All config arguments are always passed as an 'addrs' slice and not used as -// names. -// -// As a consequence of having no per-instance name, InstanceName of the module -// object always returns the same value as Name. -type FuncNewEndpoint func(modName string, addrs []string) (LifetimeModule, error) diff --git a/framework/module/dummy.go b/framework/module/modules/dummy.go similarity index 86% rename from framework/module/dummy.go rename to framework/module/modules/dummy.go index 0722fb26..7d2051d1 100644 --- a/framework/module/dummy.go +++ b/framework/module/modules/dummy.go @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -package module +package modules import ( "context" @@ -25,6 +25,8 @@ import ( "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" + "github.com/foxcpp/maddy/framework/module" ) // Dummy is a struct that implements PlainAuth and DeliveryTarget @@ -58,7 +60,7 @@ func (d *Dummy) Configure(_ []string, _ *config.Map) error { return nil } -func (d *Dummy) StartDelivery(ctx context.Context, msgMeta *MsgMetadata, mailFrom string) (Delivery, error) { +func (d *Dummy) StartDelivery(ctx context.Context, msgMeta *module.MsgMetadata, mailFrom string) (module.Delivery, error) { return dummyDelivery{}, nil } @@ -80,8 +82,6 @@ func (dd dummyDelivery) Commit(ctx context.Context) error { return nil } -func init() { - Register("dummy", func(_, instName string) (Module, error) { - return &Dummy{instName: instName}, nil - }) +func NewDummy(_ *container.C, _, instName string) (module.Module, error) { + return &Dummy{instName: instName}, nil } diff --git a/framework/module/modules.go b/framework/module/modules/modules.go similarity index 69% rename from framework/module/modules.go rename to framework/module/modules/modules.go index d7669808..44604ab9 100644 --- a/framework/module/modules.go +++ b/framework/module/modules/modules.go @@ -16,14 +16,38 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -package module +package modules import ( "sync" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" + "github.com/foxcpp/maddy/framework/module" ) +// FuncNewModule is function that creates new instance of module with specified name. +// +// Module.InstanceName() of the returned module object should return instName. +// If module is defined inline, instName will be empty. +// +// Returned Module may additionally implement LifetimeModule. +type FuncNewModule func(c *container.C, modName, instName string) (module.Module, error) + +// FuncNewEndpoint is a function that creates new instance of endpoint +// module. +// +// Compared to regular modules, endpoint module instances are: +// - Not registered in the global registry. +// - Can't be defined inline. +// - Don't have an unique name +// - All config arguments are always passed as an 'addrs' slice and not used as +// names. +// +// As a consequence of having no per-instance name, InstanceName of the module +// object always returns the same value as Name. +type FuncNewEndpoint func(c *container.C, modName string, addrs []string) (container.LifetimeModule, error) + var ( modules = make(map[string]FuncNewModule) endpoints = make(map[string]FuncNewEndpoint) @@ -52,9 +76,9 @@ func Register(name string, factory FuncNewModule) { // It prints warning to the log about name being deprecated and suggests using // a new name. func RegisterDeprecated(name, newName string, factory FuncNewModule) { - Register(name, func(modName, instName string) (Module, error) { + Register(name, func(c *container.C, modName, instName string) (module.Module, error) { log.Printf("module initialized via deprecated name %s, %s should be used instead; deprecated name may be removed in the next version", name, newName) - return factory(modName, instName) + return factory(c, modName, instName) }) } @@ -94,3 +118,7 @@ func RegisterEndpoint(name string, factory FuncNewEndpoint) { endpoints[name] = factory } + +func init() { + Register("dummy", NewDummy) +} diff --git a/internal/auth/dovecot_sasl/dovecot_sasl.go b/internal/auth/dovecot_sasl/dovecot_sasl.go index fc0585e6..9b30f924 100644 --- a/internal/auth/dovecot_sasl/dovecot_sasl.go +++ b/internal/auth/dovecot_sasl/dovecot_sasl.go @@ -25,16 +25,18 @@ import ( "github.com/emersion/go-sasl" dovecotsasl "github.com/foxcpp/go-dovecot-sasl" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/auth" ) type Auth struct { instName string serverEndpoint string - log log.Logger + log *log.Logger network string addr string @@ -44,10 +46,10 @@ type Auth struct { const modName = "dovecot_sasl" -func New(_, instName string) (module.Module, error) { +func New(c *container.C, _, instName string) (module.Module, error) { a := &Auth{ instName: instName, - log: log.Logger{Name: modName, Debug: log.DefaultLogger.Debug}, + log: c.DefaultLogger.Sublogger(modName), } return a, nil @@ -162,5 +164,5 @@ func (a *Auth) AuthPlain(username, password string) error { } func init() { - module.Register(modName, New) + modules.Register(modName, New) } diff --git a/internal/auth/external/externalauth.go b/internal/auth/external/externalauth.go index 144864da..1d3f4ae3 100644 --- a/internal/auth/external/externalauth.go +++ b/internal/auth/external/externalauth.go @@ -25,8 +25,10 @@ import ( "path/filepath" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/auth" ) @@ -38,14 +40,14 @@ type ExternalAuth struct { perDomain bool domains []string - Log log.Logger + log *log.Logger } -func NewExternalAuth(modName, instName string) (module.Module, error) { +func New(c *container.C, modName, instName string) (module.Module, error) { ea := &ExternalAuth{ modName: modName, instName: instName, - Log: log.Logger{Name: modName}, + log: c.DefaultLogger.Sublogger(modName), } return ea, nil @@ -64,7 +66,7 @@ func (ea *ExternalAuth) Configure(inlineArgs []string, cfg *config.Map) error { return errors.New("external: inline arguments are not used") } - cfg.Bool("debug", false, false, &ea.Log.Debug) + cfg.Bool("debug", false, false, &ea.log.Debug) cfg.Bool("perdomain", false, false, &ea.perDomain) cfg.StringList("domains", false, false, nil, &ea.domains) cfg.String("helper", false, false, "", &ea.helperPath) @@ -76,7 +78,7 @@ func (ea *ExternalAuth) Configure(inlineArgs []string, cfg *config.Map) error { } if ea.helperPath != "" { - ea.Log.Debugln("using helper:", ea.helperPath) + ea.log.Debugln("using helper:", ea.helperPath) } else { ea.helperPath = filepath.Join(config.LibexecDirectory, "maddy-auth-helper") } @@ -84,7 +86,7 @@ func (ea *ExternalAuth) Configure(inlineArgs []string, cfg *config.Map) error { return fmt.Errorf("%s doesn't exist", ea.helperPath) } - ea.Log.Debugln("using helper:", ea.helperPath) + ea.log.Debugln("using helper:", ea.helperPath) return nil } @@ -99,5 +101,5 @@ func (ea *ExternalAuth) AuthPlain(username, password string) error { } func init() { - module.Register("auth.external", NewExternalAuth) + modules.Register("auth.external", New) } diff --git a/internal/auth/ldap/ldap.go b/internal/auth/ldap/ldap.go index 06890fd5..af8c6304 100644 --- a/internal/auth/ldap/ldap.go +++ b/internal/auth/ldap/ldap.go @@ -12,8 +12,10 @@ import ( "github.com/foxcpp/maddy/framework/config" tls2 "github.com/foxcpp/maddy/framework/config/tls" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/go-ldap/ldap/v3" ) @@ -37,13 +39,13 @@ type Auth struct { conn *ldap.Conn connLock sync.Mutex - log log.Logger + log *log.Logger } -func New(modName, instName string) (module.Module, error) { +func New(c *container.C, modName, instName string) (module.Module, error) { return &Auth{ instName: instName, - log: log.Logger{Name: modName}, + log: c.DefaultLogger.Sublogger(modName), }, nil } @@ -297,6 +299,6 @@ func (a *Auth) Stop() error { func init() { var _ module.PlainAuth = &Auth{} var _ module.Table = &Auth{} - module.Register(modName, New) - module.Register("table.ldap", New) + modules.Register(modName, New) + modules.Register("table.ldap", New) } diff --git a/internal/auth/netauth/netauth.go b/internal/auth/netauth/netauth.go index db84cd18..ce6715e6 100644 --- a/internal/auth/netauth/netauth.go +++ b/internal/auth/netauth/netauth.go @@ -5,8 +5,10 @@ import ( "fmt" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/hashicorp/go-hclog" "github.com/netauth/netauth/pkg/netauth" ) @@ -16,8 +18,8 @@ const modName = "auth.netauth" func init() { var _ module.PlainAuth = &Auth{} var _ module.Table = &Auth{} - module.Register(modName, New) - module.Register("table.netauth", New) + modules.Register(modName, New) + modules.Register("table.netauth", New) } // Auth binds all methods related to the NetAuth client library. @@ -27,22 +29,23 @@ type Auth struct { nacl *netauth.Client - log log.Logger + log *log.Logger } // New creates a new instance of the NetAuth module. -func New(modName, instName string) (module.Module, error) { +func New(c *container.C, modName, instName string) (module.Module, error) { return &Auth{ instName: instName, - log: log.Logger{Name: modName}, + log: c.DefaultLogger.Sublogger(modName), }, nil } + func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { if len(inlineArgs)> 0 { return fmt.Errorf("%s: inline arguments are not used", modName) } - l := hclog.New(&hclog.LoggerOptions{Output: &a.log}) + l := hclog.New(&hclog.LoggerOptions{Output: a.log}) n, err := netauth.NewWithLog(l) if err != nil { return err diff --git a/internal/auth/pam/module.go b/internal/auth/pam/module.go index da8e59f9..12977a52 100644 --- a/internal/auth/pam/module.go +++ b/internal/auth/pam/module.go @@ -25,8 +25,10 @@ import ( "path/filepath" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/auth/external" ) @@ -35,13 +37,13 @@ type Auth struct { useHelper bool helperPath string - Log log.Logger + Log *log.Logger } -func New(modName, instName string) (module.Module, error) { +func New(c *container.C, modName, instName string) (module.Module, error) { return &Auth{ instName: instName, - Log: log.Logger{Name: modName}, + Log: c.DefaultLogger.Sublogger(modName), }, nil } @@ -91,5 +93,5 @@ func (a *Auth) AuthPlain(username, password string) error { } func init() { - module.Register("auth.pam", New) + modules.Register("auth.pam", New) } diff --git a/internal/auth/pass_table/table.go b/internal/auth/pass_table/table.go index 626913b4..8d3cea34 100644 --- a/internal/auth/pass_table/table.go +++ b/internal/auth/pass_table/table.go @@ -25,7 +25,9 @@ import ( "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "golang.org/x/crypto/bcrypt" "golang.org/x/text/secure/precis" ) @@ -37,7 +39,7 @@ type Auth struct { table module.Table } -func New(modName, instName string) (module.Module, error) { +func New(_ *container.C, modName, instName string) (module.Module, error) { return &Auth{ modName: modName, instName: instName, @@ -192,5 +194,5 @@ func (a *Auth) DeleteUser(username string) error { } func init() { - module.Register("auth.pass_table", New) + modules.Register("auth.pass_table", New) } diff --git a/internal/auth/pass_table/table_test.go b/internal/auth/pass_table/table_test.go index 7f688cac..7c8cf614 100644 --- a/internal/auth/pass_table/table_test.go +++ b/internal/auth/pass_table/table_test.go @@ -22,13 +22,14 @@ import ( "testing" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/internal/testutils" ) func TestAuth_AuthPlain(t *testing.T) { addSHA256() - mod, err := New("pass_table", "") + mod, err := New(container.New(), "pass_table", "") if err != nil { t.Fatal(err) } diff --git a/internal/auth/plain_separate/plain_separate.go b/internal/auth/plain_separate/plain_separate.go index b3e06016..ae437143 100644 --- a/internal/auth/plain_separate/plain_separate.go +++ b/internal/auth/plain_separate/plain_separate.go @@ -25,8 +25,10 @@ import ( "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) type Auth struct { @@ -38,15 +40,15 @@ type Auth struct { onlyFirstID bool - Log log.Logger + log *log.Logger } -func NewAuth(modName, instName string) (module.Module, error) { +func New(c *container.C, modName, instName string) (module.Module, error) { a := &Auth{ modName: modName, instName: instName, onlyFirstID: false, - Log: log.Logger{Name: modName}, + log: c.DefaultLogger.Sublogger(modName), } return a, nil @@ -65,7 +67,7 @@ func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { return errors.New("plain_separate: inline arguments are not used") } - cfg.Bool("debug", false, false, &a.Log.Debug) + cfg.Bool("debug", false, false, &a.log.Debug) cfg.Callback("user", func(m *config.Map, node config.Node) error { var tbl module.Table err := modconfig.ModuleFromNode("table", node.Args, node, m.Globals, &tbl) @@ -141,5 +143,5 @@ func (a *Auth) AuthPlain(username, password string) error { } func init() { - module.Register("auth.plain_separate", NewAuth) + modules.Register("auth.plain_separate", New) } diff --git a/internal/auth/sasl.go b/internal/auth/sasl.go index 616ad618..591e3764 100644 --- a/internal/auth/sasl.go +++ b/internal/auth/sasl.go @@ -47,7 +47,7 @@ var ( // It supports reporting of multiple authorization identities so multiple // accounts can be associated with a single set of credentials. type SASLAuth struct { - Log log.Logger + Log *log.Logger OnlyFirstID bool EnableLogin bool diff --git a/internal/auth/shadow/module.go b/internal/auth/shadow/module.go index 90a65a23..8223af28 100644 --- a/internal/auth/shadow/module.go +++ b/internal/auth/shadow/module.go @@ -28,8 +28,10 @@ import ( "path/filepath" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/auth/external" ) @@ -38,13 +40,13 @@ type Auth struct { useHelper bool helperPath string - Log log.Logger + log *log.Logger } -func New(modName, instName string) (module.Module, error) { +func New(c *container.C, modName, instName string) (module.Module, error) { return &Auth{ instName: instName, - Log: log.Logger{Name: modName}, + log: c.DefaultLogger.Sublogger(modName), }, nil } @@ -61,7 +63,7 @@ func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { return errors.New("shadow: inline arguments are not used") } - cfg.Bool("debug", true, false, &a.Log.Debug) + cfg.Bool("debug", true, false, &a.log.Debug) cfg.Bool("use_helper", false, false, &a.useHelper) if _, err := cfg.Process(); err != nil { return err @@ -81,7 +83,7 @@ func (a *Auth) Configure(inlineArgs []string, cfg *config.Map) error { return fmt.Errorf("shadow: can't read /etc/shadow: %v", err) } if err := f.Close(); err != nil { - a.Log.Error("can't close /etc/shadow file", err) + a.log.Error("can't close /etc/shadow file", err) } } @@ -137,5 +139,5 @@ func (a *Auth) AuthPlain(username, password string) error { } func init() { - module.Register("auth.shadow", New) + modules.Register("auth.shadow", New) } diff --git a/internal/check/authorize_sender/authorize_sender.go b/internal/check/authorize_sender/authorize_sender.go index ab091c56..e1785fdb 100644 --- a/internal/check/authorize_sender/authorize_sender.go +++ b/internal/check/authorize_sender/authorize_sender.go @@ -27,9 +27,11 @@ import ( "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/authz" "github.com/foxcpp/maddy/internal/table" "github.com/foxcpp/maddy/internal/target" @@ -39,7 +41,7 @@ const modName = "check.authorize_sender" type Check struct { instName string - log log.Logger + log *log.Logger checkHeader bool emailPrepare module.Table @@ -53,9 +55,10 @@ type Check struct { authNorm authz.NormalizeFunc } -func New(_, instName string) (module.Module, error) { +func New(c *container.C, modName, instName string) (module.Module, error) { return &Check{ instName: instName, + log: c.DefaultLogger.Sublogger(modName), }, nil } @@ -108,7 +111,7 @@ func (c *Check) Configure(inlineArgs []string, cfg *config.Map) error { type state struct { c *Check msgMeta *module.MsgMetadata - log log.Logger + log *log.Logger } func (c *Check) CheckStateForMsg(_ context.Context, msgMeta *module.MsgMetadata) (module.CheckState, error) { @@ -306,5 +309,5 @@ func (s *state) Close() error { } func init() { - module.Register(modName, New) + modules.Register(modName, New) } diff --git a/internal/check/command/command.go b/internal/check/command/command.go index 512f147a..a69db4fd 100644 --- a/internal/check/command/command.go +++ b/internal/check/command/command.go @@ -37,9 +37,11 @@ import ( "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/target" ) @@ -58,7 +60,7 @@ var placeholderRe = regexp.MustCompile(`{[a-zA-Z0-9_]+?}`) type Check struct { instName string - log log.Logger + log *log.Logger stage Stage actions map[int]modconfig.FailAction @@ -66,9 +68,10 @@ type Check struct { cmdArgs []string } -func New(modName, instName string) (module.Module, error) { - c := &Check{ +func New(c *container.C, modName, instName string) (module.Module, error) { + chk := &Check{ instName: instName, + log: c.DefaultLogger.Sublogger(modName), actions: map[int]modconfig.FailAction{ 1: { Reject: true, @@ -79,7 +82,7 @@ func New(modName, instName string) (module.Module, error) { }, } - return c, nil + return chk, nil } func (c *Check) Name() string { @@ -140,7 +143,7 @@ func (c *Check) Configure(inlineArgs []string, cfg *config.Map) error { type state struct { c *Check msgMeta *module.MsgMetadata - log log.Logger + log *log.Logger mailFrom string rcpts []string @@ -397,5 +400,5 @@ func (s *state) Close() error { } func init() { - module.Register(modName, New) + modules.Register(modName, New) } diff --git a/internal/check/dkim/dkim.go b/internal/check/dkim/dkim.go index b6921d40..9178cb98 100644 --- a/internal/check/dkim/dkim.go +++ b/internal/check/dkim/dkim.go @@ -33,16 +33,18 @@ import ( "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/dns" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/target" ) type Check struct { instName string - log log.Logger + log *log.Logger requiredFields map[string]struct{} brokenSigAction modconfig.FailAction @@ -52,10 +54,10 @@ type Check struct { resolver dns.Resolver } -func New(_, instName string) (module.Module, error) { +func New(c *container.C, modName, instName string) (module.Module, error) { return &Check{ instName: instName, - log: log.Logger{Name: "check.dkim"}, + log: c.DefaultLogger.Sublogger(modName), resolver: dns.DefaultResolver(), }, nil } @@ -102,7 +104,7 @@ func (c *Check) InstanceName() string { type dkimCheckState struct { c *Check msgMeta *module.MsgMetadata - log log.Logger + log *log.Logger } func (d *dkimCheckState) CheckConnection(ctx context.Context) module.CheckResult { @@ -269,5 +271,5 @@ func (c *Check) CheckStateForMsg(ctx context.Context, msgMeta *module.MsgMetadat } func init() { - module.Register("check.dkim", New) + modules.Register("check.dkim", New) } diff --git a/internal/check/dkim/dkim_test.go b/internal/check/dkim/dkim_test.go index b4054124..70fafa53 100644 --- a/internal/check/dkim/dkim_test.go +++ b/internal/check/dkim/dkim_test.go @@ -28,6 +28,7 @@ import ( "github.com/foxcpp/go-mockdns" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/testutils" @@ -84,7 +85,7 @@ Joe. func testCheck(t *testing.T, zones map[string]mockdns.Zone, cfg []config.Node) *Check { t.Helper() - mod, err := New("check.dkim", "") + mod, err := New(container.New(), "check.dkim", "") if err != nil { t.Fatal(err) } diff --git a/internal/check/dnsbl/dnsbl.go b/internal/check/dnsbl/dnsbl.go index 77b5e3ea..ae292185 100644 --- a/internal/check/dnsbl/dnsbl.go +++ b/internal/check/dnsbl/dnsbl.go @@ -30,10 +30,12 @@ import ( "github.com/foxcpp/maddy/framework/address" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/dns" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/target" "golang.org/x/sync/errgroup" ) @@ -72,15 +74,15 @@ type DNSBL struct { rejectThres int resolver dns.Resolver - log log.Logger + log *log.Logger } -func New(_, instName string) (module.Module, error) { +func New(c *container.C, modName, instName string) (module.Module, error) { return &DNSBL{ instName: instName, resolver: dns.DefaultResolver(), - log: log.Logger{Name: "dnsbl"}, + log: c.DefaultLogger.Sublogger(modName), }, nil } @@ -462,7 +464,7 @@ func (bl *DNSBL) CheckConnection(ctx context.Context, state *module.ConnState) e type state struct { bl *DNSBL msgMeta *module.MsgMetadata - log log.Logger + log *log.Logger } func (bl *DNSBL) CheckStateForMsg(ctx context.Context, msgMeta *module.MsgMetadata) (module.CheckState, error) { @@ -506,5 +508,5 @@ func (*state) Close() error { } func init() { - module.Register("check.dnsbl", New) + modules.Register("check.dnsbl", New) } diff --git a/internal/check/milter/milter.go b/internal/check/milter/milter.go index 38f81930..2a6d6e9e 100644 --- a/internal/check/milter/milter.go +++ b/internal/check/milter/milter.go @@ -30,9 +30,11 @@ import ( "github.com/emersion/go-milter" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/target" ) @@ -43,16 +45,16 @@ type Check struct { milterUrl string failOpen bool instName string - log log.Logger + log *log.Logger } -func New(_, instName string) (module.Module, error) { - c := &Check{ +func New(c *container.C, _, instName string) (module.Module, error) { + chk := &Check{ instName: instName, - log: log.Logger{Name: modName, Debug: log.DefaultLogger.Debug}, + log: c.DefaultLogger.Sublogger(modName), } - return c, nil + return chk, nil } func (c *Check) Name() string { @@ -111,7 +113,7 @@ type state struct { session *milter.ClientSession msgMeta *module.MsgMetadata skipChecks bool - log log.Logger + log *log.Logger } func (c *Check) CheckStateForMsg(ctx context.Context, msgMeta *module.MsgMetadata) (module.CheckState, error) { @@ -444,5 +446,5 @@ var ( ) func init() { - module.Register(modName, New) + modules.Register(modName, New) } diff --git a/internal/check/rspamd/rspamd.go b/internal/check/rspamd/rspamd.go index 4f32edf7..f03bdeb3 100644 --- a/internal/check/rspamd/rspamd.go +++ b/internal/check/rspamd/rspamd.go @@ -35,9 +35,11 @@ import ( "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" tls2 "github.com/foxcpp/maddy/framework/config/tls" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/target" ) @@ -45,7 +47,7 @@ const modName = "check.rspamd" type Check struct { instName string - log log.Logger + log *log.Logger apiPath string flags string @@ -63,14 +65,14 @@ type Check struct { client *http.Client } -func New(modName, instName string) (module.Module, error) { - c := &Check{ +func New(c *container.C, modName, instName string) (module.Module, error) { + chk := &Check{ instName: instName, client: http.DefaultClient, - log: log.Logger{Name: modName, Debug: log.DefaultLogger.Debug}, + log: c.DefaultLogger.Sublogger(modName), } - return c, nil + return chk, nil } func (c *Check) Name() string { @@ -146,7 +148,7 @@ func (c *Check) Configure(inlineArgs []string, cfg *config.Map) error { type state struct { c *Check msgMeta *module.MsgMetadata - log log.Logger + log *log.Logger mailFrom string rcpt []string @@ -378,5 +380,5 @@ func (s *state) Close() error { } func init() { - module.Register(modName, New) + modules.Register(modName, New) } diff --git a/internal/check/skeleton.go b/internal/check/skeleton.go index f3743596..3bfc9cde 100644 --- a/internal/check/skeleton.go +++ b/internal/check/skeleton.go @@ -34,6 +34,7 @@ import ( "github.com/foxcpp/maddy/framework/config" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/target" ) @@ -97,5 +98,5 @@ func (s *state) Close() error { } func init() { - module.Register(modName, New) + modules.Register(modName, New) } diff --git a/internal/check/spf/spf.go b/internal/check/spf/spf.go index a799cd85..01154b7d 100644 --- a/internal/check/spf/spf.go +++ b/internal/check/spf/spf.go @@ -34,10 +34,12 @@ import ( "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/dns" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" maddydmarc "github.com/foxcpp/maddy/internal/dmarc" "github.com/foxcpp/maddy/internal/target" "golang.org/x/net/idna" @@ -56,14 +58,14 @@ type Check struct { permerrAction modconfig.FailAction temperrAction modconfig.FailAction - log log.Logger + log *log.Logger resolver dns.Resolver } -func New(_, instName string) (module.Module, error) { +func New(c *container.C, _, instName string) (module.Module, error) { return &Check{ instName: instName, - log: log.Logger{Name: modName}, + log: c.DefaultLogger.Sublogger(modName), resolver: dns.DefaultResolver(), }, nil } @@ -120,7 +122,7 @@ type state struct { c *Check msgMeta *module.MsgMetadata spfFetch chan spfRes - log log.Logger + log *log.Logger skip bool } @@ -416,5 +418,5 @@ func (s *state) Close() error { } func init() { - module.Register(modName, New) + modules.Register(modName, New) } diff --git a/internal/check/stateless_check.go b/internal/check/stateless_check.go index 729c106b..cda7faf6 100644 --- a/internal/check/stateless_check.go +++ b/internal/check/stateless_check.go @@ -27,9 +27,11 @@ import ( "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/dns" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/target" ) @@ -47,7 +49,7 @@ type ( // Logger that should be used by the check for logging, note that it is // already wrapped to append Msg ID to all messages so check code // should not do the same. - Logger log.Logger + Logger *log.Logger } FuncConnCheck func(checkContext StatelessCheckContext) module.CheckResult FuncSenderCheck func(checkContext StatelessCheckContext, mailFrom string) module.CheckResult @@ -59,7 +61,7 @@ type statelessCheck struct { modName string instName string resolver dns.Resolver - logger log.Logger + logger *log.Logger // One used by Init if config option is not passed by a user. defaultFailAction modconfig.FailAction @@ -185,12 +187,12 @@ func (c *statelessCheck) InstanceName() string { // code doesn't need to know about it. It should assume that it is always "Reject" and hence it should // populate Reason field of the result object with the relevant error description. func RegisterStatelessCheck(name string, defaultFailAction modconfig.FailAction, connCheck FuncConnCheck, senderCheck FuncSenderCheck, rcptCheck FuncRcptCheck, bodyCheck FuncBodyCheck) { - module.Register(name, func(modName, instName string) (module.Module, error) { + modules.Register(name, func(c *container.C, modName, instName string) (module.Module, error) { return &statelessCheck{ modName: modName, instName: instName, resolver: dns.DefaultResolver(), - logger: log.Logger{Name: modName}, + logger: c.DefaultLogger.Sublogger(modName), defaultFailAction: defaultFailAction, diff --git a/internal/cli/ctl/moduleinit.go b/internal/cli/ctl/moduleinit.go index 5519d0e3..f280b181 100644 --- a/internal/cli/ctl/moduleinit.go +++ b/internal/cli/ctl/moduleinit.go @@ -32,7 +32,7 @@ import ( ) func closeIfNeeded(i any) { - if c, ok := i.(module.LifetimeModule); ok { + if c, ok := i.(container.LifetimeModule); ok { if err := c.Stop(); err != nil { log.DefaultLogger.Error("failed to stop module", err) } @@ -48,7 +48,7 @@ func (m *managedStorage) Close() error { if !m.started { return nil } - if lm, ok := m.ManageableStorage.(module.LifetimeModule); ok { + if lm, ok := m.ManageableStorage.(container.LifetimeModule); ok { return lm.Stop() } return nil @@ -63,7 +63,7 @@ func (m *managedUserDB) Close() error { if !m.started { return nil } - if lm, ok := m.PlainUserDB.(module.LifetimeModule); ok { + if lm, ok := m.PlainUserDB.(container.LifetimeModule); ok { return lm.Stop() } return nil @@ -88,6 +88,11 @@ func getCfgBlockModule(ctx *cli.Context) (*container.C, module.Module, error) { return nil, nil, err } + // For CLI management we force-rollback configured logger and consider only + // --log so messages relevant to command execution will go where admin would + // see them. + c.DefaultLogger.Out = log.DefaultLogger.Out + if err := maddy.InitDirs(c); err != nil { return nil, nil, err } @@ -104,7 +109,7 @@ func getCfgBlockModule(ctx *cli.Context) (*container.C, module.Module, error) { mod, err := c.Modules.Get(cfgBlock) if err != nil { - if errors.Is(err, module.ErrInstanceUnknown) { + if errors.Is(err, container.ErrInstanceUnknown) { return nil, nil, cli.Exit(fmt.Sprintf("Error: unknown configuration block: %s", cfgBlock), 2) } return nil, nil, err @@ -125,7 +130,7 @@ func openStorage(ctx *cli.Context) (module.Storage, error) { } started := false - if lt, ok := storage.(module.LifetimeModule); ok { + if lt, ok := storage.(container.LifetimeModule); ok { if err := lt.Start(); err != nil { return nil, err } @@ -160,7 +165,7 @@ func openUserDB(ctx *cli.Context) (module.PlainUserDB, error) { } started := false - if lt, ok := userDB.(module.LifetimeModule); ok { + if lt, ok := userDB.(container.LifetimeModule); ok { if err := lt.Start(); err != nil { return nil, err } diff --git a/internal/endpoint/dovecot_sasld/dovecot_sasl.go b/internal/endpoint/dovecot_sasld/dovecot_sasl.go index 4bb7a942..215b8508 100644 --- a/internal/endpoint/dovecot_sasld/dovecot_sasl.go +++ b/internal/endpoint/dovecot_sasld/dovecot_sasl.go @@ -29,8 +29,9 @@ import ( dovecotsasl "github.com/foxcpp/go-dovecot-sasl" "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" - "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/framework/resource/netresource" "github.com/foxcpp/maddy/internal/auth" "github.com/foxcpp/maddy/internal/authz" @@ -40,7 +41,7 @@ const modName = "dovecot_sasld" type Endpoint struct { addrs []string - log log.Logger + log *log.Logger saslAuth auth.SASLAuth endpoints []config.Endpoint @@ -49,13 +50,14 @@ type Endpoint struct { srv *dovecotsasl.Server } -func New(_ string, addrs []string) (module.LifetimeModule, error) { +func New(c *container.C, _ string, addrs []string) (container.LifetimeModule, error) { + logger := c.DefaultLogger.Sublogger(modName) return &Endpoint{ addrs: addrs, saslAuth: auth.SASLAuth{ - Log: log.Logger{Name: modName + "/saslauth"}, + Log: logger.Sublogger("sasl"), }, - log: log.Logger{Name: modName, Debug: log.DefaultLogger.Debug}, + log: logger, }, nil } @@ -81,7 +83,7 @@ func (endp *Endpoint) Configure(_ []string, cfg *config.Map) error { endp.srv = dovecotsasl.NewServer() endp.saslAuth.Log.Debug = endp.log.Debug - endp.srv.Log = stdlog.New(&endp.log, "", 0) + endp.srv.Log = stdlog.New(endp.log, "", 0) for _, mech := range endp.saslAuth.SASLMechanisms() { endp.srv.AddMechanism(mech, mechInfo[mech], func(req *dovecotsasl.AuthReq) sasl.Server { @@ -133,5 +135,5 @@ func (endp *Endpoint) Stop() error { } func init() { - module.RegisterEndpoint(modName, New) + modules.RegisterEndpoint(modName, New) } diff --git a/internal/endpoint/imap/imap.go b/internal/endpoint/imap/imap.go index 1a64ca3d..afc68a79 100644 --- a/internal/endpoint/imap/imap.go +++ b/internal/endpoint/imap/imap.go @@ -40,8 +40,10 @@ import ( "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" tls2 "github.com/foxcpp/maddy/framework/config/tls" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/framework/resource/netresource" "github.com/foxcpp/maddy/internal/auth" "github.com/foxcpp/maddy/internal/authz" @@ -65,15 +67,16 @@ type Endpoint struct { storageNormalize authz.NormalizeFunc storageMap module.Table - Log log.Logger + log *log.Logger } -func New(modName string, addrs []string) (module.LifetimeModule, error) { +func New(c *container.C, modName string, addrs []string) (container.LifetimeModule, error) { + logger := c.DefaultLogger.Sublogger(modName) endp := &Endpoint{ addrs: addrs, - Log: log.Logger{Name: modName}, + log: logger, saslAuth: auth.SASLAuth{ - Log: log.Logger{Name: modName + "/sasl"}, + Log: logger.Sublogger("sasl"), }, } @@ -97,7 +100,7 @@ func (endp *Endpoint) Configure(_ []string, cfg *config.Map) error { cfg.Bool("insecure_auth", false, false, &insecureAuth) cfg.Bool("io_debug", false, false, &ioDebug) cfg.Bool("io_errors", false, false, &ioErrors) - cfg.Bool("debug", true, false, &endp.Log.Debug) + cfg.Bool("debug", true, false, &endp.log.Debug) config.EnumMapped(cfg, "storage_map_normalize", false, false, authz.NormalizeFuncs, authz.NormalizeAuto, &endp.storageNormalize) modconfig.Table(cfg, "storage_map", false, false, nil, &endp.storageMap) @@ -108,7 +111,7 @@ func (endp *Endpoint) Configure(_ []string, cfg *config.Map) error { return err } - endp.saslAuth.Log.Debug = endp.Log.Debug + endp.saslAuth.Log.Debug = endp.log.Debug addresses := make([]config.Endpoint, 0, len(endp.addrs)) for _, addr := range endp.addrs { @@ -127,13 +130,13 @@ func (endp *Endpoint) Configure(_ []string, cfg *config.Map) error { endp.serv.AllowInsecureAuth = insecureAuth endp.serv.TLSConfig = endp.tlsConfig if ioErrors { - endp.serv.ErrorLog = &endp.Log + endp.serv.ErrorLog = endp.log } else { - endp.serv.ErrorLog = &log.Logger{Out: log.NopOutput{}} + endp.serv.ErrorLog = &log.NopLogger } if ioDebug { - endp.serv.Debug = endp.Log.DebugWriter() - endp.Log.Println("I/O debugging is on! It may leak passwords in logs, be careful!") + endp.serv.Debug = endp.log.DebugWriter() + endp.log.Println("I/O debugging is on! It may leak passwords in logs, be careful!") } if err := endp.enableExtensions(); err != nil { @@ -149,10 +152,10 @@ func (endp *Endpoint) Configure(_ []string, cfg *config.Map) error { } if endp.serv.AllowInsecureAuth { - endp.Log.Println("authentication over unencrypted connections is allowed, this is insecure configuration and should be used only for testing!") + endp.log.Println("authentication over unencrypted connections is allowed, this is insecure configuration and should be used only for testing!") } if endp.serv.TLSConfig == nil { - endp.Log.Println("TLS is disabled, this is insecure configuration and should be used only for testing!") + endp.log.Println("TLS is disabled, this is insecure configuration and should be used only for testing!") endp.serv.AllowInsecureAuth = true } @@ -162,13 +165,13 @@ func (endp *Endpoint) Configure(_ []string, cfg *config.Map) error { func (endp *Endpoint) Start() error { if updBe, ok := endp.Store.(updatepipe.Backend); ok { if err := updBe.EnableUpdatePipe(updatepipe.ModeReplicate); err != nil { - endp.Log.Error("failed to initialize updates pipe", err) + endp.log.Error("failed to initialize updates pipe", err) } } if err := endp.setupListeners(endp.endpoints); err != nil { if err := endp.Stop(); err != nil { - endp.Log.Error("failed to stop after setupListeners error", err) + endp.log.Error("failed to stop after setupListeners error", err) } return err } @@ -183,7 +186,7 @@ func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { if err != nil { return fmt.Errorf("imap: %v", err) } - endp.Log.Printf("listening on %v", addr) + endp.log.Printf("listening on %v", addr) if addr.IsTLS() { if endp.tlsConfig == nil { @@ -193,7 +196,7 @@ func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { } if endp.proxyProtocol != nil { - l = proxy_protocol.NewListener(l, endp.proxyProtocol, endp.Log) + l = proxy_protocol.NewListener(l, endp.proxyProtocol, endp.log) } endp.listeners = append(endp.listeners, l) @@ -201,7 +204,7 @@ func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { go func() { defer endp.listenersWg.Done() if err := endp.serv.Serve(l); err != nil && !strings.HasSuffix(err.Error(), "use of closed network connection") { - endp.Log.Printf("imap: failed to serve %s: %s", addr, err) + endp.log.Printf("imap: failed to serve %s: %s", addr, err) } }() } @@ -220,7 +223,7 @@ func (endp *Endpoint) InstanceName() string { func (endp *Endpoint) Stop() error { for _, l := range endp.listeners { if err := l.Close(); err != nil { - endp.Log.Error("failed to close listener", err) + endp.log.Error("failed to close listener", err) } } if err := endp.serv.Close(); err != nil { @@ -249,7 +252,7 @@ func (endp *Endpoint) usernameForStorage(ctx context.Context, saslUsername strin } if saslUsername != mapped { - endp.Log.DebugMsg("using mapped username for storage", "username", saslUsername, "mapped_username", mapped) + endp.log.DebugMsg("using mapped username for storage", "username", saslUsername, "mapped_username", mapped) } return mapped, nil @@ -261,7 +264,7 @@ func (endp *Endpoint) openAccount(c imapserver.Conn, identity string) error { if errors.Is(err, imapbackend.ErrInvalidCredentials) { return err } - endp.Log.Error("failed to determine storage account name", err, "username", username) + endp.log.Error("failed to determine storage account name", err, "username", username) return fmt.Errorf("internal server error") } @@ -279,7 +282,7 @@ func (endp *Endpoint) Login(connInfo *imap.ConnInfo, username, password string) // saslAuth handles AuthMap calling. err := endp.saslAuth.AuthPlain(username, password) if err != nil { - endp.Log.Error("authentication failed", err, "username", username, "src_ip", connInfo.RemoteAddr) + endp.log.Error("authentication failed", err, "username", username, "src_ip", connInfo.RemoteAddr) return nil, imapbackend.ErrInvalidCredentials } @@ -288,7 +291,7 @@ func (endp *Endpoint) Login(connInfo *imap.ConnInfo, username, password string) if errors.Is(err, imapbackend.ErrInvalidCredentials) { return nil, err } - endp.Log.Error("authentication failed due to an internal error", err, "username", username, "src_ip", connInfo.RemoteAddr) + endp.log.Error("authentication failed due to an internal error", err, "username", username, "src_ip", connInfo.RemoteAddr) return nil, fmt.Errorf("internal server error") } @@ -333,7 +336,7 @@ func (endp *Endpoint) SupportedThreadAlgorithms() []sortthread.ThreadAlgorithm { } func init() { - module.RegisterEndpoint("imap", New) + modules.RegisterEndpoint("imap", New) imap.CharsetReader = message.CharsetReader } diff --git a/internal/endpoint/openmetrics/om.go b/internal/endpoint/openmetrics/om.go index 4c917835..7251b85f 100644 --- a/internal/endpoint/openmetrics/om.go +++ b/internal/endpoint/openmetrics/om.go @@ -25,8 +25,9 @@ import ( "sync" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" - "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/framework/resource/netresource" "github.com/prometheus/client_golang/prometheus/promhttp" ) @@ -36,17 +37,17 @@ const modName = "openmetrics" type Endpoint struct { addrs []string endpoints []config.Endpoint - logger log.Logger + logger *log.Logger listenersWg sync.WaitGroup serv http.Server mux *http.ServeMux } -func New(_ string, args []string) (module.LifetimeModule, error) { +func New(c *container.C, _ string, args []string) (container.LifetimeModule, error) { return &Endpoint{ addrs: args, - logger: log.Logger{Name: modName, Debug: log.DefaultLogger.Debug}, + logger: c.DefaultLogger.Sublogger(modName), }, nil } @@ -114,5 +115,5 @@ func (e *Endpoint) Stop() error { } func init() { - module.RegisterEndpoint(modName, New) + modules.RegisterEndpoint(modName, New) } diff --git a/internal/endpoint/smtp/session.go b/internal/endpoint/smtp/session.go index 01e36e73..c6adfa0c 100644 --- a/internal/endpoint/smtp/session.go +++ b/internal/endpoint/smtp/session.go @@ -95,7 +95,7 @@ type Session struct { delivery module.Delivery deliveryErr error - log log.Logger + log *log.Logger } func (s *Session) AuthMechanisms() []string { @@ -117,7 +117,7 @@ func (s *Session) Reset() { if s.delivery != nil { s.abort(s.msgCtx) } - s.endp.Log.DebugMsg("reset") + s.endp.log.DebugMsg("reset") } func (s *Session) releaseLimits() { @@ -139,7 +139,7 @@ func (s *Session) releaseLimits() { func (s *Session) abort(ctx context.Context) { if err := s.delivery.Abort(ctx); err != nil { - s.endp.Log.Error("delivery abort failed", err) + s.endp.log.Error("delivery abort failed", err) } s.log.Msg("aborted", "msg_id", s.msgMeta.ID) abortedSMTPTransactions.WithLabelValues(s.endp.name).Inc() @@ -167,7 +167,7 @@ func (s *Session) AuthPlain(username, password string) error { // saslAuth will handle AuthMap and AuthNormalize. err := s.endp.saslAuth.AuthPlain(username, password) if err != nil { - s.endp.Log.Error("authentication failed", err, "username", username, "src_ip", s.connState.RemoteAddr) + s.endp.log.Error("authentication failed", err, "username", username, "src_ip", s.connState.RemoteAddr) failedLogins.WithLabelValues(s.endp.name).Inc() @@ -371,7 +371,7 @@ func (s *Session) Rcpt(to string, opts *smtp.RcptOptions) error { } return s.endp.wrapErr(s.msgMeta.ID, !s.opts.UTF8, "RCPT", err) } - s.endp.Log.Msg("RCPT ok", "rcpt", to, "msg_id", s.msgMeta.ID) + s.endp.log.Msg("RCPT ok", "rcpt", to, "msg_id", s.msgMeta.ID) return nil } @@ -610,7 +610,7 @@ func (endp *Endpoint) wrapErr(msgId string, mangleUTF8 bool, command string, err } if smtpErr, ok := err.(*smtp.SMTPError); ok { - endp.Log.Printf("plain SMTP error returned, this is deprecated") + endp.log.Printf("plain SMTP error returned, this is deprecated") res.Code = smtpErr.Code res.EnhancedCode = smtpErr.EnhancedCode res.Message = smtpErr.Message diff --git a/internal/endpoint/smtp/smtp.go b/internal/endpoint/smtp/smtp.go index cabde482..eb334556 100644 --- a/internal/endpoint/smtp/smtp.go +++ b/internal/endpoint/smtp/smtp.go @@ -37,11 +37,13 @@ import ( "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" tls2 "github.com/foxcpp/maddy/framework/config/tls" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/dns" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/future" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/framework/resource/netresource" "github.com/foxcpp/maddy/internal/auth" "github.com/foxcpp/maddy/internal/authz" @@ -78,7 +80,7 @@ type Endpoint struct { listenersWg sync.WaitGroup - Log log.Logger + log *log.Logger } func (endp *Endpoint) Name() string { @@ -89,7 +91,8 @@ func (endp *Endpoint) InstanceName() string { return endp.name } -func New(modName string, addrs []string) (module.LifetimeModule, error) { +func New(c *container.C, modName string, addrs []string) (container.LifetimeModule, error) { + logger := c.DefaultLogger.Sublogger(modName) endp := &Endpoint{ name: modName, addrs: addrs, @@ -97,9 +100,9 @@ func New(modName string, addrs []string) (module.LifetimeModule, error) { lmtp: modName == "lmtp", resolver: dns.DefaultResolver(), buffer: buffer.BufferInMemory, - Log: log.Logger{Name: modName}, + log: logger, saslAuth: auth.SASLAuth{ - Log: log.Logger{Name: modName + "/sasl"}, + Log: logger.Sublogger("sasl"), }, } return endp, nil @@ -107,7 +110,7 @@ func New(modName string, addrs []string) (module.LifetimeModule, error) { func (endp *Endpoint) Configure(_ []string, cfg *config.Map) error { endp.serv = smtp.NewServer(endp) - endp.serv.ErrorLog = &endp.Log + endp.serv.ErrorLog = endp.log endp.serv.LMTP = endp.lmtp endp.serv.EnableSMTPUTF8 = true endp.serv.EnableREQUIRETLS = true @@ -134,11 +137,11 @@ func (endp *Endpoint) Configure(_ []string, cfg *config.Map) error { } if endp.serv.AllowInsecureAuth && !allLocal { - endp.Log.Println("authentication over unencrypted connections is allowed, this is insecure configuration and should be used only for testing!") + endp.log.Println("authentication over unencrypted connections is allowed, this is insecure configuration and should be used only for testing!") } if endp.serv.TLSConfig == nil { if !allLocal { - endp.Log.Println("TLS is disabled, this is insecure configuration and should be used only for testing!") + endp.log.Println("TLS is disabled, this is insecure configuration and should be used only for testing!") } endp.serv.AllowInsecureAuth = true @@ -267,7 +270,7 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { cfg.Bool("insecure_auth", endp.name == "lmtp", false, &endp.serv.AllowInsecureAuth) cfg.Int("smtp_max_line_length", false, false, 4000, &endp.serv.MaxLineLength) cfg.Bool("io_debug", false, false, &ioDebug) - cfg.Bool("debug", true, false, &endp.Log.Debug) + cfg.Bool("debug", true, false, &endp.log.Debug) cfg.Bool("defer_sender_reject", false, true, &endp.deferServerReject) cfg.Int("max_logged_rcpt_errors", false, false, 5, &endp.maxLoggedRcptErrors) cfg.Custom("limits", false, false, func() (interface{}, error) { @@ -285,7 +288,7 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { return err } - endp.saslAuth.Log.Debug = endp.Log.Debug + endp.saslAuth.Log.Debug = endp.log.Debug endp.saslAuth.ErrorMap = endp.authErrorMap // INTERNATIONALIZATION: See RFC 6531 Section 3.3. @@ -300,7 +303,7 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { } endp.pipeline.Hostname = endp.serv.Domain endp.pipeline.Resolver = endp.resolver - endp.pipeline.Log = log.Logger{Name: "smtp/pipeline", Debug: endp.Log.Debug} + endp.pipeline.Log = endp.log.Sublogger("pipeline") endp.pipeline.FirstPipeline = true if endp.submission { @@ -311,8 +314,8 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { } if ioDebug { - endp.serv.Debug = endp.Log.DebugWriter() - endp.Log.Println("I/O debugging is on! It may leak passwords in logs, be careful!") + endp.serv.Debug = endp.log.DebugWriter() + endp.log.Println("I/O debugging is on! It may leak passwords in logs, be careful!") } return nil @@ -321,7 +324,7 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { func (endp *Endpoint) Start() error { if err := endp.setupListeners(endp.endpoints); err != nil { if err := endp.Stop(); err != nil { - endp.Log.Error("failed to Stop after setupListeners fail", err) + endp.log.Error("failed to Stop after setupListeners fail", err) } return err } @@ -352,7 +355,7 @@ func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { if err != nil { return fmt.Errorf("%s: %w", endp.name, err) } - endp.Log.Printf("listening on %v", addr) + endp.log.Printf("listening on %v", addr) if addr.IsTLS() { if endp.serv.TLSConfig == nil { @@ -362,7 +365,7 @@ func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { } if endp.proxyProtocol != nil { - l = proxy_protocol.NewListener(l, endp.proxyProtocol, endp.Log) + l = proxy_protocol.NewListener(l, endp.proxyProtocol, endp.log.Sublogger("proxy")) } endp.listeners = append(endp.listeners, l) @@ -370,7 +373,7 @@ func (endp *Endpoint) setupListeners(addresses []config.Endpoint) error { endp.listenersWg.Add(1) go func() { if err := endp.serv.Serve(l); err != nil { - endp.Log.Printf("failed to serve %s: %s", addr, err) + endp.log.Printf("failed to serve %s: %s", addr, err) } endp.listenersWg.Done() }() @@ -385,7 +388,7 @@ func (endp *Endpoint) NewSession(conn *smtp.Conn) (smtp.Session, error) { // Executed before authentication and session initialization. if err := endp.pipeline.RunEarlyChecks(context.TODO(), &sess.connState); err != nil { if err := sess.Logout(); err != nil { - endp.Log.Error("early checks logout failed", err) + endp.log.Error("early checks logout failed", err) } return nil, endp.wrapErr("", true, "EHLO", err) } @@ -398,7 +401,7 @@ func (endp *Endpoint) NewSession(conn *smtp.Conn) (smtp.Session, error) { func (endp *Endpoint) newSession(conn *smtp.Conn) *Session { s := &Session{ endp: endp, - log: endp.Log, + log: endp.log, sessionCtx: context.Background(), } @@ -456,7 +459,7 @@ func (endp *Endpoint) Stop() error { } func init() { - module.RegisterEndpoint("smtp", New) - module.RegisterEndpoint("submission", New) - module.RegisterEndpoint("lmtp", New) + modules.RegisterEndpoint("smtp", New) + modules.RegisterEndpoint("submission", New) + modules.RegisterEndpoint("lmtp", New) } diff --git a/internal/endpoint/smtp/smtp_test.go b/internal/endpoint/smtp/smtp_test.go index ee87efee..485c7e9e 100644 --- a/internal/endpoint/smtp/smtp_test.go +++ b/internal/endpoint/smtp/smtp_test.go @@ -32,8 +32,10 @@ import ( "github.com/emersion/go-smtp" "github.com/foxcpp/go-mockdns" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/auth" "github.com/foxcpp/maddy/internal/msgpipeline" "github.com/foxcpp/maddy/internal/testutils" @@ -51,7 +53,7 @@ const testMsg = "From: \r\n" + func testEndpoint(t *testing.T, modName string, authMod module.PlainAuth, tgt module.DeliveryTarget, checks []module.Check, cfg []config.Node) *Endpoint { t.Helper() - mod, err := New(modName, []string{"tcp://127.0.0.1:" + testPort}) + mod, err := New(container.New(), modName, []string{"tcp://127.0.0.1:" + testPort}) if err != nil { t.Fatal(err) } @@ -67,7 +69,7 @@ func testEndpoint(t *testing.T, modName string, authMod module.PlainAuth, tgt mo }, }, } - endp.Log = testutils.Logger(t, "smtp") + endp.log = testutils.Logger(t, "smtp") cfg = append(cfg, config.Node{ @@ -568,7 +570,7 @@ func TestSMTPDelivery_Reset(t *testing.T) { func TestSMTPDelivery_SubmissionAuthRequire(t *testing.T) { tgt := testutils.Target{} - endp := testEndpoint(t, "submission", &module.Dummy{}, &tgt, nil, nil) + endp := testEndpoint(t, "submission", &modules.Dummy{}, &tgt, nil, nil) defer func() { assert.NoError(t, endp.Stop()) }() @@ -588,7 +590,7 @@ func TestSMTPDelivery_SubmissionAuthRequire(t *testing.T) { func TestSMTPDelivery_SubmissionAuthOK(t *testing.T) { tgt := testutils.Target{} - endp := testEndpoint(t, "submission", &module.Dummy{}, &tgt, nil, nil) + endp := testEndpoint(t, "submission", &modules.Dummy{}, &tgt, nil, nil) defer func() { assert.NoError(t, endp.Stop()) }() diff --git a/internal/endpoint/smtp/submission_test.go b/internal/endpoint/smtp/submission_test.go index 91f030cf..911d4328 100644 --- a/internal/endpoint/smtp/submission_test.go +++ b/internal/endpoint/smtp/submission_test.go @@ -26,6 +26,7 @@ import ( "github.com/emersion/go-message/textproto" "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/stretchr/testify/assert" ) @@ -50,7 +51,7 @@ func TestSubmissionPrepare(t *testing.T) { } } - endp := testEndpoint(t, "submission", &module.Dummy{}, &module.Dummy{}, nil, nil) + endp := testEndpoint(t, "submission", &modules.Dummy{}, &modules.Dummy{}, nil, nil) defer func() { // Synchronize the endpoint initialization. // Otherwise Close will race with Serve called by setupListeners. diff --git a/internal/imap_filter/command/command.go b/internal/imap_filter/command/command.go index 32f05c03..9961c4ea 100644 --- a/internal/imap_filter/command/command.go +++ b/internal/imap_filter/command/command.go @@ -32,8 +32,10 @@ import ( "github.com/emersion/go-message/textproto" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) const modName = "imap.filter.command" @@ -42,7 +44,7 @@ var placeholderRe = regexp.MustCompile(`{[a-zA-Z0-9_]+?}`) type Check struct { instName string - log log.Logger + log *log.Logger cmd string cmdArgs []string @@ -61,13 +63,13 @@ func (c *Check) IMAPFilter(accountName string, rcptTo string, msgMeta *module.Ms return c.run(cmd, args, io.MultiReader(bytes.NewReader(buf.Bytes()), bR)) } -func New(_, instName string) (module.Module, error) { - c := &Check{ +func New(c *container.C, _, instName string) (module.Module, error) { + chk := &Check{ instName: instName, - log: log.Logger{Name: modName, Debug: log.DefaultLogger.Debug}, + log: c.DefaultLogger.Sublogger(modName), } - return c, nil + return chk, nil } func (c *Check) Name() string { @@ -203,5 +205,5 @@ func (c *Check) run(cmdName string, args []string, stdin io.Reader) (string, []s } func init() { - module.Register(modName, New) + modules.Register(modName, New) } diff --git a/internal/imap_filter/group.go b/internal/imap_filter/group.go index f86ea866..fd818f86 100644 --- a/internal/imap_filter/group.go +++ b/internal/imap_filter/group.go @@ -23,8 +23,10 @@ import ( "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) // Group wraps multiple modifiers and runs them serially. @@ -34,13 +36,13 @@ import ( type Group struct { instName string Filters []module.IMAPFilter - log log.Logger + log *log.Logger } -func NewGroup(_, instName string) (module.Module, error) { +func NewGroup(c *container.C, modName, instName string) (module.Module, error) { return &Group{ instName: instName, - log: log.Logger{Name: "imap_filters", Debug: log.DefaultLogger.Debug}, + log: c.DefaultLogger.Sublogger(modName), }, nil } @@ -88,5 +90,5 @@ func (g *Group) InstanceName() string { } func init() { - module.Register("imap_filters", NewGroup) + modules.Register("imap_filters", NewGroup) } diff --git a/internal/libdns/acmedns.go b/internal/libdns/acmedns.go index d088811d..ec829f88 100644 --- a/internal/libdns/acmedns.go +++ b/internal/libdns/acmedns.go @@ -5,12 +5,14 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/libdns/acmedns" ) func init() { - module.Register("libdns.acmedns", func(modName, instName string) (module.Module, error) { + modules.Register("libdns.acmedns", func(c *container.C, modName, instName string) (module.Module, error) { p := acmedns.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/alidns.go b/internal/libdns/alidns.go index 9e32399c..27c89074 100644 --- a/internal/libdns/alidns.go +++ b/internal/libdns/alidns.go @@ -5,12 +5,14 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/libdns/alidns" ) func init() { - module.Register("libdns.alidns", func(modName, instName string) (module.Module, error) { + modules.Register("libdns.alidns", func(c *container.C, modName, instName string) (module.Module, error) { p := alidns.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/cloudflare.go b/internal/libdns/cloudflare.go index b69ec75d..cc8cc7db 100644 --- a/internal/libdns/cloudflare.go +++ b/internal/libdns/cloudflare.go @@ -5,12 +5,14 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/libdns/cloudflare" ) func init() { - module.Register("libdns.cloudflare", func(modName, instName string) (module.Module, error) { + modules.Register("libdns.cloudflare", func(c *container.C, modName, instName string) (module.Module, error) { p := cloudflare.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/digitalocean.go b/internal/libdns/digitalocean.go index 20369c9a..96cdc728 100644 --- a/internal/libdns/digitalocean.go +++ b/internal/libdns/digitalocean.go @@ -5,12 +5,14 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/libdns/digitalocean" ) func init() { - module.Register("libdns.digitalocean", func(modName, instName string) (module.Module, error) { + modules.Register("libdns.digitalocean", func(c *container.C, modName, instName string) (module.Module, error) { p := digitalocean.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/gandi.go b/internal/libdns/gandi.go index 59a493d5..828a48cd 100644 --- a/internal/libdns/gandi.go +++ b/internal/libdns/gandi.go @@ -7,13 +7,15 @@ import ( "fmt" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/libdns/gandi" ) func init() { - module.Register("libdns.gandi", func(modName, instName string) (module.Module, error) { + modules.Register("libdns.gandi", func(c *container.C, modName, instName string) (module.Module, error) { p := gandi.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/gcore.go b/internal/libdns/gcore.go index 69d0da5e..98f71e7e 100644 --- a/internal/libdns/gcore.go +++ b/internal/libdns/gcore.go @@ -6,12 +6,14 @@ import ( "fmt" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/libdns/gcore" ) func init() { - module.Register("libdns.gcore", func(modName, instName string) (module.Module, error) { + modules.Register("libdns.gcore", func(c *container.C, modName, instName string) (module.Module, error) { p := gcore.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/googleclouddns.go b/internal/libdns/googleclouddns.go index a9bc493e..dc027ddc 100644 --- a/internal/libdns/googleclouddns.go +++ b/internal/libdns/googleclouddns.go @@ -5,12 +5,14 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/libdns/googleclouddns" ) func init() { - module.Register("libdns.googleclouddns", func(modName, instName string) (module.Module, error) { + modules.Register("libdns.googleclouddns", func(c *container.C, modName, instName string) (module.Module, error) { p := googleclouddns.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/hetzner.go b/internal/libdns/hetzner.go index d9a39def..cd5c3458 100644 --- a/internal/libdns/hetzner.go +++ b/internal/libdns/hetzner.go @@ -5,13 +5,15 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/libdns/hetzner" ) func init() { - module.Register("libdns.hetzner", func(modName, instName string) (module.Module, error) { + modules.Register("libdns.hetzner", func(c *container.C, modName, instName string) (module.Module, error) { p := hetzner.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/leaseweb.go b/internal/libdns/leaseweb.go index 013e8b6e..a76487a6 100644 --- a/internal/libdns/leaseweb.go +++ b/internal/libdns/leaseweb.go @@ -5,13 +5,15 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/libdns/leaseweb" ) func init() { - module.Register("libdns.leaseweb", func(modName, instName string) (module.Module, error) { + modules.Register("libdns.leaseweb", func(c *container.C, modName, instName string) (module.Module, error) { p := leaseweb.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/metaname.go b/internal/libdns/metaname.go index 2180509b..0ffc6177 100644 --- a/internal/libdns/metaname.go +++ b/internal/libdns/metaname.go @@ -5,12 +5,14 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/libdns/metaname" ) func init() { - module.Register("libdns.metaname", func(modName, instName string) (module.Module, error) { + modules.Register("libdns.metaname", func(c *container.C, modName, instName string) (module.Module, error) { p := metaname.Provider{ Endpoint: "https://metaname.net/api/1.1", } diff --git a/internal/libdns/namecheap.go b/internal/libdns/namecheap.go index cb38461e..a8538c31 100644 --- a/internal/libdns/namecheap.go +++ b/internal/libdns/namecheap.go @@ -5,12 +5,14 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/libdns/namecheap" ) func init() { - module.Register("libdns.namecheap", func(modName, instName string) (module.Module, error) { + modules.Register("libdns.namecheap", func(c *container.C, modName, instName string) (module.Module, error) { p := namecheap.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/namedotcom.go b/internal/libdns/namedotcom.go index 0b4ab608..57c31481 100644 --- a/internal/libdns/namedotcom.go +++ b/internal/libdns/namedotcom.go @@ -5,13 +5,15 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/libdns/namedotcom" ) func init() { - module.Register("libdns.namedotcom", func(modName, instName string) (module.Module, error) { + modules.Register("libdns.namedotcom", func(c *container.C, modName, instName string) (module.Module, error) { p := namedotcom.Provider{ Server: "https://api.name.com", } diff --git a/internal/libdns/rfc2136.go b/internal/libdns/rfc2136.go index 90eca724..686bb324 100644 --- a/internal/libdns/rfc2136.go +++ b/internal/libdns/rfc2136.go @@ -5,12 +5,14 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/libdns/rfc2136" ) func init() { - module.Register("libdns.rfc2136", func(modName, instName string) (module.Module, error) { + modules.Register("libdns.rfc2136", func(c *container.C, modName, instName string) (module.Module, error) { p := rfc2136.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/route53.go b/internal/libdns/route53.go index fbb5edf7..be0a1dab 100644 --- a/internal/libdns/route53.go +++ b/internal/libdns/route53.go @@ -5,12 +5,14 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/libdns/route53" ) func init() { - module.Register("libdns.route53", func(modName, instName string) (module.Module, error) { + modules.Register("libdns.route53", func(c *container.C, modName, instName string) (module.Module, error) { p := route53.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/libdns/vultr.go b/internal/libdns/vultr.go index 35ba38c2..097bc442 100644 --- a/internal/libdns/vultr.go +++ b/internal/libdns/vultr.go @@ -5,13 +5,15 @@ package libdns import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/libdns/vultr" ) func init() { - module.Register("libdns.vultr", func(modName, instName string) (module.Module, error) { + modules.Register("libdns.vultr", func(c *container.C, modName, instName string) (module.Module, error) { p := vultr.Provider{} return &ProviderModule{ RecordDeleter: &p, diff --git a/internal/limits/limits.go b/internal/limits/limits.go index 34161f7a..eba335d4 100644 --- a/internal/limits/limits.go +++ b/internal/limits/limits.go @@ -33,7 +33,9 @@ import ( "time" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/limits/limiters" ) @@ -46,7 +48,7 @@ type Group struct { dest *limiters.BucketSet // BucketSet of MultiLimit } -func New(_, instName string) (module.Module, error) { +func New(c *container.C, _, instName string) (module.Module, error) { return &Group{ instName: instName, }, nil @@ -230,5 +232,5 @@ func (g *Group) InstanceName() string { } func init() { - module.Register("limits", New) + modules.Register("limits", New) } diff --git a/internal/modify/dkim/dkim.go b/internal/modify/dkim/dkim.go index b7864cb2..bf413121 100644 --- a/internal/modify/dkim/dkim.go +++ b/internal/modify/dkim/dkim.go @@ -34,10 +34,12 @@ import ( "github.com/foxcpp/maddy/framework/address" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/dns" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/target" "golang.org/x/net/idna" ) @@ -108,14 +110,14 @@ type Modifier struct { multipleFromOk bool signSubdomains bool - log log.Logger + log *log.Logger } -func New(_, instName string) (module.Module, error) { +func New(c *container.C, modName, instName string) (module.Module, error) { m := &Modifier{ instName: instName, signers: map[string]crypto.Signer{}, - log: log.Logger{Name: "modify.dkim"}, + log: c.DefaultLogger.Sublogger(modName), } return m, nil @@ -254,7 +256,7 @@ type state struct { m *Modifier meta *module.MsgMetadata from string - log log.Logger + log *log.Logger } func (m *Modifier) ModStateForMsg(ctx context.Context, msgMeta *module.MsgMetadata) (module.ModifierState, error) { @@ -370,5 +372,5 @@ func (s *state) Close() error { } func init() { - module.Register("modify.dkim", New) + modules.Register("modify.dkim", New) } diff --git a/internal/modify/dkim/dkim_test.go b/internal/modify/dkim/dkim_test.go index 172f0f34..7e189149 100644 --- a/internal/modify/dkim/dkim_test.go +++ b/internal/modify/dkim/dkim_test.go @@ -32,12 +32,13 @@ import ( "github.com/foxcpp/go-mockdns" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/internal/testutils" ) func newTestModifier(t *testing.T, dir, keyAlgo string, domains []string) *Modifier { - mod, err := New("", "test") + mod, err := New(container.New(), "", "test") if err != nil { t.Fatal(err) } diff --git a/internal/modify/group.go b/internal/modify/group.go index 26e80408..116a32cf 100644 --- a/internal/modify/group.go +++ b/internal/modify/group.go @@ -25,8 +25,10 @@ import ( "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) type ( @@ -135,7 +137,7 @@ func (gs groupState) Close() error { } func init() { - module.Register("modifiers", func(_, instName string) (module.Module, error) { + modules.Register("modifiers", func(c *container.C, _, instName string) (module.Module, error) { return &Group{ instName: instName, }, nil diff --git a/internal/modify/replace_addr.go b/internal/modify/replace_addr.go index f305ac95..7b3e9842 100644 --- a/internal/modify/replace_addr.go +++ b/internal/modify/replace_addr.go @@ -28,7 +28,9 @@ import ( "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) // replaceAddr is a simple module that replaces matching sender (or recipient) address @@ -45,7 +47,7 @@ type replaceAddr struct { table module.MultiTable } -func NewReplaceAddr(modName, instName string) (module.Module, error) { +func NewReplaceAddr(c *container.C, modName, instName string) (module.Module, error) { r := replaceAddr{ modName: modName, instName: instName, @@ -149,6 +151,6 @@ func (r *replaceAddr) rewrite(ctx context.Context, val string) ([]string, error) } func init() { - module.Register("modify.replace_sender", NewReplaceAddr) - module.Register("modify.replace_rcpt", NewReplaceAddr) + modules.Register("modify.replace_sender", NewReplaceAddr) + modules.Register("modify.replace_rcpt", NewReplaceAddr) } diff --git a/internal/modify/replace_addr_test.go b/internal/modify/replace_addr_test.go index 9c3b0b75..35ebbb2e 100644 --- a/internal/modify/replace_addr_test.go +++ b/internal/modify/replace_addr_test.go @@ -24,6 +24,7 @@ import ( "testing" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/internal/testutils" ) @@ -31,7 +32,7 @@ func testReplaceAddr(t *testing.T, modName string) { test := func(addr string, expectedMulti []string, aliases map[string][]string) { t.Helper() - mod, err := NewReplaceAddr(modName, "") + mod, err := NewReplaceAddr(container.New(), modName, "") if err != nil { t.Fatal(err) } diff --git a/internal/msgpipeline/check_group.go b/internal/msgpipeline/check_group.go index 27bcdb6c..98b6f9c5 100644 --- a/internal/msgpipeline/check_group.go +++ b/internal/msgpipeline/check_group.go @@ -21,7 +21,9 @@ package msgpipeline import ( "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) // CheckGroup is a module container for a group of Check implementations. @@ -59,7 +61,7 @@ func (cg *CheckGroup) InstanceName() string { } func init() { - module.Register("checks", func(_, instName string) (module.Module, error) { + modules.Register("checks", func(_ *container.C, _, instName string) (module.Module, error) { return &CheckGroup{ instName: instName, }, nil diff --git a/internal/msgpipeline/check_runner.go b/internal/msgpipeline/check_runner.go index 7f3a2cb7..48ce0049 100644 --- a/internal/msgpipeline/check_runner.go +++ b/internal/msgpipeline/check_runner.go @@ -49,14 +49,14 @@ type checkRunner struct { didDMARCFetch bool dmarcVerify *dmarc.Verifier - log log.Logger + log *log.Logger states map[module.Check]module.CheckState mergedRes module.CheckResult } -func newCheckRunner(msgMeta *module.MsgMetadata, log log.Logger, r dns.Resolver) *checkRunner { +func newCheckRunner(msgMeta *module.MsgMetadata, log *log.Logger, r dns.Resolver) *checkRunner { return &checkRunner{ msgMeta: msgMeta, checkedRcptsPerCheck: map[module.CheckState]map[string]struct{}{}, diff --git a/internal/msgpipeline/module.go b/internal/msgpipeline/module.go index 29cd4a8f..f7c80792 100644 --- a/internal/msgpipeline/module.go +++ b/internal/msgpipeline/module.go @@ -20,19 +20,21 @@ package msgpipeline import ( "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) type Module struct { instName string - log log.Logger + log *log.Logger *MsgPipeline } -func NewModule(modName, instName string) (module.Module, error) { +func NewModule(c *container.C, modName, instName string) (module.Module, error) { return &Module{ - log: log.Logger{Name: "msgpipeline"}, + log: c.DefaultLogger.Sublogger(modName), instName: instName, }, nil } @@ -66,5 +68,5 @@ func (m *Module) InstanceName() string { } func init() { - module.Register("msgpipeline", NewModule) + modules.Register("msgpipeline", NewModule) } diff --git a/internal/msgpipeline/msgpipeline.go b/internal/msgpipeline/msgpipeline.go index e15c49d5..8fc9c54d 100644 --- a/internal/msgpipeline/msgpipeline.go +++ b/internal/msgpipeline/msgpipeline.go @@ -61,7 +61,7 @@ type MsgPipeline struct { // exactly in this place. FirstPipeline bool - Log log.Logger + Log *log.Logger } type rcptIn struct { @@ -269,7 +269,7 @@ type msgpipelineDelivery struct { sourceModifiersState module.ModifierState rcptModifiersState map[*rcptBlock]module.ModifierState - log log.Logger + log *log.Logger sourceAddr string sourceBlock sourceBlock diff --git a/internal/proxy_protocol/proxy_protocol.go b/internal/proxy_protocol/proxy_protocol.go index e48c2fa3..24fe20c0 100644 --- a/internal/proxy_protocol/proxy_protocol.go +++ b/internal/proxy_protocol/proxy_protocol.go @@ -50,7 +50,7 @@ func ProxyProtocolDirective(_ *config.Map, node config.Node) (interface{}, error return &p, nil } -func NewListener(inner net.Listener, p *ProxyProtocol, logger log.Logger) net.Listener { +func NewListener(inner net.Listener, p *ProxyProtocol, logger *log.Logger) net.Listener { var listener net.Listener sourceChecker := func(upstream net.Addr) (bool, error) { @@ -68,14 +68,12 @@ func NewListener(inner net.Listener, p *ProxyProtocol, logger log.Logger) net.Li return true, nil } - logger.Printf("proxy_protocol: connection from untrusted source %s", upstream) + logger.Printf("connection from untrusted source %s", upstream) return false, nil } proxyListener := proxyprotocol.NewDefaultListener(inner). - WithLogger(proxyprotocol.LoggerFunc(func(format string, v ...interface{}) { - logger.Debugf("proxy_protocol: "+format, v...) - })). + WithLogger(proxyprotocol.LoggerFunc(logger.Debugf)). WithSourceChecker(sourceChecker) listener = &proxyListener diff --git a/internal/smtpconn/smtpconn.go b/internal/smtpconn/smtpconn.go index 25fd49a5..22398937 100644 --- a/internal/smtpconn/smtpconn.go +++ b/internal/smtpconn/smtpconn.go @@ -73,7 +73,7 @@ type C struct { TLSConfig *tls.Config // Logger to use for debug log and certain errors. - Log log.Logger + Log *log.Logger // Include the remote server address in SMTP status messages in the form // "ADDRESS said: ..." diff --git a/internal/storage/blob/fs/fs.go b/internal/storage/blob/fs/fs.go index 48e5f21a..0ef45cd0 100644 --- a/internal/storage/blob/fs/fs.go +++ b/internal/storage/blob/fs/fs.go @@ -8,7 +8,9 @@ import ( "path/filepath" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) // FSStore struct represents directory on FS used to store blobs. @@ -17,7 +19,7 @@ type FSStore struct { root string } -func New(_, instName string) (module.Module, error) { +func New(_ *container.C, _, instName string) (module.Module, error) { return &FSStore{instName: instName}, nil } @@ -92,5 +94,5 @@ func (s *FSStore) Delete(_ context.Context, keys []string) error { func init() { var _ module.BlobStore = &FSStore{} - module.Register((&FSStore{}).Name(), New) + modules.Register((&FSStore{}).Name(), New) } diff --git a/internal/storage/blob/s3/s3.go b/internal/storage/blob/s3/s3.go index b470e2d2..4b2c0d63 100644 --- a/internal/storage/blob/s3/s3.go +++ b/internal/storage/blob/s3/s3.go @@ -7,8 +7,10 @@ import ( "net/http" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" ) @@ -25,7 +27,7 @@ const ( type Store struct { instName string - log log.Logger + log *log.Logger endpoint string cl *minio.Client @@ -34,10 +36,10 @@ type Store struct { objectPrefix string } -func New(_, instName string) (module.Module, error) { +func New(c *container.C, modName, instName string) (module.Module, error) { return &Store{ instName: instName, - log: log.Logger{Name: modName}, + log: c.DefaultLogger.Sublogger(modName), }, nil } @@ -194,5 +196,5 @@ func (s *Store) Delete(ctx context.Context, keys []string) error { func init() { var _ module.BlobStore = &Store{} - module.Register(modName, New) + modules.Register(modName, New) } diff --git a/internal/storage/blob/test_blob.go b/internal/storage/blob/test_blob.go index 8f3d0bcb..f0b2e2d9 100644 --- a/internal/storage/blob/test_blob.go +++ b/internal/storage/blob/test_blob.go @@ -43,7 +43,7 @@ func TestStore(t *testing.T, newStore func() module.BlobStore, cleanStore func(m b, err := imapsql.New("sqlite3", ":memory:", imapsql2.ExtBlobStore{Base: store}, imapsql.Opts{ PRNG: prng, - Log: &l, + Log: l, }, ) if err != nil { diff --git a/internal/storage/imapsql/delivery.go b/internal/storage/imapsql/delivery.go index 55dc36b3..a20c6c3b 100644 --- a/internal/storage/imapsql/delivery.go +++ b/internal/storage/imapsql/delivery.go @@ -108,7 +108,7 @@ func (d *delivery) Body(ctx context.Context, header textproto.Header, body buffe for rcpt, rcptData := range d.addedRcpts { folder, flags, err := d.store.filters.IMAPFilter(rcpt, rcptData.rcptTo, d.msgMeta, header, body) if err != nil { - d.store.Log.Error("IMAPFilter failed", err, "rcpt", rcpt) + d.store.log.Error("IMAPFilter failed", err, "rcpt", rcpt) continue } d.d.UserMailbox(rcpt, folder, flags) diff --git a/internal/storage/imapsql/imapsql.go b/internal/storage/imapsql/imapsql.go index 2a335b6a..fe9103de 100644 --- a/internal/storage/imapsql/imapsql.go +++ b/internal/storage/imapsql/imapsql.go @@ -44,9 +44,11 @@ import ( imapsql "github.com/foxcpp/go-imap-sql" "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/dns" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/authz" sqliteprovider "github.com/foxcpp/maddy/internal/sqlite" "github.com/foxcpp/maddy/internal/updatepipe" @@ -56,10 +58,12 @@ import ( _ "github.com/lib/pq" ) +const modName = "storage.imapsql" + type Storage struct { Back *imapsql.Backend instName string - Log log.Logger + log *log.Logger junkMbox string @@ -83,17 +87,17 @@ type Storage struct { } func (store *Storage) Name() string { - return "imapsql" + return modName } func (store *Storage) InstanceName() string { return store.instName } -func New(_, instName string) (module.Module, error) { +func New(c *container.C, modName, instName string) (module.Module, error) { store := &Storage{ instName: instName, - Log: log.Logger{Name: "imapsql"}, + log: c.DefaultLogger.Sublogger(modName), resolver: dns.DefaultResolver(), } return store, nil @@ -124,7 +128,7 @@ func (store *Storage) Configure(inlineArgs []string, cfg *config.Map) error { cfg.String("driver", false, false, store.driver, &driver) cfg.StringList("dsn", false, false, store.dsn, &dsn) cfg.Callback("fsstore", func(m *config.Map, node config.Node) error { - store.Log.Msg("'fsstore' directive is deprecated, use 'msg_store fs' instead") + store.log.Msg("'fsstore' directive is deprecated, use 'msg_store fs' instead") return modconfig.ModuleFromNode("storage.blob", append([]string{"fs"}, node.Args...), node, m.Globals, &blobStore) }) @@ -141,7 +145,7 @@ func (store *Storage) Configure(inlineArgs []string, cfg *config.Map) error { }, &blobStore) cfg.StringList("compression", false, false, []string{"off"}, &compression) cfg.DataSize("appendlimit", false, false, 32*1024*1024, &appendlimitVal) - cfg.Bool("debug", true, false, &store.Log.Debug) + cfg.Bool("debug", true, false, &store.log.Debug) cfg.Int("sqlite3_cache_size", false, false, 0, &opts.CacheSize) cfg.Int("sqlite3_busy_timeout", false, false, 5000, &opts.BusyTimeout) cfg.Bool("disable_recent", false, true, &opts.DisableRecent) @@ -175,9 +179,9 @@ func (store *Storage) Configure(inlineArgs []string, cfg *config.Map) error { if sqliteprovider.IsSqliteDriver(driver) { if sqliteprovider.IsTranspiled { - store.Log.Println("using transpiled SQLite (modernc.org/sqlite)") + store.log.Println("using transpiled SQLite (modernc.org/sqlite)") } else if sqliteprovider.IsAvailable { - store.Log.Debugln("using cgo SQLite") + store.log.Debugln("using cgo SQLite") } else { return errors.New("imapsql: SQLite is not supported, recompile without no_sqlite3 tag set") } @@ -206,7 +210,7 @@ func (store *Storage) Configure(inlineArgs []string, cfg *config.Map) error { } if authNormalize != "auto" { - store.Log.Msg("auth_normalize in storage.imapsql is deprecated and will be removed in the next release, use storage_map in imap config instead") + store.log.Msg("auth_normalize in storage.imapsql is deprecated and will be removed in the next release, use storage_map in imap config instead") } authNormFunc, ok := authz.NormalizeFuncs[authNormalize] if !ok { @@ -216,7 +220,7 @@ func (store *Storage) Configure(inlineArgs []string, cfg *config.Map) error { return authNormFunc(s) } if store.authMap != nil { - store.Log.Msg("auth_map in storage.imapsql is deprecated and will be removed in the next release, use storage_map in imap config instead") + store.log.Msg("auth_map in storage.imapsql is deprecated and will be removed in the next release, use storage_map in imap config instead") store.authNormalize = func(ctx context.Context, username string) (string, error) { username, err := authNormFunc(username) if err != nil { @@ -230,7 +234,7 @@ func (store *Storage) Configure(inlineArgs []string, cfg *config.Map) error { } } - opts.Log = &store.Log + opts.Log = store.log if appendlimitVal == -1 { opts.MaxMsgBytes = nil @@ -281,7 +285,7 @@ func (store *Storage) Configure(inlineArgs []string, cfg *config.Map) error { store.dsn = dsn store.blobStore = blobStore store.opts = opts - store.Log.Debugln("go-imap-sql version", imapsql.VersionStr) + store.log.Debugln("go-imap-sql version", imapsql.VersionStr) return nil } @@ -307,21 +311,21 @@ func (store *Storage) EnableUpdatePipe(mode updatepipe.BackendMode) error { sockPath := filepath.Join( config.RuntimeDirectory, fmt.Sprintf("sql-%s.sock", hex.EncodeToString(dbId[:]))) - store.Log.DebugMsg("using unix socket for external updates", "path", sockPath) + store.log.DebugMsg("using unix socket for external updates", "path", sockPath) store.updPipe = &updatepipe.UnixSockPipe{ SockPath: sockPath, - Log: log.Logger{Name: "storage.imapsql/updpipe", Debug: store.Log.Debug}, + Log: store.log.Sublogger("updpipe"), } case "postgres": - store.Log.DebugMsg("using PostgreSQL broker for external updates") + store.log.DebugMsg("using PostgreSQL broker for external updates") ps, err := pubsub.NewPQ(strings.Join(store.dsn, " ")) if err != nil { return fmt.Errorf("enable_update_pipe: %w", err) } - ps.Log = log.Logger{Name: "storage.imapsql/updpipe/pubsub", Debug: store.Log.Debug} + ps.Log = store.log.Sublogger("updpipe/pubsub") pipe := &updatepipe.PubSubPipe{ PubSub: ps, - Log: log.Logger{Name: "storage.imapsql/updpipe", Debug: store.Log.Debug}, + Log: store.log.Sublogger("updpipe"), } store.Back.UpdateManager().ExternalUnsubscribe = pipe.Unsubscribe store.Back.UpdateManager().ExternalSubscribe = pipe.Subscribe @@ -354,7 +358,7 @@ func (store *Storage) EnableUpdatePipe(mode updatepipe.BackendMode) error { // Ensure we sent all outbound updates. for upd := range outbound { if err := store.updPipe.Push(upd); err != nil { - store.Log.Error("IMAP update pipe push failed", err) + store.log.Error("IMAP update pipe push failed", err) } } store.updPushStop <- struct{}{} @@ -368,15 +372,15 @@ func (store *Storage) EnableUpdatePipe(mode updatepipe.BackendMode) error { for { select { case u := <-inbound: - store.Log.DebugMsg("external update received", "type", u.Type, "key", u.Key) + store.log.DebugMsg("external update received", "type", u.Type, "key", u.Key) store.Back.UpdateManager().ExternalUpdate(u) case u, ok := <-outbound: if !ok { return } - store.Log.DebugMsg("sending external update", "type", u.Type, "key", u.Key) + store.log.DebugMsg("sending external update", "type", u.Type, "key", u.Key) if err := store.updPipe.Push(u); err != nil { - store.Log.Error("IMAP update pipe push failed", err) + store.log.Error("IMAP update pipe push failed", err) } } } @@ -420,7 +424,7 @@ func (store *Storage) Lookup(ctx context.Context, key string) (string, bool, err return "", false, err } if err := usr.Logout(); err != nil { - store.Log.Error("logout failed", err, "username", accountName) + store.log.Error("logout failed", err, "username", accountName) } return "", true, nil @@ -429,7 +433,7 @@ func (store *Storage) Lookup(ctx context.Context, key string) (string, bool, err func (store *Storage) Stop() error { // Stop backend from generating new updates. if err := store.Back.Close(); err != nil { - store.Log.Error("close backend failed", err) + store.log.Error("close backend failed", err) } // Wait for 'updates replicate' goroutine to actually stop so we will send @@ -440,7 +444,7 @@ func (store *Storage) Stop() error { <-store.updpushstop if err := store.updPipe.Close(); err != nil { - store.Log.Error("updatepipe close failed", err) + store.log.Error("updatepipe close failed", err) } } @@ -456,6 +460,6 @@ func (store *Storage) SupportedThreadAlgorithms() []sortthread.ThreadAlgorithm { } func init() { - module.Register("storage.imapsql", New) - module.Register("target.imapsql", New) + modules.Register("storage.imapsql", New) + modules.Register("target.imapsql", New) } diff --git a/internal/table/chain.go b/internal/table/chain.go index 3b29a1c0..819002a9 100644 --- a/internal/table/chain.go +++ b/internal/table/chain.go @@ -23,7 +23,9 @@ import ( "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) type Chain struct { @@ -34,7 +36,7 @@ type Chain struct { optional []bool } -func NewChain(modName, instName string) (module.Module, error) { +func NewChain(_ *container.C, modName, instName string) (module.Module, error) { return &Chain{ modName: modName, instName: instName, @@ -127,5 +129,5 @@ STEP: } func init() { - module.Register("table.chain", NewChain) + modules.Register("table.chain", NewChain) } diff --git a/internal/table/email_localpart.go b/internal/table/email_localpart.go index 4371a3e3..b500b31c 100644 --- a/internal/table/email_localpart.go +++ b/internal/table/email_localpart.go @@ -23,7 +23,9 @@ import ( "github.com/foxcpp/maddy/framework/address" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) type EmailLocalpart struct { @@ -32,7 +34,7 @@ type EmailLocalpart struct { allowNonEmail bool } -func NewEmailLocalpart(modName, instName string) (module.Module, error) { +func NewEmailLocalpart(_ *container.C, modName, instName string) (module.Module, error) { return &EmailLocalpart{ modName: modName, instName: instName, @@ -65,6 +67,6 @@ func (s *EmailLocalpart) Lookup(ctx context.Context, key string) (string, bool, } func init() { - module.Register("table.email_localpart", NewEmailLocalpart) - module.Register("table.email_localpart_optional", NewEmailLocalpart) + modules.Register("table.email_localpart", NewEmailLocalpart) + modules.Register("table.email_localpart_optional", NewEmailLocalpart) } diff --git a/internal/table/email_with_domain.go b/internal/table/email_with_domain.go index 62d9c565..6ebb706b 100644 --- a/internal/table/email_with_domain.go +++ b/internal/table/email_with_domain.go @@ -24,22 +24,24 @@ import ( "github.com/foxcpp/maddy/framework/address" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) type EmailWithDomain struct { modName string instName string domains []string - log log.Logger + log *log.Logger } -func NewEmailWithDomain(modName, instName string) (module.Module, error) { +func NewEmailWithDomain(c *container.C, modName, instName string) (module.Module, error) { return &EmailWithDomain{ modName: modName, instName: instName, - log: log.Logger{Name: modName}, + log: c.DefaultLogger.Sublogger(modName), }, nil } @@ -86,5 +88,5 @@ func (s *EmailWithDomain) LookupMulti(ctx context.Context, key string) ([]string } func init() { - module.Register("table.email_with_domain", NewEmailWithDomain) + modules.Register("table.email_with_domain", NewEmailWithDomain) } diff --git a/internal/table/file.go b/internal/table/file.go index 3b7fcc03..47d8f372 100644 --- a/internal/table/file.go +++ b/internal/table/file.go @@ -29,8 +29,10 @@ import ( "time" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) const FileModName = "table.file" @@ -46,16 +48,16 @@ type File struct { stopReloader chan struct{} forceReload chan struct{} - log log.Logger + log *log.Logger } -func NewFile(_, instName string) (module.Module, error) { +func NewFile(c *container.C, modName, instName string) (module.Module, error) { m := &File{ instName: instName, m: make(map[string][]string), stopReloader: make(chan struct{}), forceReload: make(chan struct{}), - log: log.Logger{Name: FileModName}, + log: c.DefaultLogger.Sublogger(modName), } return m, nil @@ -258,5 +260,5 @@ func (f *File) LookupMulti(_ context.Context, val string) ([]string, error) { } func init() { - module.Register(FileModName, NewFile) + modules.Register(FileModName, NewFile) } diff --git a/internal/table/file_test.go b/internal/table/file_test.go index abfc81f8..33cabe05 100644 --- a/internal/table/file_test.go +++ b/internal/table/file_test.go @@ -25,6 +25,7 @@ import ( "time" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/internal/testutils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -118,7 +119,7 @@ func TestFileReload(t *testing.T) { t.Fatal(err) } - mod, err := NewFile("", "") + mod, err := NewFile(container.New(),"", "") if err != nil { t.Fatal(err) } @@ -179,7 +180,7 @@ func TestFileReload_Broken(t *testing.T) { } require.NoError(t, f.Close()) - mod, err := NewFile("", "") + mod, err := NewFile(container.New(), "", "") if err != nil { t.Fatal(err) } @@ -240,7 +241,7 @@ func TestFileReload_Removed(t *testing.T) { t.Fatal(err) } - mod, err := NewFile("", "") + mod, err := NewFile(container.New(), "", "") if err != nil { t.Fatal(err) } diff --git a/internal/table/identity.go b/internal/table/identity.go index 6db17df8..2a179b27 100644 --- a/internal/table/identity.go +++ b/internal/table/identity.go @@ -22,7 +22,9 @@ import ( "context" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) type Identity struct { @@ -30,7 +32,7 @@ type Identity struct { instName string } -func NewIdentity(modName, instName string) (module.Module, error) { +func NewIdentity(_ *container.C, modName, instName string) (module.Module, error) { return &Identity{ modName: modName, instName: instName, @@ -54,5 +56,5 @@ func (s *Identity) Lookup(_ context.Context, key string) (string, bool, error) { } func init() { - module.Register("table.identity", NewIdentity) + modules.Register("table.identity", NewIdentity) } diff --git a/internal/table/regexp.go b/internal/table/regexp.go index 0c22cd8a..1cf214a9 100644 --- a/internal/table/regexp.go +++ b/internal/table/regexp.go @@ -25,7 +25,9 @@ import ( "strings" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) type Regexp struct { @@ -38,7 +40,7 @@ type Regexp struct { expandPlaceholders bool } -func NewRegexp(modName, instName string) (module.Module, error) { +func NewRegexp(_ *container.C, modName, instName string) (module.Module, error) { return &Regexp{ modName: modName, instName: instName, @@ -121,5 +123,5 @@ func (r *Regexp) Lookup(ctx context.Context, key string) (string, bool, error) { } func init() { - module.Register("table.regexp", NewRegexp) + modules.Register("table.regexp", NewRegexp) } diff --git a/internal/table/sql_query.go b/internal/table/sql_query.go index 6fa0f730..248dc874 100644 --- a/internal/table/sql_query.go +++ b/internal/table/sql_query.go @@ -26,8 +26,10 @@ import ( "strings" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" sqliteprovider "github.com/foxcpp/maddy/internal/sqlite" _ "github.com/lib/pq" ) @@ -47,7 +49,7 @@ type SQL struct { del *sql.Stmt } -func NewSQL(modName, instName string) (module.Module, error) { +func NewSQL(_ *container.C, modName, instName string) (module.Module, error) { return &SQL{ modName: modName, instName: instName, @@ -265,5 +267,5 @@ func (s *SQL) SetKey(k, v string) error { } func init() { - module.Register("table.sql_query", NewSQL) + modules.Register("table.sql_query", NewSQL) } diff --git a/internal/table/sql_query_test.go b/internal/table/sql_query_test.go index 99269c62..ac976f6c 100644 --- a/internal/table/sql_query_test.go +++ b/internal/table/sql_query_test.go @@ -28,12 +28,13 @@ import ( "testing" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/internal/testutils" ) func TestSQL(t *testing.T) { path := testutils.Dir(t) - mod, err := NewSQL("sql_table", "") + mod, err := NewSQL(container.New(),"sql_table", "") if err != nil { t.Fatal("Module create failed:", err) } diff --git a/internal/table/sql_table.go b/internal/table/sql_table.go index edadb919..c8f80216 100644 --- a/internal/table/sql_table.go +++ b/internal/table/sql_table.go @@ -23,7 +23,9 @@ import ( "fmt" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" _ "github.com/lib/pq" ) @@ -34,7 +36,7 @@ type SQLTable struct { wrapped *SQL } -func NewSQLTable(modName, instName string) (module.Module, error) { +func NewSQLTable(_ *container.C, modName, instName string) (module.Module, error) { return &SQLTable{ modName: modName, instName: instName, @@ -171,5 +173,5 @@ func (s *SQLTable) SetKey(k, v string) error { } func init() { - module.Register("table.sql_table", NewSQLTable) + modules.Register("table.sql_table", NewSQLTable) } diff --git a/internal/table/static.go b/internal/table/static.go index 4444f991..21b09c1f 100644 --- a/internal/table/static.go +++ b/internal/table/static.go @@ -22,7 +22,9 @@ import ( "context" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) type Static struct { @@ -32,7 +34,7 @@ type Static struct { m map[string][]string } -func NewStatic(modName, instName string) (module.Module, error) { +func NewStatic(_ *container.C, modName, instName string) (module.Module, error) { return &Static{ modName: modName, instName: instName, @@ -73,5 +75,5 @@ func (s *Static) LookupMulti(ctx context.Context, key string) ([]string, error) } func init() { - module.Register("table.static", NewStatic) + modules.Register("table.static", NewStatic) } diff --git a/internal/target/delivery.go b/internal/target/delivery.go index 1c3450fa..75959136 100644 --- a/internal/target/delivery.go +++ b/internal/target/delivery.go @@ -23,7 +23,8 @@ import ( "github.com/foxcpp/maddy/framework/module" ) -func DeliveryLogger(l log.Logger, msgMeta *module.MsgMetadata) log.Logger { +func DeliveryLogger(parent *log.Logger, msgMeta *module.MsgMetadata) *log.Logger { + l := parent.Sublogger("") fields := make(map[string]interface{}, len(l.Fields)+1) for k, v := range l.Fields { fields[k] = v diff --git a/internal/target/queue/queue.go b/internal/target/queue/queue.go index 11b79683..1aec8213 100644 --- a/internal/target/queue/queue.go +++ b/internal/target/queue/queue.go @@ -79,9 +79,11 @@ import ( "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/dsn" "github.com/foxcpp/maddy/internal/msgpipeline" "github.com/foxcpp/maddy/internal/target" @@ -145,7 +147,7 @@ type Queue struct { // after start-up for whatever reason it will not affect the queue. postInitDelay time.Duration - Log log.Logger + log *log.Logger Target module.DeliveryTarget deliveryWg sync.WaitGroup @@ -187,13 +189,13 @@ type queueSlot struct { Body buffer.Buffer } -func NewQueue(_, instName string) (module.Module, error) { +func New(c *container.C, modName, instName string) (module.Module, error) { q := &Queue{ name: instName, initialRetryTime: 15 * time.Minute, retryTimeScale: 1.25, postInitDelay: 10 * time.Second, - Log: log.Logger{Name: "queue"}, + log: c.DefaultLogger.Sublogger(modName), } return q, nil } @@ -208,7 +210,7 @@ func (q *Queue) Configure(inlineArgs []string, cfg *config.Map) error { return errors.New("queue: wrong amount of inline arguments") } - cfg.Bool("debug", true, false, &q.Log.Debug) + cfg.Bool("debug", true, false, &q.log.Debug) cfg.Int("max_tries", false, false, 20, &q.maxTries) cfg.Int("max_parallelism", false, false, 16, &q.maxParallelism) cfg.Duration("post_init_delay", false, false, q.postInitDelay, &q.postInitDelay) @@ -231,7 +233,7 @@ func (q *Queue) Configure(inlineArgs []string, cfg *config.Map) error { } q.dsnPipeline.(*msgpipeline.MsgPipeline).Hostname = q.hostname - q.dsnPipeline.(*msgpipeline.MsgPipeline).Log = log.Logger{Name: "queue/pipeline", Debug: q.Log.Debug} + q.dsnPipeline.(*msgpipeline.MsgPipeline).Log = q.log.Sublogger("pipeline") } if q.location == "" && q.name == "" { return errors.New("queue: need explicit location directive or inline argument if defined inline") @@ -259,7 +261,7 @@ func (q *Queue) start(maxParallelism int) error { return err } - q.Log.Debugf("delivery target: %T", q.Target) + q.log.Debugf("delivery target: %T", q.Target) return nil } @@ -294,11 +296,11 @@ func (q *Queue) discardBroken(id string) { func (q *Queue) dispatch(ctx context.Context, value TimeSlot[queueSlot]) { slot := value.Value - q.Log.Debugln("starting delivery for", slot.ID) + q.log.Debugln("starting delivery for", slot.ID) q.deliveryWg.Add(1) go func() { - q.Log.Debugln("waiting on delivery semaphore for", slot.ID) + q.log.Debugln("waiting on delivery semaphore for", slot.ID) q.deliverySemaphore <- struct{}{} defer func() { <-q.deliverysemaphore @@ -315,7 +317,7 @@ func (q *Queue) dispatch(ctx context.Context, value TimeSlot[queueSlot]) { } }() - q.Log.Debugln("delivery semaphore acquired for", slot.ID) + q.log.Debugln("delivery semaphore acquired for", slot.ID) var ( meta *QueueMetadata hdr textproto.Header @@ -325,7 +327,7 @@ func (q *Queue) dispatch(ctx context.Context, value TimeSlot[queueSlot]) { var err error meta, hdr, body, err = q.openMessage(slot.ID) if err != nil { - q.Log.Error("read message", err, slot.ID) + q.log.Error("read message", err, slot.ID) return } if meta == nil { @@ -382,7 +384,7 @@ func toSMTPErr(err error) *smtp.SMTPError { } func (q *Queue) tryDelivery(ctx context.Context, meta *QueueMetadata, header textproto.Header, body buffer.Buffer) { - dl := target.DeliveryLogger(q.Log, meta.MsgMeta) + dl := target.DeliveryLogger(q.log, meta.MsgMeta) partialErr := q.deliver(ctx, meta, header, body) dl.Debugf("errors: %v", partialErr.Errs) @@ -469,7 +471,7 @@ func (q *Queue) tryDelivery(ctx context.Context, meta *QueueMetadata, header tex } func (q *Queue) deliver(ctx context.Context, meta *QueueMetadata, header textproto.Header, body buffer.Buffer) partialError { - dl := target.DeliveryLogger(q.Log, meta.MsgMeta) + dl := target.DeliveryLogger(q.log, meta.MsgMeta) perr := partialError{ Errs: map[string]error{}, statusLock: new(sync.Mutex), @@ -650,7 +652,7 @@ func (q *Queue) StartDelivery(ctx context.Context, msgMeta *module.MsgMetadata, func (q *Queue) removeFromDisk(msgMeta *module.MsgMetadata) { id := msgMeta.ID - dl := target.DeliveryLogger(q.Log, msgMeta) + dl := target.DeliveryLogger(q.log, msgMeta) // Order is important. // If we remove header and body but can't remove meta now - readDiskQueue @@ -692,18 +694,18 @@ func (q *Queue) readDiskQueue() error { meta, err := q.readMessageMeta(id) if err != nil { - q.Log.Printf("failed to read meta-data, skipping: %v (msg ID = %s)", err, id) + q.log.Printf("failed to read meta-data, skipping: %v (msg ID = %s)", err, id) continue } // Check header file existence. if _, err := os.Stat(filepath.Join(q.location, id+".header")); err != nil { if os.IsNotExist(err) { - q.Log.Printf("header file doesn't exist for msg ID = %s", id) + q.log.Printf("header file doesn't exist for msg ID = %s", id) q.tryRemoveDanglingFile(id + ".meta") q.tryRemoveDanglingFile(id + ".body") } else { - q.Log.Printf("skipping nonstat'able header file: %v (msg ID = %s)", err, id) + q.log.Printf("skipping nonstat'able header file: %v (msg ID = %s)", err, id) } continue } @@ -711,11 +713,11 @@ func (q *Queue) readDiskQueue() error { // Check body file existence. if _, err := os.Stat(filepath.Join(q.location, id+".body")); err != nil { if os.IsNotExist(err) { - q.Log.Printf("body file doesn't exist for msg ID = %s", id) + q.log.Printf("body file doesn't exist for msg ID = %s", id) q.tryRemoveDanglingFile(id + ".meta") q.tryRemoveDanglingFile(id + ".header") } else { - q.Log.Printf("skipping nonstat'able body file: %v (msg ID = %s)", err, id) + q.log.Printf("skipping nonstat'able body file: %v (msg ID = %s)", err, id) } continue } @@ -734,7 +736,7 @@ func (q *Queue) readDiskQueue() error { nextTryTime = time.Now().Add(q.postInitDelay) } - q.Log.Debugf("will try to deliver (msg ID = %s) in %v (%v)", id, time.Until(nextTryTime), nextTryTime) + q.log.Debugf("will try to deliver (msg ID = %s) in %v (%v)", id, time.Until(nextTryTime), nextTryTime) q.wheel.Add(nextTryTime, queueSlot{ ID: id, }) @@ -744,7 +746,7 @@ func (q *Queue) readDiskQueue() error { } if loadedCount != 0 { - q.Log.Printf("loaded %d saved queue entries", loadedCount) + q.log.Printf("loaded %d saved queue entries", loadedCount) } return nil @@ -760,7 +762,7 @@ func (q *Queue) storeNewMessage(meta *QueueMetadata, header textproto.Header, bo } defer func() { if err := headerFile.Close(); err != nil { - q.Log.Error("header file close failed", err) + q.log.Error("header file close failed", err) } }() @@ -776,7 +778,7 @@ func (q *Queue) storeNewMessage(meta *QueueMetadata, header textproto.Header, bo } defer func() { if err := bodyReader.Close(); err != nil { - q.Log.Error("bodyReader close failed", err) + q.log.Error("bodyReader close failed", err) } }() @@ -787,7 +789,7 @@ func (q *Queue) storeNewMessage(meta *QueueMetadata, header textproto.Header, bo } defer func() { if err := bodyFile.Close(); err != nil { - q.Log.Error("body file close failed", err) + q.log.Error("body file close failed", err) } }() @@ -834,7 +836,7 @@ func (q *Queue) updateMetadataOnDisk(meta *QueueMetadata) error { } defer func() { if err := file.Close(); err != nil { - q.Log.Error("metadata file close failed", err) + q.log.Error("metadata file close failed", err) } }() @@ -867,7 +869,7 @@ func (q *Queue) readMessageMeta(id string) (*QueueMetadata, error) { } defer func() { if err := file.Close(); err != nil { - q.Log.Error("metadata file close failed", err) + q.log.Error("metadata file close failed", err) } }() @@ -894,10 +896,10 @@ type BufferedReadCloser struct { func (q *Queue) tryRemoveDanglingFile(name string) { if err := os.Remove(filepath.Join(q.location, name)); err != nil { - q.Log.Error("dangling file remove failed", err) + q.log.Error("dangling file remove failed", err) return } - q.Log.Printf("removed dangling file %s", name) + q.log.Printf("removed dangling file %s", name) } func (q *Queue) openMessage(id string) (*QueueMetadata, textproto.Header, buffer.Buffer, error) { @@ -956,7 +958,7 @@ func (q *Queue) emitDSN(meta *QueueMetadata, header textproto.Header, failedRcpt dsnID, err := module.GenerateMsgID() if err != nil { - q.Log.Error("rand.Rand error", err) + q.log.Error("rand.Rand error", err) return } @@ -996,7 +998,7 @@ func (q *Queue) emitDSN(meta *QueueMetadata, header textproto.Header, failedRcpt } var dsnBodyBlob bytes.Buffer - dl := target.DeliveryLogger(q.Log, meta.MsgMeta) + dl := target.DeliveryLogger(q.log, meta.MsgMeta) dsnHeader, err := dsn.GenerateDSN(meta.MsgMeta.SMTPOpts.UTF8, dsnEnvelope, mtaInfo, rcptInfo, header, &dsnBodyBlob) if err != nil { dl.Error("failed to generate fail DSN", err) @@ -1053,5 +1055,5 @@ func (q *Queue) emitDSN(meta *QueueMetadata, header textproto.Header, failedRcpt } func init() { - module.Register("target.queue", NewQueue) + modules.Register("target.queue", New) } diff --git a/internal/target/queue/queue_test.go b/internal/target/queue/queue_test.go index 659770b6..2ad9e821 100644 --- a/internal/target/queue/queue_test.go +++ b/internal/target/queue/queue_test.go @@ -35,6 +35,7 @@ import ( "github.com/emersion/go-message/textproto" "github.com/emersion/go-smtp" "github.com/foxcpp/maddy/framework/buffer" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" @@ -58,7 +59,7 @@ func cleanQueue(t *testing.T, q *Queue) { } func newTestQueueDir(t *testing.T, target module.DeliveryTarget, dir string) *Queue { - mod, _ := NewQueue("", "queue") + mod, _ := New(container.New(), "", "queue") q := mod.(*Queue) q.initialRetryTime = 0 q.retryTimeScale = 1 @@ -68,9 +69,9 @@ func newTestQueueDir(t *testing.T, target module.DeliveryTarget, dir string) *Qu q.Target = target if testing.Verbose() { - q.Log = testutils.Logger(t, "queue") + q.log = testutils.Logger(t, "queue") } else { - q.Log = log.Logger{Out: log.NopOutput{}} + q.log = &log.NopLogger } if err := q.start(1); err != nil { diff --git a/internal/target/remote/connect.go b/internal/target/remote/connect.go index a8c5cb97..4456d927 100644 --- a/internal/target/remote/connect.go +++ b/internal/target/remote/connect.go @@ -89,7 +89,7 @@ func (rd *remoteDelivery) connect(ctx context.Context, conn mxConn, host string, tlsCfg.ServerName = host } - rd.Log.DebugMsg("trying", "remote_server", host, "domain", conn.domain) + rd.log.DebugMsg("trying", "remote_server", host, "domain", conn.domain) retry: // smtpconn.C default TLS behavior is not useful for us, we want to handle @@ -110,7 +110,7 @@ retry: // rejecting STARTTLS (despite advertising STARTTLS). // We err on the caution side here and do not perform any fallbacks. if err := conn.DirectClose(); err != nil { - rd.Log.Error("conn.DirectClose failed", err) + rd.log.Error("conn.DirectClose failed", err) } return module.TLSNone, nil, err } @@ -127,24 +127,24 @@ retry: // error happens with InsecureSkipVerify too (e.g. certificate is // *too* broken). if isVerifyError(err) && tlsLevel == module.TLSAuthenticated { - rd.Log.Error("TLS verify error, trying without authentication", err, "remote_server", host, "domain", conn.domain) + rd.log.Error("TLS verify error, trying without authentication", err, "remote_server", host, "domain", conn.domain) tlsCfg.InsecureSkipVerify = true tlsLevel = module.TLSEncrypted // TODO: Check go-smtp code to make TLS verification errors // non-sticky so we can properly send QUIT in this case. if err := conn.DirectClose(); err != nil { - rd.Log.Error("conn.DirectClose failed", err) + rd.log.Error("conn.DirectClose failed", err) } goto retry } - rd.Log.Error("TLS error, trying plaintext", err, "remote_server", host, "domain", conn.domain) + rd.log.Error("TLS error, trying plaintext", err, "remote_server", host, "domain", conn.domain) tlsCfg = nil tlsLevel = module.TLSNone if err := conn.DirectClose(); err != nil { - rd.Log.Error("conn.DirectClose failed", err) + rd.log.Error("conn.DirectClose failed", err) } goto retry @@ -208,7 +208,7 @@ func (rd *remoteDelivery) attemptMX(ctx context.Context, conn *mxConn, record *n func (rd *remoteDelivery) closeConn(c *mxConn) { if err := c.Close(); err != nil { - rd.Log.Error("client connection close failed", err) + rd.log.Error("client connection close failed", err) } } @@ -228,10 +228,10 @@ func (rd *remoteDelivery) connectionForDomain(ctx context.Context, domain string // connection with weaker security. if pooledConn != nil && !rd.msgMeta.SMTPOpts.RequireTLS { conn = pooledConn.(*mxConn) - rd.Log.Msg("reusing cached connection", "domain", domain, "transactions_counter", conn.transactions, + rd.log.Msg("reusing cached connection", "domain", domain, "transactions_counter", conn.transactions, "local_addr", conn.LocalAddr(), "remote_addr", conn.RemoteAddr()) } else { - rd.Log.DebugMsg("opening new connection", "domain", domain, "cache_ignored", pooledConn != nil) + rd.log.DebugMsg("opening new connection", "domain", domain, "cache_ignored", pooledConn != nil) conn, err = rd.newConn(ctx, domain) if err != nil { return nil, err @@ -302,7 +302,7 @@ func (rd *remoteDelivery) newConn(ctx context.Context, domain string) (*mxConn, } conn.Dialer = rd.rt.dialer - conn.Log = rd.Log + conn.Log = rd.log conn.Hostname = rd.rt.hostname conn.AddrInSMTPMsg = true if rd.rt.connectTimeout != 0 { @@ -340,7 +340,7 @@ func (rd *remoteDelivery) newConn(ctx context.Context, domain string) (*mxConn, if err := rd.attemptMX(ctx, &conn, record); err != nil { if len(records) != 0 { - rd.Log.Error("cannot use MX", err, "remote_server", record.Host, "domain", domain) + rd.log.Error("cannot use MX", err, "remote_server", record.Host, "domain", domain) } lastErr = err continue diff --git a/internal/target/remote/dane_delivery_test.go b/internal/target/remote/dane_delivery_test.go index 8d1cec22..cb441aca 100644 --- a/internal/target/remote/dane_delivery_test.go +++ b/internal/target/remote/dane_delivery_test.go @@ -35,7 +35,7 @@ import ( func targetWithExtResolver(t *testing.T, zones map[string]mockdns.Zone) (*mockdns.Server, *Target) { l := testutils.Logger(t, "mockdns") - dnsSrv, err := mockdns.NewServerWithLogger(zones, &l, false) + dnsSrv, err := mockdns.NewServerWithLogger(zones, l, false) if err != nil { t.Fatal(err) } diff --git a/internal/target/remote/mxauth_test.go b/internal/target/remote/mxauth_test.go index 5f698554..8a9e7e37 100644 --- a/internal/target/remote/mxauth_test.go +++ b/internal/target/remote/mxauth_test.go @@ -337,7 +337,7 @@ func TestRemoteDelivery_AuthMX_DNSSEC(t *testing.T) { } l := testutils.Logger(t, "mockdns") - dnsSrv, err := mockdns.NewServerWithLogger(zones, &l, false) + dnsSrv, err := mockdns.NewServerWithLogger(zones, l, false) if err != nil { t.Fatal(err) } @@ -383,7 +383,7 @@ func TestRemoteDelivery_AuthMX_DNSSEC_Fail(t *testing.T) { } l := testutils.Logger(t, "mockdns") - dnsSrv, err := mockdns.NewServerWithLogger(zones, &l, false) + dnsSrv, err := mockdns.NewServerWithLogger(zones, l, false) if err != nil { t.Fatal(err) } diff --git a/internal/target/remote/policy_group.go b/internal/target/remote/policy_group.go index 992bcfa4..a7d949b6 100644 --- a/internal/target/remote/policy_group.go +++ b/internal/target/remote/policy_group.go @@ -21,7 +21,9 @@ package remote import ( "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) // PolicyGroup is a module container for a group of Policy implementations. @@ -96,7 +98,7 @@ func (pg *PolicyGroup) InstanceName() string { } func init() { - module.Register("mx_auth", func(_, instName string) (module.Module, error) { + modules.Register("mx_auth", func(_ *container.C, _, instName string) (module.Module, error) { return &PolicyGroup{ instName: instName, pols: map[string]module.MXAuthPolicy{}, diff --git a/internal/target/remote/remote.go b/internal/target/remote/remote.go index b382a88b..281b7ce6 100644 --- a/internal/target/remote/remote.go +++ b/internal/target/remote/remote.go @@ -41,10 +41,12 @@ import ( "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" tls2 "github.com/foxcpp/maddy/framework/config/tls" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/dns" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/limits" "github.com/foxcpp/maddy/internal/smtpconn/pool" "github.com/foxcpp/maddy/internal/target" @@ -78,7 +80,7 @@ type Target struct { pool *pool.P connReuseLimit int - Log log.Logger + log *log.Logger connectTimeout time.Duration commandTimeout time.Duration @@ -87,13 +89,13 @@ type Target struct { var _ module.DeliveryTarget = &Target{} -func New(_, instName string) (module.Module, error) { +func New(c *container.C, modName, instName string) (module.Module, error) { // Keep this synchronized with testTarget. return &Target{ name: instName, resolver: dns.DefaultResolver(), dialer: (&net.Dialer{}).DialContext, - Log: log.Logger{Name: "remote"}, + log: c.DefaultLogger.Sublogger(modName), }, nil } @@ -105,13 +107,13 @@ func (rt *Target) Configure(inlineArgs []string, cfg *config.Map) error { var err error rt.extResolver, err = dns.NewExtResolver() if err != nil { - rt.Log.Error("cannot initialize DNSSEC-aware resolver, DNSSEC and DANE are not available", err) + rt.log.Error("cannot initialize DNSSEC-aware resolver, DNSSEC and DANE are not available", err) } cfg.String("hostname", true, true, "", &rt.hostname) cfg.String("local_ip", false, false, "", &rt.localIP) cfg.Bool("force_ipv4", false, false, &rt.ipv4) - cfg.Bool("debug", true, false, &rt.Log.Debug) + cfg.Bool("debug", true, false, &rt.log.Debug) cfg.Custom("tls_client", true, false, func() (interface{}, error) { return &tls.Config{}, nil }, tls2.TLSClientBlock, &rt.tlsConfig) @@ -206,8 +208,8 @@ func (rt *Target) InstanceName() string { type remoteDelivery struct { rt *Target mailFrom string - msgMeta *module.MsgMetadata - Log log.Logger + msgMeta *module.MsgMetadata + log *log.Logger recipients []string connections map[string]*mxConn @@ -269,7 +271,7 @@ func (rt *Target) StartDelivery(ctx context.Context, msgMeta *module.MsgMetadata rt: rt, mailFrom: mailFrom, msgMeta: msgMeta, - Log: target.DeliveryLogger(rt.Log, msgMeta), + log: target.DeliveryLogger(rt.log, msgMeta), connections: map[string]*mxConn{}, policies: policies, }, nil @@ -424,7 +426,7 @@ func (rd *remoteDelivery) BodyNonAtomic(ctx context.Context, c module.StatusColl } defer func() { if err := bodyR.Close(); err != nil { - rd.Log.Error("failed to close message buffer", err) + rd.log.Error("failed to close message buffer", err) } }() @@ -456,11 +458,11 @@ func (rd *remoteDelivery) Close() error { conn.transactions++ if !conn.Usable() { - rd.Log.Debugf("disconnected %v from %s (errored=%v,transactions=%v,disconnected before=%v)", + rd.log.Debugf("disconnected %v from %s (errored=%v,transactions=%v,disconnected before=%v)", conn.LocalAddr(), conn.ServerName(), conn.errored, conn.transactions, conn.Client() == nil) rd.closeConn(conn) } else { - rd.Log.Debugf("returning connection %v for %s to pool", conn.LocalAddr(), conn.ServerName()) + rd.log.Debugf("returning connection %v for %s to pool", conn.LocalAddr(), conn.ServerName()) rd.rt.pool.Return(conn.domain, conn) } } @@ -489,5 +491,5 @@ func (rd *remoteDelivery) Close() error { } func init() { - module.Register("target.remote", New) + modules.Register("target.remote", New) } diff --git a/internal/target/remote/remote_test.go b/internal/target/remote/remote_test.go index 1578303b..73bc0231 100644 --- a/internal/target/remote/remote_test.go +++ b/internal/target/remote/remote_test.go @@ -34,6 +34,7 @@ import ( "github.com/foxcpp/go-mtasts" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/dns" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/module" @@ -59,7 +60,7 @@ func testTarget(t *testing.T, zones map[string]mockdns.Zone, extResolver *dns.Ex dialer: resolver.DialContext, extResolver: extResolver, tlsConfig: &tls.Config{}, - Log: testutils.Logger(t, "remote"), + log: testutils.Logger(t, "remote"), policies: extraPolicies, limits: &limits.Group{}, pool: pool.New(pool.Config{ @@ -74,7 +75,7 @@ func testTarget(t *testing.T, zones map[string]mockdns.Zone, extResolver *dns.Ex } func testSTSPolicy(t *testing.T, zones map[string]mockdns.Zone, mtastsGet func(context.Context, string) (*mtasts.Policy, error)) *mtastsPolicy { - m, err := NewMTASTSPolicy("mx_auth.mtasts", "test") + m, err := NewMTASTSPolicy(container.New(),"mx_auth.mtasts", "test") if err != nil { t.Fatal(err) } @@ -100,7 +101,7 @@ func testSTSPolicy(t *testing.T, zones map[string]mockdns.Zone, mtastsGet func(c } func testDANEPolicy(t *testing.T, extR *dns.ExtResolver) *danePolicy { - m, err := NewDANEPolicy("mx_auth.dane", "test") + m, err := NewDANEPolicy(container.New(),"mx_auth.dane", "test") if err != nil { t.Fatal(err) } diff --git a/internal/target/remote/security.go b/internal/target/remote/security.go index efbbb1d2..c40c5fc1 100644 --- a/internal/target/remote/security.go +++ b/internal/target/remote/security.go @@ -28,11 +28,13 @@ import ( "github.com/foxcpp/go-mtasts" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/dns" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/future" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/target" ) @@ -41,21 +43,21 @@ type ( cache *mtasts.Cache mtastsGet func(context.Context, string) (*mtasts.Policy, error) updaterStop chan struct{} - log log.Logger + log *log.Logger instName string } mtastsDelivery struct { c *mtastsPolicy domain string policyFut *future.Future - log log.Logger + log *log.Logger } ) -func NewMTASTSPolicy(_, instName string) (module.Module, error) { +func NewMTASTSPolicy(c *container.C, modName, instName string) (module.Module, error) { return &mtastsPolicy{ instName: instName, - log: log.Logger{Name: "mx_auth.mtasts", Debug: log.DefaultLogger.Debug}, + log: c.DefaultLogger.Sublogger(modName), }, nil } @@ -238,14 +240,14 @@ func (c *mtastsDelivery) Reset(msgMeta *module.MsgMetadata) { // Stub that will be removed in 0.5. type stsPreloadPolicy struct { - log log.Logger + log *log.Logger instName string } -func NewSTSPreload(_, instName string) (module.Module, error) { +func NewSTSPreload(c *container.C, modName, instName string) (module.Module, error) { return &stsPreloadPolicy{ instName: instName, - log: log.Logger{Name: "mx_auth.sts_preload", Debug: log.DefaultLogger.Debug}, + log: c.DefaultLogger.Sublogger(modName), }, nil } @@ -300,7 +302,7 @@ type dnssecPolicy struct { instName string } -func NewDNSSECPolicy(_, instName string) (module.Module, error) { +func NewDNSSECPolicy(_ *container.C, _, instName string) (module.Module, error) { return &dnssecPolicy{ instName: instName, }, nil @@ -345,7 +347,7 @@ func (dnssecPolicy) CheckConn(ctx context.Context, mxLevel module.MXLevel, tlsLe type ( danePolicy struct { extResolver *dns.ExtResolver - log log.Logger + log *log.Logger instName string } daneDelivery struct { @@ -354,10 +356,10 @@ type ( } ) -func NewDANEPolicy(_, instName string) (module.Module, error) { +func NewDANEPolicy(c *container.C, modName, instName string) (module.Module, error) { return &danePolicy{ instName: instName, - log: log.Logger{Name: "remote/dane", Debug: log.DefaultLogger.Debug}, + log: c.DefaultLogger.Sublogger(modName), }, nil } @@ -529,7 +531,7 @@ type ( } ) -func NewLocalPolicy(_, instName string) (module.Module, error) { +func NewLocalPolicy(_ *container.C, _, instName string) (module.Module, error) { return &localPolicy{ instName: instName, }, nil @@ -623,9 +625,9 @@ func (l *localPolicy) CheckConn(ctx context.Context, mxLevel module.MXLevel, tls } func init() { - module.Register("mx_auth.mtasts", NewMTASTSPolicy) - module.Register("mx_auth.sts_preload", NewSTSPreload) - module.Register("mx_auth.dnssec", NewDNSSECPolicy) - module.Register("mx_auth.dane", NewDANEPolicy) - module.Register("mx_auth.local_policy", NewLocalPolicy) + modules.Register("mx_auth.mtasts", NewMTASTSPolicy) + modules.Register("mx_auth.sts_preload", NewSTSPreload) + modules.Register("mx_auth.dnssec", NewDNSSECPolicy) + modules.Register("mx_auth.dane", NewDANEPolicy) + modules.Register("mx_auth.local_policy", NewLocalPolicy) } diff --git a/internal/target/skeleton.go b/internal/target/skeleton.go index 00d481cc..488ce813 100644 --- a/internal/target/skeleton.go +++ b/internal/target/skeleton.go @@ -31,6 +31,7 @@ import ( "github.com/foxcpp/maddy/framework/config" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) const modName = "target.target_name" @@ -126,5 +127,5 @@ func (d *delivery) Commit(ctx context.Context) error { } func init() { - module.Register(modName, New) + modules.Register(modName, New) } diff --git a/internal/target/smtp/smtp_downstream.go b/internal/target/smtp/smtp_downstream.go index 6c678f0a..5fbbd751 100644 --- a/internal/target/smtp/smtp_downstream.go +++ b/internal/target/smtp/smtp_downstream.go @@ -39,9 +39,11 @@ import ( "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" tls2 "github.com/foxcpp/maddy/framework/config/tls" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/exterrors" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/smtpconn" "github.com/foxcpp/maddy/internal/target" "golang.org/x/net/idna" @@ -62,7 +64,7 @@ type Downstream struct { commandTimeout time.Duration submissionTimeout time.Duration - log log.Logger + log *log.Logger } func (u *Downstream) moduleError(err error) error { @@ -75,12 +77,12 @@ func (u *Downstream) moduleError(err error) error { }) } -func NewDownstream(modName, instName string) (module.Module, error) { +func New(c *container.C, modName, instName string) (module.Module, error) { return &Downstream{ modName: modName, instName: instName, - lmtp: modName == "target.lmtp" || modName == "lmtp_downstream", /* compatibility with 0.3 configs */ - log: log.Logger{Name: modName}, + lmtp: modName == "target.lmtp", + log: c.DefaultLogger.Sublogger(modName), }, nil } @@ -167,7 +169,7 @@ func (u *Downstream) InstanceName() string { type delivery struct { u *Downstream - log log.Logger + log *log.Logger msgMeta *module.MsgMetadata mailFrom string @@ -344,6 +346,6 @@ func (d *delivery) Commit(ctx context.Context) error { } func init() { - module.Register("target.smtp", NewDownstream) - module.Register("target.lmtp", NewDownstream) + modules.Register("target.smtp", New) + modules.Register("target.lmtp", New) } diff --git a/internal/target/smtp/smtputf8_test.go b/internal/target/smtp/smtputf8_test.go index 1f469e8a..7c8962e0 100644 --- a/internal/target/smtp/smtputf8_test.go +++ b/internal/target/smtp/smtputf8_test.go @@ -22,6 +22,7 @@ import ( "testing" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/internal/testutils" "github.com/stretchr/testify/require" ) @@ -33,7 +34,7 @@ func TestDownstreamDelivery_EHLO_ALabel(t *testing.T) { }() defer testutils.CheckSMTPConnLeak(t, srv) - mod, err := NewDownstream("", "") + mod, err := New(container.New(), "", "") if err != nil { t.Fatal(err) } diff --git a/internal/testutils/check.go b/internal/testutils/check.go index 2c44883d..7c829cd5 100644 --- a/internal/testutils/check.go +++ b/internal/testutils/check.go @@ -24,7 +24,9 @@ import ( "github.com/emersion/go-message/textproto" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) type Check struct { @@ -104,7 +106,7 @@ func (cs *checkState) Close() error { } func init() { - module.Register("test_check", func(_, _ string) (module.Module, error) { + modules.Register("test_check", func(_ *container.C, _, _ string) (module.Module, error) { return &Check{}, nil }) } diff --git a/internal/testutils/logger.go b/internal/testutils/logger.go index 9fd55061..712d53bb 100644 --- a/internal/testutils/logger.go +++ b/internal/testutils/logger.go @@ -33,16 +33,18 @@ var ( directLog = flag.Bool("test.directlog", false, "(maddy) Log to stderr instead of test log") ) -func Logger(t *testing.T, name string) log.Logger { +func Logger(t *testing.T, name string) *log.Logger { if *directLog { - return log.Logger{ - Out: log.WriterOutput(os.Stderr, true), - Name: name, - Debug: *debugLog, + return &log.Logger{ + Parent: &log.DefaultLogger, // silence "no parent" warning + Out: log.WriterOutput(os.Stderr, true), + Name: name, + Debug: *debugLog, } } - return log.Logger{ + return &log.Logger{ + Parent: &log.DefaultLogger, Out: log.FuncOutput(func(_ time.Time, debug bool, str string) { t.Helper() str = strings.TrimSuffix(str, "\n") diff --git a/internal/testutils/modifier.go b/internal/testutils/modifier.go index 1c6f135a..c276b925 100644 --- a/internal/testutils/modifier.go +++ b/internal/testutils/modifier.go @@ -24,7 +24,9 @@ import ( "github.com/emersion/go-message/textproto" "github.com/foxcpp/maddy/framework/buffer" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) type Modifier struct { @@ -115,7 +117,7 @@ func (ms modifierState) Close() error { } func init() { - module.Register("test_modifier", func(_, _ string) (module.Module, error) { + modules.Register("test_modifier", func(_ *container.C, _, _ string) (module.Module, error) { return &Modifier{}, nil }) } diff --git a/internal/tls/acme/acme.go b/internal/tls/acme/acme.go index 0ebcd241..ca171616 100644 --- a/internal/tls/acme/acme.go +++ b/internal/tls/acme/acme.go @@ -9,9 +9,11 @@ import ( "github.com/caddyserver/certmagic" "github.com/foxcpp/maddy/framework/config" modconfig "github.com/foxcpp/maddy/framework/config/module" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/hooks" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) const modName = "tls.loader.acme" @@ -25,13 +27,13 @@ type Loader struct { cfg *certmagic.Config cancelManage context.CancelFunc - log log.Logger + log *log.Logger } -func New(_, instName string) (module.Module, error) { +func New(c *container.C, _, instName string) (module.Module, error) { return &Loader{ instName: instName, - log: log.Logger{Name: modName}, + log: c.DefaultLogger.Sublogger(modName), }, nil } @@ -163,5 +165,5 @@ func init() { func init() { var _ module.TLSLoader = &Loader{} - module.Register(modName, New) + modules.Register(modName, New) } diff --git a/internal/tls/file.go b/internal/tls/file.go index ae260ec6..ab7b90ed 100644 --- a/internal/tls/file.go +++ b/internal/tls/file.go @@ -27,15 +27,17 @@ import ( "time" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) type FileLoader struct { instName string certPaths []string keyPaths []string - log log.Logger + log *log.Logger certs []tls.Certificate certsLock sync.RWMutex @@ -44,10 +46,10 @@ type FileLoader struct { stopTick chan struct{} } -func NewFileLoader(_, instName string) (module.Module, error) { +func NewFileLoader(c *container.C, modName, instName string) (module.Module, error) { return &FileLoader{ instName: instName, - log: log.Logger{Name: "tls.loader.file", Debug: log.DefaultLogger.Debug}, + log: c.DefaultLogger.Sublogger(modName), stopTick: make(chan struct{}), }, nil } @@ -163,5 +165,5 @@ func (f *FileLoader) ConfigureTLS(c *tls.Config) error { func init() { var _ module.TLSLoader = &FileLoader{} - module.Register("tls.loader.file", NewFileLoader) + modules.Register("tls.loader.file", NewFileLoader) } diff --git a/internal/tls/self_signed.go b/internal/tls/self_signed.go index 269b0c0d..1d6c5134 100644 --- a/internal/tls/self_signed.go +++ b/internal/tls/self_signed.go @@ -30,7 +30,9 @@ import ( "time" "github.com/foxcpp/maddy/framework/config" + "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" ) type SelfSignedLoader struct { @@ -40,7 +42,7 @@ type SelfSignedLoader struct { cert tls.Certificate } -func NewSelfSignedLoader(_, instName string) (module.Module, error) { +func NewSelfSignedLoader(_ *container.C, _, instName string) (module.Module, error) { return &SelfSignedLoader{ instName: instName, }, nil @@ -108,5 +110,5 @@ func (f *SelfSignedLoader) ConfigureTLS(c *tls.Config) error { func init() { var _ module.TLSLoader = &SelfSignedLoader{} - module.Register("tls.loader.self_signed", NewSelfSignedLoader) + modules.Register("tls.loader.self_signed", NewSelfSignedLoader) } diff --git a/internal/updatepipe/pubsub/pq.go b/internal/updatepipe/pubsub/pq.go index c084e600..bee74922 100644 --- a/internal/updatepipe/pubsub/pq.go +++ b/internal/updatepipe/pubsub/pq.go @@ -20,12 +20,12 @@ type PqPubSub struct { L *pq.Listener sender *sql.DB - Log log.Logger + Log *log.Logger } func NewPQ(dsn string) (*PqPubSub, error) { l := &PqPubSub{ - Log: log.Logger{Name: "pgpubsub"}, + Log: log.DefaultLogger.Sublogger("pgpubsub"), Notify: make(chan Msg), } l.L = pq.NewListener(dsn, 10*time.Second, time.Minute, l.eventHandler) diff --git a/internal/updatepipe/pubsub_pipe.go b/internal/updatepipe/pubsub_pipe.go index b1cef67f..4341f4d3 100644 --- a/internal/updatepipe/pubsub_pipe.go +++ b/internal/updatepipe/pubsub_pipe.go @@ -13,7 +13,7 @@ import ( type PubSubPipe struct { PubSub pubsub.PubSub - Log log.Logger + Log *log.Logger } func (p *PubSubPipe) Listen(upds chan<- mess.Update) error { diff --git a/internal/updatepipe/unix_pipe.go b/internal/updatepipe/unix_pipe.go index a2042d62..8cd124d8 100644 --- a/internal/updatepipe/unix_pipe.go +++ b/internal/updatepipe/unix_pipe.go @@ -45,7 +45,7 @@ import ( // is initialized on the first call to Listen or (Init)Push. type UnixSockPipe struct { SockPath string - Log log.Logger + Log *log.Logger listener net.Listener sender net.Conn diff --git a/maddy.go b/maddy.go index 5c9381d2..c3a560bc 100644 --- a/maddy.go +++ b/maddy.go @@ -36,7 +36,7 @@ import ( "github.com/foxcpp/maddy/framework/container" "github.com/foxcpp/maddy/framework/hooks" "github.com/foxcpp/maddy/framework/log" - "github.com/foxcpp/maddy/framework/module" + "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/framework/resource/netresource" "github.com/foxcpp/maddy/internal/authz" maddycli "github.com/foxcpp/maddy/internal/cli" @@ -208,11 +208,11 @@ func Run(c *cli.Context) error { } hooks.AddHook(hooks.EventLogRotate, reinitLogging) - defer func() { - if err := log.DefaultLogger.Out.Close(); err != nil { + defer func(out log.Output) { + if err := out.Close(); err != nil { log.Println("failed to close default logger output:", err) } - }() + }(log.DefaultLogger.Out) defer hooks.RunHooks(hooks.EventShutdown) defer func() { @@ -239,7 +239,7 @@ func VerifyConfig(c *cli.Context) error { return cli.Exit(err.Error(), 2) } - log.DefaultLogger.Msg("No errors detected") + _, _ = fmt.Fprintln(os.Stderr, "No errors detected") return nil } @@ -337,7 +337,7 @@ func ReadGlobals(c *container.C, cfg []config.Node) (map[string]interface{}, []c globals.Bool("auth_perdomain", false, false, nil) globals.StringList("auth_domains", false, false, nil, nil) globals.Custom("log", false, false, defaultLogOutput, logOutput, &c.DefaultLogger.Out) - globals.Bool("debug", false, log.DefaultLogger.Debug, &c.DefaultLogger.Debug) + globals.Bool("debug", false, false, &c.DefaultLogger.Debug) config.EnumMapped(globals, "auth_map_normalize", true, false, authz.NormalizeFuncs, authz.NormalizeAuto, nil) modconfig.Table(globals, "auth_map", true, false, nil, nil) globals.AllowUnknown() @@ -373,6 +373,11 @@ func moduleConfigure(configPath string) (*container.C, error) { return nil, err } + // ReadGlobals will configure c.DefaultLogger. + if c.DefaultLogger.Out != nil { + log.DefaultLogger.Out = c.DefaultLogger.Out + } + if err := InitDirs(c); err != nil { return nil, err } @@ -397,7 +402,7 @@ func moduleStart(c *container.C) error { func moduleStop(c *container.C, earlyStop bool) error { if earlyStop { if err := c.Lifetime.EarlyStopAll(); err != nil { - log.DefaultLogger.Error("early stop failed", err) + c.DefaultLogger.Error("early stop failed", err) } } @@ -444,6 +449,12 @@ func moduleMain(configPath string) error { } c.DefaultLogger.Msg("server stopped") + if c.DefaultLogger.Out != nil { + if err := c.DefaultLogger.Out.Close(); err != nil { + log.DefaultLogger.Error("failed to close output logger", err) + } + } + return nil } @@ -451,26 +462,40 @@ func moduleReload(oldContainer *container.C, configPath string, asyncStopWg *syn oldContainer.DefaultLogger.Msg("reloading server...") systemdStatus(SDReloading, "Reloading server...") + rollbackReload := func() { + // Restore DefaultLogger config that might be set by moduleConfig + log.DefaultLogger.Out = oldContainer.DefaultLogger.Out + } + oldContainer.DefaultLogger.Msg("loading new configuration...") newContainer, err := moduleConfigure(configPath) if err != nil { + rollbackReload() oldContainer.DefaultLogger.Error("failed to load new configuration", err) + return oldContainer } oldContainer.DefaultLogger.Msg("configuration loaded") + rollbackReload = func() { + // Restore DefaultLogger config that might be set by moduleConfig + log.DefaultLogger.Out = oldContainer.DefaultLogger.Out + container.Global = oldContainer + } if err := oldContainer.Lifetime.EarlyStopAll(); err != nil { + rollbackReload() oldContainer.DefaultLogger.Error("failed to early-stop old server", err) - container.Global = oldContainer + return oldContainer } netresource.ResetListenersUsage() oldContainer.DefaultLogger.Msg("starting new server") if err := moduleStart(newContainer); err != nil { + rollbackReload() oldContainer.DefaultLogger.Error("failed to start new server", err) - container.Global = oldContainer + return oldContainer } @@ -492,6 +517,9 @@ func moduleReload(oldContainer *container.C, configPath string, asyncStopWg *syn oldContainer.DefaultLogger.Error("moduleStop failed", err) } oldContainer.DefaultLogger.Msg("old server stopped") + if err := oldContainer.DefaultLogger.Out.Close(); err != nil { + newContainer.DefaultLogger.Error("failed to close old server log", err) + } systemdStatus(SDReloading, "Configuration running.") }() @@ -501,7 +529,7 @@ func moduleReload(oldContainer *container.C, configPath string, asyncStopWg *syn func RegisterModules(c *container.C, globals map[string]interface{}, nodes []config.Node) (err error) { var endpoints []struct { - Endpoint module.LifetimeModule + Endpoint container.LifetimeModule Cfg *config.Map } @@ -517,26 +545,26 @@ func RegisterModules(c *container.C, globals map[string]interface{}, nodes []con modName := block.Name - endpFactory := module.GetEndpoint(modName) + endpFactory := modules.GetEndpoint(modName) if endpFactory != nil { - inst, err := endpFactory(modName, block.Args) + inst, err := endpFactory(c, modName, block.Args) if err != nil { return err } endpoints = append(endpoints, struct { - Endpoint module.LifetimeModule + Endpoint container.LifetimeModule Cfg *config.Map }{Endpoint: inst, Cfg: config.NewMap(globals, block)}) continue } - factory := module.Get(modName) + factory := modules.Get(modName) if factory == nil { return config.NodeErr(block, "unknown module or global directive: %s", modName) } - inst, err := factory(modName, instName) + inst, err := factory(c, modName, instName) if err != nil { return err } @@ -547,13 +575,13 @@ func RegisterModules(c *container.C, globals map[string]interface{}, nodes []con return err } - if lt, ok := inst.(module.LifetimeModule); ok { + if lt, ok := inst.(container.LifetimeModule); ok { c.Lifetime.Add(lt) } return nil }) if err != nil { - if errors.Is(err, module.ErrInstanceNameDuplicate) { + if errors.Is(err, container.ErrInstanceNameDuplicate) { return config.NodeErr(block, "config block named %s already exists", inst.InstanceName()) } return err @@ -561,7 +589,7 @@ func RegisterModules(c *container.C, globals map[string]interface{}, nodes []con for _, alias := range modAliases { if err := c.Modules.AddAlias(instName, alias); err != nil { - if errors.Is(err, module.ErrInstanceNameDuplicate) { + if errors.Is(err, container.ErrInstanceNameDuplicate) { return config.NodeErr(block, "config block named %s already exists", alias) } return err From b5bd761afd3f7324c16051277772faf24c955869 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Fri, 1 May 2026 01:01:45 +0300 Subject: [PATCH 162/171] cli: Remove old-style maddyctl compatibility (pre-0.6 binary split) --- internal/cli/app.go | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/internal/cli/app.go b/internal/cli/app.go index 55ab1862..2e6011e6 100644 --- a/internal/cli/app.go +++ b/internal/cli/app.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "os" - "strings" "github.com/foxcpp/maddy/framework/log" "github.com/urfave/cli/v2" @@ -81,17 +80,6 @@ func AddGlobalFlag(f cli.Flag) { func AddSubcommand(cmd *cli.Command) { app.Commands = append(app.Commands, cmd) - - if cmd.Name == "run" { - // Backward compatibility hack to start the server as just ./maddy - // Needs to be done here so we will register all known flags with - // stdlib before Run is called. - app.Action = func(c *cli.Context) error { - log.Println("WARNING: Starting server not via 'maddy run' is deprecated and will stop working in the next version") - return cmd.Action(c) - } - app.Flags = append(app.Flags, cmd.Flags...) - } } // RunWithoutExit is like Run but returns exit code instead of calling os.Exit @@ -114,15 +102,6 @@ func Run() { // Actual entry point is registered in maddy.go. - // Print help when called via maddyctl executable. To be removed - // once backward compatibility hack for 'maddy run' is removed too. - if strings.Contains(os.Args[0], "maddyctl") && len(os.Args) == 1 { - if err := app.Run([]string{os.Args[0], "help"}); err != nil { - log.DefaultLogger.Error("app.Run failed", err) - } - return - } - if err := app.Run(os.Args); err != nil { log.DefaultLogger.Error("app.Run failed", err) } From 6885f8531969b81e17330c32d8a7453b669a00c6 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Fri, 1 May 2026 01:03:30 +0300 Subject: [PATCH 163/171] Fix formatting issues, linter warnings and test errors after logger refactor --- framework/dns/dnssec_test.go | 6 +++++- internal/table/file_test.go | 2 +- internal/table/sql_query_test.go | 2 +- internal/target/remote/remote.go | 4 ++-- internal/target/remote/remote_test.go | 4 ++-- tests/t.go | 2 +- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/framework/dns/dnssec_test.go b/framework/dns/dnssec_test.go index 325b14fe..8a6de426 100644 --- a/framework/dns/dnssec_test.go +++ b/framework/dns/dnssec_test.go @@ -129,7 +129,11 @@ func TestExtResolver_AuthLookupIPAddr(t *testing.T) { // AD flag handling for use in DANE algorithms. // Silence log messages about disregarded I/O errors. - log.DefaultLogger.Out = nil + oldLog := log.DefaultLogger + log.DefaultLogger = log.NopLogger + t.Cleanup(func() { + log.DefaultLogger = oldLog + }) test := func(aAct, aaaaAct TestSrvAction, aAD, aaaaAD, ad bool, addrs []net.IP, err bool) { t.Helper() diff --git a/internal/table/file_test.go b/internal/table/file_test.go index 33cabe05..20335eb1 100644 --- a/internal/table/file_test.go +++ b/internal/table/file_test.go @@ -119,7 +119,7 @@ func TestFileReload(t *testing.T) { t.Fatal(err) } - mod, err := NewFile(container.New(),"", "") + mod, err := NewFile(container.New(), "", "") if err != nil { t.Fatal(err) } diff --git a/internal/table/sql_query_test.go b/internal/table/sql_query_test.go index ac976f6c..a81f1bfe 100644 --- a/internal/table/sql_query_test.go +++ b/internal/table/sql_query_test.go @@ -34,7 +34,7 @@ import ( func TestSQL(t *testing.T) { path := testutils.Dir(t) - mod, err := NewSQL(container.New(),"sql_table", "") + mod, err := NewSQL(container.New(), "sql_table", "") if err != nil { t.Fatal("Module create failed:", err) } diff --git a/internal/target/remote/remote.go b/internal/target/remote/remote.go index 281b7ce6..93b26573 100644 --- a/internal/target/remote/remote.go +++ b/internal/target/remote/remote.go @@ -208,8 +208,8 @@ func (rt *Target) InstanceName() string { type remoteDelivery struct { rt *Target mailFrom string - msgMeta *module.MsgMetadata - log *log.Logger + msgMeta *module.MsgMetadata + log *log.Logger recipients []string connections map[string]*mxConn diff --git a/internal/target/remote/remote_test.go b/internal/target/remote/remote_test.go index 73bc0231..ca4fd656 100644 --- a/internal/target/remote/remote_test.go +++ b/internal/target/remote/remote_test.go @@ -75,7 +75,7 @@ func testTarget(t *testing.T, zones map[string]mockdns.Zone, extResolver *dns.Ex } func testSTSPolicy(t *testing.T, zones map[string]mockdns.Zone, mtastsGet func(context.Context, string) (*mtasts.Policy, error)) *mtastsPolicy { - m, err := NewMTASTSPolicy(container.New(),"mx_auth.mtasts", "test") + m, err := NewMTASTSPolicy(container.New(), "mx_auth.mtasts", "test") if err != nil { t.Fatal(err) } @@ -101,7 +101,7 @@ func testSTSPolicy(t *testing.T, zones map[string]mockdns.Zone, mtastsGet func(c } func testDANEPolicy(t *testing.T, extR *dns.ExtResolver) *danePolicy { - m, err := NewDANEPolicy(container.New(),"mx_auth.dane", "test") + m, err := NewDANEPolicy(container.New(), "mx_auth.dane", "test") if err != nil { t.Fatal(err) } diff --git a/tests/t.go b/tests/t.go index 58d021d0..a2795e7b 100644 --- a/tests/t.go +++ b/tests/t.go @@ -217,7 +217,7 @@ func (t *T) buildCmd(additionalArgs ...string) *exec.Cmd { args := []string{"-config", filepath.Join(t.testDir, "maddy.conf"), "-debug.smtpport", remoteSmtp, "-debug.dnsoverride", t.dnsServ.LocalAddr().String(), - "-log", "/tmp/test.log"} + } if CoverageOut != "" { args = append(args, "-test.coverprofile", CoverageOut+"."+strconv.FormatInt(time.Now().UnixNano(), 16)) From 837b1b8a7761eaaa9ae2b3ff8c9b3a4495e9821e Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: Fri, 1 May 2026 01:15:36 +0300 Subject: [PATCH 164/171] maddy 0.9.4 --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index 965065db..a602fc9e 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.9.3 +0.9.4 From 1c1a01ae4b7ec6bd3a43f48fdfa00884db92b8e4 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年5月16日 01:24:05 +0300 Subject: [PATCH 165/171] msgpipeline: Ensure logger is always initialized for nested pipelines --- internal/msgpipeline/msgpipeline.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/msgpipeline/msgpipeline.go b/internal/msgpipeline/msgpipeline.go index 8fc9c54d..6025bf06 100644 --- a/internal/msgpipeline/msgpipeline.go +++ b/internal/msgpipeline/msgpipeline.go @@ -90,6 +90,7 @@ func New(globals map[string]interface{}, cfg []config.Node) (*MsgPipeline, error return &MsgPipeline{ msgpipelineCfg: parsedCfg, Resolver: dns.DefaultResolver(), + Log: log.DefaultLogger.Sublogger("msgpipeline"), }, err } From fd6d880141d880251fba84eeb0a171648506393a Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年5月16日 02:06:40 +0300 Subject: [PATCH 166/171] target/remote: Fix dangling destination limiter on first RCPT error, add debug logs Might fix #842. --- internal/endpoint/smtp/smtp.go | 2 +- internal/limits/limits.go | 37 ++++++++++++++++++++++++--- internal/smtpconn/smtpconn.go | 2 +- internal/target/remote/connect.go | 6 +++++ internal/target/remote/remote.go | 3 ++- internal/target/remote/remote_test.go | 2 +- 6 files changed, 44 insertions(+), 8 deletions(-) diff --git a/internal/endpoint/smtp/smtp.go b/internal/endpoint/smtp/smtp.go index eb334556..acfcca3b 100644 --- a/internal/endpoint/smtp/smtp.go +++ b/internal/endpoint/smtp/smtp.go @@ -274,7 +274,7 @@ func (endp *Endpoint) setConfig(cfg *config.Map) error { cfg.Bool("defer_sender_reject", false, true, &endp.deferServerReject) cfg.Int("max_logged_rcpt_errors", false, false, 5, &endp.maxLoggedRcptErrors) cfg.Custom("limits", false, false, func() (interface{}, error) { - return &limits.Group{}, nil + return limits.Empty(endp.log.Sublogger("limits")), nil }, func(cfg *config.Map, n config.Node) (interface{}, error) { var g *limits.Group if err := modconfig.GroupFromNode("limits", n.Args, n, cfg.Globals, &g); err != nil { diff --git a/internal/limits/limits.go b/internal/limits/limits.go index eba335d4..66086f7a 100644 --- a/internal/limits/limits.go +++ b/internal/limits/limits.go @@ -28,18 +28,21 @@ package limits import ( "context" + "fmt" "net" "strconv" "time" "github.com/foxcpp/maddy/framework/config" "github.com/foxcpp/maddy/framework/container" + "github.com/foxcpp/maddy/framework/log" "github.com/foxcpp/maddy/framework/module" "github.com/foxcpp/maddy/framework/module/modules" "github.com/foxcpp/maddy/internal/limits/limiters" ) type Group struct { + log *log.Logger instName string global limiters.MultiLimit @@ -48,8 +51,15 @@ type Group struct { dest *limiters.BucketSet // BucketSet of MultiLimit } +func Empty(log *log.Logger) *Group { + return &Group{ + log: log, + } +} + func New(c *container.C, _, instName string) (module.Module, error) { return &Group{ + log: c.DefaultLogger.Sublogger("limits"), instName: instName, }, nil } @@ -177,22 +187,28 @@ func (g *Group) TakeMsg(ctx context.Context, addr net.IP, sourceDomain string) e ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() + g.log.DebugMsg("global TakeContext") if err := g.global.TakeContext(ctx); err != nil { - return err + return fmt.Errorf("TakeMsg: global: %w", err) } + g.log.DebugMsg("global TakeContext done") if g.ip != nil { + g.log.DebugMsg("ip TakeContext", "ip", addr.String()) if err := g.ip.TakeContext(ctx, addr.String()); err != nil { g.global.Release() - return err + return fmt.Errorf("TakeMsg: ip: %w", err) } + g.log.DebugMsg("ip TakeContext done", "ip", addr.String()) } if g.source != nil { + g.log.DebugMsg("source TakeContext", "domain", sourceDomain) if err := g.source.TakeContext(ctx, sourceDomain); err != nil { g.global.Release() g.ip.Release(addr.String()) - return err + return fmt.Errorf("TakeMSg: source: %w", err) } + g.log.DebugMsg("source TakeContext done", "domain", sourceDomain) } return nil } @@ -201,17 +217,27 @@ func (g *Group) TakeDest(ctx context.Context, domain string) error { if g.dest == nil { return nil } + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() - return g.dest.TakeContext(ctx, domain) + + g.log.DebugMsg("TakeDest", "domain", domain) + if err := g.dest.TakeContext(ctx, domain); err != nil { + return fmt.Errorf("TakeDest: dest: %w", err) + } + g.log.DebugMsg("TakeDest done", "domain", domain) + return nil } func (g *Group) ReleaseMsg(addr net.IP, sourceDomain string) { + g.log.DebugMsg("global ReleaseMsg") g.global.Release() if g.ip != nil { + g.log.DebugMsg("ip ReleaseMsg", "ip", addr.String()) g.ip.Release(addr.String()) } if g.source != nil { + g.log.DebugMsg("source ReleaseMsg", "domain", sourceDomain) g.source.Release(sourceDomain) } } @@ -220,7 +246,10 @@ func (g *Group) ReleaseDest(domain string) { if g.dest == nil { return } + + g.log.DebugMsg("ReleaseDest", "domain", domain) g.dest.Release(domain) + g.log.DebugMsg("ReleaseDest done", "domain", domain) } func (g *Group) Name() string { diff --git a/internal/smtpconn/smtpconn.go b/internal/smtpconn/smtpconn.go index 22398937..7ec93cac 100644 --- a/internal/smtpconn/smtpconn.go +++ b/internal/smtpconn/smtpconn.go @@ -239,7 +239,7 @@ func (c *C) attemptConnect(ctx context.Context, lmtp bool, endp config.Endpoint, conn, err = c.Dialer(dialCtx, endp.Network(), endp.Address()) cancel() if err != nil { - return false, nil, nil, err + return false, nil, nil, fmt.Errorf("dialer: %w", err) } if endp.IsTLS() { diff --git a/internal/target/remote/connect.go b/internal/target/remote/connect.go index 4456d927..3b9692d0 100644 --- a/internal/target/remote/connect.go +++ b/internal/target/remote/connect.go @@ -45,6 +45,7 @@ type mxConn struct { errored bool reuseLimit int + takeDest bool // Amount of times connection was used for an SMTP transaction. transactions int @@ -207,6 +208,10 @@ func (rd *remoteDelivery) attemptMX(ctx context.Context, conn *mxConn, record *n } func (rd *remoteDelivery) closeConn(c *mxConn) { + if c.takeDest { + rd.rt.limits.ReleaseDest(c.domain) + } + if err := c.Close(); err != nil { rd.log.Error("client connection close failed", err) } @@ -270,6 +275,7 @@ func (rd *remoteDelivery) connectionForDomain(ctx context.Context, domain string return nil, err } region.End() + conn.takeDest = true // Relaxed REQUIRETLS mode is not conforming to the specification strictly // but allows to start deploying client support for REQUIRETLS without the diff --git a/internal/target/remote/remote.go b/internal/target/remote/remote.go index 93b26573..daf5a874 100644 --- a/internal/target/remote/remote.go +++ b/internal/target/remote/remote.go @@ -130,7 +130,7 @@ func (rt *Target) Configure(inlineArgs []string, cfg *config.Map) error { return p.L, nil }, &rt.policies) cfg.Custom("limits", false, false, func() (interface{}, error) { - return &limits.Group{}, nil + return limits.Empty(rt.log.Sublogger("limits")), nil }, func(cfg *config.Map, n config.Node) (interface{}, error) { var g *limits.Group if err := modconfig.GroupFromNode("limits", n.Args, n, cfg.Globals, &g); err != nil { @@ -455,6 +455,7 @@ func (rd *remoteDelivery) Commit(ctx context.Context) error { func (rd *remoteDelivery) Close() error { for _, conn := range rd.connections { rd.rt.limits.ReleaseDest(conn.domain) + conn.takeDest = false conn.transactions++ if !conn.Usable() { diff --git a/internal/target/remote/remote_test.go b/internal/target/remote/remote_test.go index ca4fd656..06f0cb80 100644 --- a/internal/target/remote/remote_test.go +++ b/internal/target/remote/remote_test.go @@ -62,7 +62,7 @@ func testTarget(t *testing.T, zones map[string]mockdns.Zone, extResolver *dns.Ex tlsConfig: &tls.Config{}, log: testutils.Logger(t, "remote"), policies: extraPolicies, - limits: &limits.Group{}, + limits: limits.Empty(testutils.Logger(t, "limits")), pool: pool.New(pool.Config{ MaxKeys: 5000, MaxConnsPerKey: 5, // basically, max. amount of idle connections in cache From e42741c7577746a474a45a818d554d578f4f853d Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年5月16日 15:59:40 +0300 Subject: [PATCH 167/171] ci: Re-enable arm64 docker builds --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e7155b68..6ec83b75 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -144,7 +144,7 @@ jobs: id: docker with: context: . - platforms: linux/amd64 #,linux/arm64 Temporary disabled due to SIGSEGV in gcc. + platforms: linux/amd64,linux/arm64 file: Dockerfile push: true tags: ${{ steps.meta.outputs.tags }} From 9c78f96902f931383dda12e6f8fdeade2262a010 Mon Sep 17 00:00:00 2001 From: oidq Date: 2026年5月20日 18:42:10 +0200 Subject: [PATCH 168/171] fix: check for nil Out in Logger on Close() * add Logger.Close(), which safely closes underlying Out * should fix #846 --- framework/log/log.go | 9 +++++++++ maddy.go | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/framework/log/log.go b/framework/log/log.go index 3ad291a6..263732a0 100644 --- a/framework/log/log.go +++ b/framework/log/log.go @@ -205,6 +205,15 @@ func (l *Logger) Write(s []byte) (int, error) { return len(s), nil } +// Close closes underlying output in Out. +func (l *Logger) Close() error { + if l.Out == nil { + return nil + } + + return l.Out.Close() +} + // DebugWriter returns a writer that will act like Logger.Write // but will use debug flag on messages. If Logger.Debug is false, // Write method of returned object will be no-op. diff --git a/maddy.go b/maddy.go index c3a560bc..4d7a8179 100644 --- a/maddy.go +++ b/maddy.go @@ -450,7 +450,7 @@ func moduleMain(configPath string) error { c.DefaultLogger.Msg("server stopped") if c.DefaultLogger.Out != nil { - if err := c.DefaultLogger.Out.Close(); err != nil { + if err := c.DefaultLogger.Close(); err != nil { log.DefaultLogger.Error("failed to close output logger", err) } } @@ -517,7 +517,7 @@ func moduleReload(oldContainer *container.C, configPath string, asyncStopWg *syn oldContainer.DefaultLogger.Error("moduleStop failed", err) } oldContainer.DefaultLogger.Msg("old server stopped") - if err := oldContainer.DefaultLogger.Out.Close(); err != nil { + if err := oldContainer.DefaultLogger.Close(); err != nil { newContainer.DefaultLogger.Error("failed to close old server log", err) } From 6200c517c3b3ce95c5f2ba523fc9b911dbc2af51 Mon Sep 17 00:00:00 2001 From: oidq Date: 2026年5月20日 18:48:47 +0200 Subject: [PATCH 169/171] fix(systemd): report READY=1 after reload SystemD would report maddy in "reloading (reload-notify)" state even after successful reload. It should send "READY=1" after finishing the reload. --- maddy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maddy.go b/maddy.go index c3a560bc..952e8354 100644 --- a/maddy.go +++ b/maddy.go @@ -521,7 +521,7 @@ func moduleReload(oldContainer *container.C, configPath string, asyncStopWg *syn newContainer.DefaultLogger.Error("failed to close old server log", err) } - systemdStatus(SDReloading, "Configuration running.") + systemdStatus(SDReady, "Configuration running.") }() return newContainer From 58e8a11423e140ad37063ce8dfc446de4c1591ed Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年5月24日 01:17:29 +0300 Subject: [PATCH 170/171] maddy 0.9.5 --- .version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.version b/.version index a602fc9e..b0bb8785 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -0.9.4 +0.9.5 From 6bfec6cc7240fec7807829ab34e72f40d8411f19 Mon Sep 17 00:00:00 2001 From: "fox.cpp" Date: 2026年7月13日 22:55:48 +0300 Subject: [PATCH 171/171] log: Fix logger name duplication in zap adapter --- framework/log/log.go | 15 +++++++++++++++ framework/log/zap.go | 9 +++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/framework/log/log.go b/framework/log/log.go index 263732a0..f17a6a0e 100644 --- a/framework/log/log.go +++ b/framework/log/log.go @@ -251,6 +251,21 @@ func (l *Logger) log(debug bool, s string) { // Logging is disabled - do nothing. } +func (l *Logger) logNameOverwrite(loggerName string, debug bool, s string) { + if loggerName == "" { + loggerName = l.Name + } + if loggerName != "" { + s = loggerName + ": " + s + } + + out := l.output() + out.Write(time.Now(), debug, s) + + // Logging is disabled - do nothing. +} + + func (l *Logger) Sublogger(name string) *Logger { if l.Name != "" && name != "" { name = l.Name + "/" + name diff --git a/framework/log/zap.go b/framework/log/zap.go index 893bf484..4dff1510 100644 --- a/framework/log/zap.go +++ b/framework/log/zap.go @@ -46,9 +46,14 @@ func (l zapLogger) Write(entry zapcore.Entry, fields []zapcore.Field) error { f.AddTo(enc) } if entry.LoggerName != "" { - l.L.Name += "/" + entry.LoggerName + l.L.logNameOverwrite( + l.L.Name+"/"+entry.LoggerName, + entry.Level == zapcore.DebugLevel, + l.L.formatMsg(entry.Message, enc.Fields), + ) + } else { + l.L.log(entry.Level == zapcore.DebugLevel, l.L.formatMsg(entry.Message, enc.Fields)) } - l.L.log(entry.Level == zapcore.DebugLevel, l.L.formatMsg(entry.Message, enc.Fields)) return nil }

AltStyle によって変換されたページ (->オリジナル) /