1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2026-10-29 15:02:30 +00:00
Files
go-chasquid-smtp/cmd/smtp-check/smtp-check.go
T
Alberto Bertogli 5131fdacb4 tlsconst: Deprecate in favour of stdlib's tls
The tlsconst internal package was created to provide human-readable
names for TLS versions and ciphers, from the IANA registry.

These days, that functionality is provided by stdlib's tls package, so
the internal package is no longer needed, and this patch removes it.

The only observable change is that the descriptive version in the
Received header now says "TLS 1.3" instead of "TLS-1.3", but it's just
as standard-compliant since that is in a free-form text section.
2026-10-04 18:35:40 +01:00

135 lines
2.8 KiB
Go

// smtp-check is a command-line too for checking SMTP setups.
package main
import (
"context"
"crypto/tls"
"flag"
"fmt"
"log"
"net"
"net/smtp"
"time"
"blitiri.com.ar/go/chasquid/internal/sts"
"blitiri.com.ar/go/spf"
"golang.org/x/net/idna"
)
var (
port = flag.String("port", "smtp",
"port to use for connecting to the MX servers")
localName = flag.String("localname", "localhost",
"specify the local name for the EHLO command")
skipTLSCheck = flag.Bool("skip_tls_check", false,
"skip TLS check (useful if connections are blocked)")
)
func main() {
flag.Parse()
domain := flag.Arg(0)
if domain == "" {
log.Fatal("Use: smtp-check <domain>")
}
domain, err := idna.ToASCII(domain)
if err != nil {
log.Fatalf("IDNA conversion failed: %v", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
log.Printf("=== STS policy")
policy, err := sts.UncheckedFetch(ctx, domain)
if err != nil {
log.Printf("Not available (%s)", err)
} else {
log.Printf("Parsed contents: [%+v]\n", *policy)
if err := policy.Check(); err != nil {
log.Fatalf("Invalid: %v", err)
}
log.Printf("OK")
}
log.Printf("")
mxs, err := net.LookupMX(domain)
if err != nil {
log.Fatalf("MX lookup: %v", err)
}
if len(mxs) == 0 {
log.Fatalf("MX lookup returned no results")
}
errs := []error{}
for _, mx := range mxs {
log.Printf("=== MX: %2d %s", mx.Pref, mx.Host)
ips, err := net.LookupIP(mx.Host)
if err != nil {
log.Fatal(err)
}
for _, ip := range ips {
result, err := spf.CheckHostWithSender(ip, domain, "test@"+domain)
log.Printf("SPF %v for %v: %v", result, ip, err)
if result != spf.Pass {
errs = append(errs,
fmt.Errorf("%s: SPF failed (%v)", mx.Host, ip))
}
}
if *skipTLSCheck {
log.Printf("TLS check skipped")
} else {
c, err := smtp.Dial(mx.Host + ":" + *port)
if err != nil {
log.Fatal(err)
}
err = c.Hello(*localName)
if err != nil {
log.Fatal(err)
}
config := &tls.Config{
// Expect the server to have a certificate valid for the MX
// we're connecting to.
ServerName: mx.Host,
}
err = c.StartTLS(config)
if err != nil {
log.Printf("TLS error: %v", err)
errs = append(errs, fmt.Errorf("%s: TLS failed", mx.Host))
} else {
cstate, _ := c.TLSConnectionState()
log.Printf("TLS OK: %s - %s", tls.VersionName(cstate.Version),
tls.CipherSuiteName(cstate.CipherSuite))
}
c.Close()
}
if policy != nil {
if !policy.MXIsAllowed(mx.Host) {
log.Printf("NOT allowed by STS policy")
errs = append(errs, fmt.Errorf("%s: STS failed", mx.Host))
}
log.Printf("Allowed by policy")
}
log.Printf("")
}
if len(errs) == 0 {
log.Printf("=== Success")
} else {
log.Printf("=== FAILED")
for _, err := range errs {
log.Printf("%v", err)
}
log.Fatal("")
}
}