diff --git a/imapserver/server.go b/imapserver/server.go index 7012549..9d75742 100644 --- a/imapserver/server.go +++ b/imapserver/server.go @@ -52,7 +52,6 @@ import ( "path" "path/filepath" "regexp" - "reflect" "runtime/debug" "slices" "sort" @@ -171,6 +170,7 @@ type conn struct { state state conn net.Conn tls bool // Whether TLS has been initialized. + viaHTTPS bool // Whether this connection came in via HTTPS (using TLS ALPN). br *bufio.Reader // From remote, with TLS unwrapped in case of TLS. line chan lineErr // If set, instead of reading from br, a line is read from this channel. For reading a line in IDLE while also waiting for mailbox/account updates. lastLine string // For detecting if syntax error is fatal, i.e. if this ends with a literal. Without crlf. @@ -347,8 +347,9 @@ func Listen() http.FnALPNHelper { } if listener.IMAPS.EnableOnHTTPS && alpnHelper == nil { alpnHelper = func(tc *tls.Config, conn net.Conn) { + protocol = protocol + "https" metricIMAPConnection.WithLabelValues(protocol).Inc() - serve(name, mox.Cid(), tc, conn, true, false) + serve(name, mox.Cid(), tc, conn, true, false, true) } } } @@ -391,7 +392,7 @@ func listen1(protocol, listenerName, ip string, port int, tlsConfig *tls.Config, } metricIMAPConnection.WithLabelValues(protocol).Inc() - go serve(listenerName, mox.Cid(), tlsConfig, conn, xtls, noRequireSTARTTLS) + go serve(listenerName, mox.Cid(), tlsConfig, conn, xtls, noRequireSTARTTLS, false) } } @@ -646,7 +647,7 @@ func (c *conn) xhighestModSeq(tx *bstore.Tx, mailboxID int64) store.ModSeq { var cleanClose struct{} // Sentinel value for panic/recover indicating clean close of connection. -func serve(listenerName string, cid int64, tlsConfig *tls.Config, nc net.Conn, xtls, noRequireSTARTTLS bool) { +func serve(listenerName string, cid int64, tlsConfig *tls.Config, nc net.Conn, xtls, noRequireSTARTTLS, viaHTTPS bool) { var remoteIP net.IP if a, ok := nc.RemoteAddr().(*net.TCPAddr); ok { remoteIP = a.IP @@ -659,6 +660,7 @@ func serve(listenerName string, cid int64, tlsConfig *tls.Config, nc net.Conn, x cid: cid, conn: nc, tls: xtls, + viaHTTPS: viaHTTPS, lastlog: time.Now(), baseTLSConfig: tlsConfig, remoteIP: remoteIP, @@ -728,8 +730,7 @@ func serve(listenerName string, cid int64, tlsConfig *tls.Config, nc net.Conn, x } }() - isAlreadyTLS := reflect.TypeOf(nc) == reflect.TypeFor[*tls.Conn]() - if xtls && !isAlreadyTLS { + if xtls && !viaHTTPS { // Start TLS on connection. We perform the handshake explicitly, so we can set a // timeout, do client certificate authentication, log TLS details afterwards. c.xtlsHandshakeAndAuthenticate(c.conn) diff --git a/smtpserver/server.go b/smtpserver/server.go index 035aad3..96020a4 100644 --- a/smtpserver/server.go +++ b/smtpserver/server.go @@ -23,7 +23,6 @@ import ( "net" "net/textproto" "os" - "reflect" "runtime/debug" "slices" "sort" @@ -266,7 +265,7 @@ func Listen() http.FnALPNHelper { alpnHelper = func(tc *tls.Config, conn net.Conn) { log := mlog.New("smtpserver", nil) resolver := dns.StrictResolver{Log: log.Logger} - serve(name, mox.Cid(), hostname, tc, conn, resolver, true, true, maxMsgSize, true, true, true, nil, 0) + serve(name, mox.Cid(), hostname, tc, conn, resolver, true, true, true, maxMsgSize, true, true, true, nil, 0) } } } @@ -310,7 +309,7 @@ func listen1(protocol, name, ip string, port int, hostname dns.Domain, tlsConfig // Package is set on the resolver by the dkim/spf/dmarc/etc packages. resolver := dns.StrictResolver{Log: log.Logger} - go serve(name, mox.Cid(), hostname, tlsConfig, conn, resolver, submission, xtls, maxMessageSize, requireTLSForAuth, requireTLSForDelivery, requireTLS, dnsBLs, firstTimeSenderDelay) + go serve(name, mox.Cid(), hostname, tlsConfig, conn, resolver, submission, xtls, false, maxMessageSize, requireTLSForAuth, requireTLSForDelivery, requireTLS, dnsBLs, firstTimeSenderDelay) } } @@ -336,6 +335,7 @@ type conn struct { tls bool extRequireTLS bool // Whether to announce and allow the REQUIRETLS extension. + viaHTTPS bool // Whether the connection came in via the HTTPS port (using TLS ALPN). resolver dns.Resolver r *bufio.Reader w *bufio.Writer @@ -810,7 +810,7 @@ func (c *conn) writelinef(format string, args ...any) { var cleanClose struct{} // Sentinel value for panic/recover indicating clean close of connection. -func serve(listenerName string, cid int64, hostname dns.Domain, tlsConfig *tls.Config, nc net.Conn, resolver dns.Resolver, submission, xtls bool, maxMessageSize int64, requireTLSForAuth, requireTLSForDelivery, requireTLS bool, dnsBLs []dns.Domain, firstTimeSenderDelay time.Duration) { +func serve(listenerName string, cid int64, hostname dns.Domain, tlsConfig *tls.Config, nc net.Conn, resolver dns.Resolver, submission, xtls, viaHTTPS bool, maxMessageSize int64, requireTLSForAuth, requireTLSForDelivery, requireTLS bool, dnsBLs []dns.Domain, firstTimeSenderDelay time.Duration) { var localIP, remoteIP net.IP if a, ok := nc.LocalAddr().(*net.TCPAddr); ok { localIP = a.IP @@ -831,6 +831,7 @@ func serve(listenerName string, cid int64, hostname dns.Domain, tlsConfig *tls.C conn: nc, submission: submission, tls: xtls, + viaHTTPS: viaHTTPS, extRequireTLS: requireTLS, resolver: resolver, lastlog: time.Now(), @@ -894,8 +895,7 @@ func serve(listenerName string, cid int64, hostname dns.Domain, tlsConfig *tls.C } }() - isAlreadyTLS := reflect.TypeOf(nc) == reflect.TypeFor[*tls.Conn]() - if xtls && !isAlreadyTLS { + if xtls && !viaHTTPS { // Start TLS on connection. We perform the handshake explicitly, so we can set a // timeout, do client certificate authentication, log TLS details afterwards. c.xtlsHandshakeAndAuthenticate(c.conn) @@ -1046,10 +1046,14 @@ func command(c *conn) { // For use in metric labels. func (c *conn) kind() string { + k := "smtp" if c.submission { - return "submission" + k = "submission" } - return "smtp" + if c.viaHTTPS { + k = k + "https" + } + return k } func (c *conn) xneedHello() {