Documentation
¶
Overview ¶
Package smtp probes a mailbox over SMTP to see whether the receiving server accepts mail for it, and detects catch-all servers.
It is a separate module from github.com/bakhod1r/emailx: it is the only part of the project that opens an outbound mail connection, and keeping it out of the core package means a program that only parses addresses links no third-party code.
import smtpx "github.com/bakhod1r/emailx/smtp"
res := smtpx.Verify("user@example.com")
res.Status // StatusValid, StatusInvalid, StatusCatchAll, StatusUnknown
res.CatchAll // the server also accepted a random address
Caveats ¶
A StatusValid result is a strong signal but not proof. Most cloud providers block outbound port 25 entirely, so probes from them return StatusUnknown. Unthrottled probing is the fastest way to get a sending IP blocklisted, so a probe-heavy workload should always set Options.Limiter. Many large providers deliberately answer every probe the same way, which shows up as StatusCatchAll.
Concurrency ¶
All exported functions are safe for concurrent use.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Limiter ¶
type Limiter interface {
// Wait blocks until the caller may proceed, or until ctx is done.
Wait(ctx context.Context) error
}
Limiter paces outbound work. Verify calls Wait before each connection, so a caller can keep probe volume under whatever the receiving side and their own reputation will tolerate.
type Options ¶
type Options struct {
// HelloName is the domain sent in EHLO. It should be a hostname that
// resolves back to the connecting IP, otherwise many servers refuse.
HelloName string
// FromAddress is the envelope sender used in MAIL FROM. An empty
// sender ("<>") is standard for verification but some servers reject it.
FromAddress string
// Timeout bounds the whole probe. Defaults to 10s.
Timeout time.Duration
// Port is the SMTP port to dial. Defaults to 25.
Port string
// SkipCatchAllCheck skips the extra random-address probe, halving the
// work when catch-all status is not needed.
SkipCatchAllCheck bool
// Limiter paces connections. Nil means no pacing, which is only safe
// for occasional one-off probes: sustained unthrottled probing gets the
// sending IP blocklisted. Share one Limiter across all probes.
Limiter Limiter
}
Options tunes an SMTP probe.
type RateLimiter ¶
type RateLimiter struct {
// contains filtered or unexported fields
}
RateLimiter is a token bucket: rate tokens per second, up to burst held in reserve. Unthrottled SMTP probing is the fastest way to get an IP blocklisted, so a probe-heavy workload should always set one.
func NewRateLimiter ¶
func NewRateLimiter(perSecond float64, burst int) *RateLimiter
NewRateLimiter allows perSecond probes per second, permitting short bursts of up to burst. A non-positive perSecond means no limit; burst below 1 is treated as 1.
Example ¶
package main
import (
"fmt"
smtpx "github.com/bakhod1r/emailx/smtp"
)
func main() {
// Five probes per second, allowing short bursts of ten.
limiter := smtpx.NewRateLimiter(5, 10)
opts := smtpx.Options{
Limiter: limiter,
SkipCatchAllCheck: true,
}
_ = opts // pass to Verify for each address in the batch
fmt.Println("limiter ready")
}
Output: limiter ready
type Result ¶
type Result struct {
Status Status
// MXHost is the mail server that answered.
MXHost string
// Code and Message are the last SMTP reply seen for the RCPT TO probe.
Code int
Message string
// CatchAll reports that a random, certainly-nonexistent address was
// also accepted.
CatchAll bool
// Greylisted reports a 4xx temporary refusal; a retry later may succeed.
Greylisted bool
// STARTTLS reports whether the server advertised STARTTLS.
STARTTLS bool
// Duration is how long the probe took.
Duration time.Duration
// Err holds the transport-level failure, if any.
Err error
}
Result describes an SMTP verification attempt.
type Status ¶
type Status string
Status is the outcome of an SMTP mailbox probe.
const ( // StatusValid means the server accepted RCPT TO for the address. StatusValid Status = "valid" // StatusInvalid means the server rejected the address permanently (5xx). StatusInvalid Status = "invalid" // StatusCatchAll means the server accepts every address, so acceptance // proves nothing about this particular mailbox. StatusCatchAll Status = "catch-all" // StatusUnknown means the probe could not reach a conclusion: no MX, // connection refused, greylisting, or a 4xx temporary refusal. StatusUnknown Status = "unknown" )