mirror of
https://github.com/mjl-/mox.git
synced 2025-07-14 04:54:39 +03:00
expose fewer internals in packages, for easier software reuse
- prometheus is now behind an interface, they aren't dependencies for the reusable components anymore. - some dependencies have been inverted: instead of packages importing a main package to get configuration, the main package now sets configuration in these packages. that means fewer internals are pulled in. - some functions now have new parameters for values that were retrieved from package "mox-".
This commit is contained in:
@ -18,46 +18,24 @@ import (
|
||||
|
||||
"golang.org/x/exp/slog"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||
|
||||
"github.com/mjl-/adns"
|
||||
|
||||
"github.com/mjl-/mox/dane"
|
||||
"github.com/mjl-/mox/dns"
|
||||
"github.com/mjl-/mox/metrics"
|
||||
"github.com/mjl-/mox/mlog"
|
||||
"github.com/mjl-/mox/mox-"
|
||||
"github.com/mjl-/mox/moxio"
|
||||
"github.com/mjl-/mox/sasl"
|
||||
"github.com/mjl-/mox/smtp"
|
||||
"github.com/mjl-/mox/stub"
|
||||
"github.com/mjl-/mox/tlsrpt"
|
||||
)
|
||||
|
||||
// todo future: add function to deliver message to multiple recipients. requires more elaborate return value, indicating success per message: some recipients may succeed, others may fail, and we should still deliver. to prevent backscatter, we also sometimes don't allow multiple recipients. ../rfc/5321:1144
|
||||
|
||||
var (
|
||||
metricCommands = promauto.NewHistogramVec(
|
||||
prometheus.HistogramOpts{
|
||||
Name: "mox_smtpclient_command_duration_seconds",
|
||||
Help: "SMTP client command duration and result codes in seconds.",
|
||||
Buckets: []float64{0.001, 0.005, 0.01, 0.05, 0.100, 0.5, 1, 5, 10, 20, 30, 60, 120},
|
||||
},
|
||||
[]string{
|
||||
"cmd",
|
||||
"code",
|
||||
"secode",
|
||||
},
|
||||
)
|
||||
metricTLSRequiredNoIgnored = promauto.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Name: "mox_smtpclient_tlsrequiredno_ignored_total",
|
||||
Help: "Connection attempts with TLS policy findings ignored due to message with TLS-Required: No header. Does not cover case where TLS certificate cannot be PKIX-verified.",
|
||||
},
|
||||
[]string{
|
||||
"ignored", // daneverification (no matching tlsa record)
|
||||
},
|
||||
)
|
||||
MetricCommands stub.HistogramVec = stub.HistogramVecIgnore{}
|
||||
MetricTLSRequiredNoIgnored stub.CounterVec = stub.CounterVecIgnore{}
|
||||
MetricPanicInc = func() {}
|
||||
)
|
||||
|
||||
var (
|
||||
@ -281,7 +259,7 @@ func New(ctx context.Context, elog *slog.Logger, conn net.Conn, tlsMode TLSMode,
|
||||
c.firstReadAfterHandshake = true
|
||||
c.tlsResultAdd(1, 0, nil)
|
||||
c.conn = tlsconn
|
||||
tlsversion, ciphersuite := mox.TLSInfo(tlsconn)
|
||||
tlsversion, ciphersuite := moxio.TLSInfo(tlsconn)
|
||||
c.log.Debug("tls client handshake done",
|
||||
slog.String("tls", tlsversion),
|
||||
slog.String("ciphersuite", ciphersuite),
|
||||
@ -334,7 +312,7 @@ func (c *Client) tlsConfig() *tls.Config {
|
||||
// DANE verification.
|
||||
// daneRecords can be non-nil and empty, that's intended.
|
||||
if c.daneRecords != nil {
|
||||
verified, record, err := dane.Verify(c.log.Logger, c.daneRecords, cs, c.remoteHostname, c.daneMoreHostnames)
|
||||
verified, record, err := dane.Verify(c.log.Logger, c.daneRecords, cs, c.remoteHostname, c.daneMoreHostnames, c.rootCAs)
|
||||
c.log.Debugx("dane verification", err, slog.Bool("verified", verified), slog.Any("record", record))
|
||||
if verified {
|
||||
if c.daneVerifiedRecord != nil {
|
||||
@ -355,7 +333,7 @@ func (c *Client) tlsConfig() *tls.Config {
|
||||
if c.ignoreTLSVerifyErrors {
|
||||
// We ignore the failure and continue the connection.
|
||||
c.log.Infox("verifying dane failed, continuing with connection", err)
|
||||
metricTLSRequiredNoIgnored.WithLabelValues("daneverification").Inc()
|
||||
MetricTLSRequiredNoIgnored.IncLabels("daneverification")
|
||||
} else {
|
||||
// This connection will fail.
|
||||
daneErr = dane.ErrNoMatch
|
||||
@ -547,7 +525,7 @@ func (c *Client) readecode(ecodes bool) (code int, secode, lastLine string, text
|
||||
c.cmds = c.cmds[1:]
|
||||
}
|
||||
}
|
||||
metricCommands.WithLabelValues(cmd, fmt.Sprintf("%d", co), sec).Observe(float64(time.Since(c.cmdStart)) / float64(time.Second))
|
||||
MetricCommands.ObserveLabels(float64(time.Since(c.cmdStart))/float64(time.Second), cmd, fmt.Sprintf("%d", co), sec)
|
||||
c.log.Debug("smtpclient command result",
|
||||
slog.String("cmd", cmd),
|
||||
slog.Int("code", co),
|
||||
@ -651,7 +629,7 @@ func (c *Client) recover(rerr *error) {
|
||||
}
|
||||
cerr, ok := x.(Error)
|
||||
if !ok {
|
||||
metrics.PanicInc(metrics.Smtpclient)
|
||||
MetricPanicInc()
|
||||
panic(x)
|
||||
}
|
||||
*rerr = cerr
|
||||
@ -779,7 +757,7 @@ func (c *Client) hello(ctx context.Context, tlsMode TLSMode, ehloHostname dns.Do
|
||||
c.r = bufio.NewReader(c.tr)
|
||||
c.w = bufio.NewWriter(c.tw)
|
||||
|
||||
tlsversion, ciphersuite := mox.TLSInfo(nconn)
|
||||
tlsversion, ciphersuite := moxio.TLSInfo(nconn)
|
||||
c.log.Debug("starttls client handshake done",
|
||||
slog.Any("tlsmode", tlsMode),
|
||||
slog.Bool("verifypkix", c.tlsVerifyPKIX),
|
||||
|
@ -10,7 +10,6 @@ import (
|
||||
|
||||
"github.com/mjl-/mox/dns"
|
||||
"github.com/mjl-/mox/mlog"
|
||||
"github.com/mjl-/mox/mox-"
|
||||
)
|
||||
|
||||
// DialHook can be used during tests to override the regular dialer from being used.
|
||||
@ -50,10 +49,9 @@ type Dialer interface {
|
||||
// Dial updates dialedIPs, callers may want to save it so it can be taken into
|
||||
// account for future delivery attempts.
|
||||
//
|
||||
// If we have fully specified local SMTP listener IPs, we set those for the
|
||||
// outgoing connection. The admin probably configured these same IPs in SPF, but
|
||||
// others possibly not.
|
||||
func Dial(ctx context.Context, elog *slog.Logger, dialer Dialer, host dns.IPDomain, ips []net.IP, port int, dialedIPs map[string][]net.IP) (conn net.Conn, ip net.IP, rerr error) {
|
||||
// The first matching protocol family from localIPs is set for the local side
|
||||
// of the TCP connection.
|
||||
func Dial(ctx context.Context, elog *slog.Logger, dialer Dialer, host dns.IPDomain, ips []net.IP, port int, dialedIPs map[string][]net.IP, localIPs []net.IP) (conn net.Conn, ip net.IP, rerr error) {
|
||||
log := mlog.New("smtpclient", elog)
|
||||
timeout := 30 * time.Second
|
||||
if deadline, ok := ctx.Deadline(); ok && len(ips) > 0 {
|
||||
@ -66,7 +64,7 @@ func Dial(ctx context.Context, elog *slog.Logger, dialer Dialer, host dns.IPDoma
|
||||
addr := net.JoinHostPort(ip.String(), fmt.Sprintf("%d", port))
|
||||
log.Debug("dialing host", slog.String("addr", addr))
|
||||
var laddr net.Addr
|
||||
for _, lip := range mox.Conf.Static.SpecifiedSMTPListenIPs {
|
||||
for _, lip := range localIPs {
|
||||
ipIs4 := ip.To4() != nil
|
||||
lipIs4 := lip.To4() != nil
|
||||
if ipIs4 == lipIs4 {
|
||||
|
@ -41,7 +41,7 @@ func TestDialHost(t *testing.T) {
|
||||
if err != nil || !reflect.DeepEqual(ips, []net.IP{net.ParseIP("10.0.0.1"), net.ParseIP("2001:db8::1")}) || !dualstack {
|
||||
t.Fatalf("expected err nil, address 10.0.0.1,2001:db8::1, dualstack true, got %v %v %v", err, ips, dualstack)
|
||||
}
|
||||
_, ip, err := Dial(ctxbg, log.Logger, nil, ipdomain("dualstack.example"), ips, 25, dialedIPs)
|
||||
_, ip, err := Dial(ctxbg, log.Logger, nil, ipdomain("dualstack.example"), ips, 25, dialedIPs, nil)
|
||||
if err != nil || ip.String() != "10.0.0.1" {
|
||||
t.Fatalf("expected err nil, address 10.0.0.1, dualstack true, got %v %v %v", err, ip, dualstack)
|
||||
}
|
||||
@ -50,7 +50,7 @@ func TestDialHost(t *testing.T) {
|
||||
if err != nil || !reflect.DeepEqual(ips, []net.IP{net.ParseIP("2001:db8::1"), net.ParseIP("10.0.0.1")}) || !dualstack {
|
||||
t.Fatalf("expected err nil, address 2001:db8::1,10.0.0.1, dualstack true, got %v %v %v", err, ips, dualstack)
|
||||
}
|
||||
_, ip, err = Dial(ctxbg, log.Logger, nil, ipdomain("dualstack.example"), ips, 25, dialedIPs)
|
||||
_, ip, err = Dial(ctxbg, log.Logger, nil, ipdomain("dualstack.example"), ips, 25, dialedIPs, nil)
|
||||
if err != nil || ip.String() != "2001:db8::1" {
|
||||
t.Fatalf("expected err nil, address 2001:db8::1, dualstack true, got %v %v %v", err, ip, dualstack)
|
||||
}
|
||||
|
Reference in New Issue
Block a user