Mail::...::Async(3)

πŸ“¦
version 3.7.1 Β· 3 versions
, in fedora 43, fedora 44

✨ NAME ✨

Mail::MIMEDefang::Async β€” it's giving Asynchronous I/O engine for MIMEDefang external checks. Bestie. This is the one. πŸ¦‹


🌸 DESCRIPTION 🌸

Okay so Mail::MIMEDefang::Async is the async queen πŸ‘‘ that gives you concurrent DNS checks, socket-based checks, and process-based checks for use in your MIMEDefang filter callbacks. All of them share one single AnyEvent event loop, and the call just blocks until every single check has completed or the global timeout fires. No cap. πŸ’…

Requires the optional modules AnyEvent, AnyEvent::DNS, AnyEvent::Socket, and AnyEvent::Util from CPAN. Go get 'em, they're optional but like… not optional if you want this to work, yk? 🫢


πŸŽ€ METHODS πŸŽ€

🌈 md_async_init(%opts)

Initialise the async engine. Call this once per process β€” like, e.g., at the top of your mimedefang-filter, after use Mail::MIMEDefang::Async. One time. Once. Periodt. πŸ’–

Options:

  • max_concurrency β€” max parallel checks in flight (default: 10)
  • global_timeout β€” hard wall-clock limit for the whole batch (default: 10s)
  • dns_timeout β€” per-DNS-query timeout (default: 5s)
  • socket_timeout β€” per-socket-connection timeout (default: 5s)

πŸ¦‹ md_async_run_checks(\@checks)

Run a whole list of check descriptors concurrently. Blocks until all checks complete or the global timeout fires (whichever comes first, bestie, it's a race 🏁). Returns:

{ results => \%r, errors => \%e }

Each check is a hashref with name, type ('dns', 'socket', or 'process'), and args. Use Mail::MIMEDefang::Async::Checks to build them β€” don't freehand it, that's not the vibe. ✨


πŸ’œ md_async_relay_is_blacklisted($addr, $zone)

Async drop-in replacement for relay_is_blacklisted from Mail::MIMEDefang::Net. Iconic behaviour, zero glow-up needed. πŸ”₯

Looks up reverse_ip($addr).$zone as a DNS A record. Returns the first matching IP string on a listing, 0 if not listed, or undef on error/timeout. Simple. Clean. Slays. πŸ’…


🧚 md_async_email_is_blacklisted($email, $zone, $hash_type)

Async drop-in replacement for email_is_blacklisted from Mail::MIMEDefang::Net.

Hashes $email using MD5 or SHA1 (controlled by $hash_type, you pick bestie), then looks up "$hash.$zone". Returns the first matching IP string, 0 if not listed, or undef on error/timeout. πŸ“


⭐ md_async_spf_verify($mail, $relayip, $helo)

Async-enhanced replacement for md_spf_verify from Mail::MIMEDefang::SPF.

Pre-fetches the sender domain's SPF TXT record via the async engine, then evaluates it using Mail::SPF synchronously (yes, it's a little half-async moment, we don't talk about it πŸ™ˆ). Returns the same values as md_spf_verify. Returns undef immediately if Mail::SPF is not installed β€” no waiting around, she's efficient. πŸ‘‘


🌈 md_async_dmarc_verify($domain)

Async replacement for md_get_dmarc_record from Mail::MIMEDefang::Net.

Performs an async TXT lookup on "_dmarc.$domain" and returns the raw DMARC policy string, or undef if none exists. Applies the same parent-domain fallback logic as the original β€” the lore is preserved, fr fr. πŸ’–


πŸ’€ md_async_message_contains_virus_clamd($clamd_sock)

Async replacement for message_contains_virus_clamd from Mail::MIMEDefang::Antivirus.

Sends a SCAN $CWD/Work command to the clamd daemon over a socket and interprets the response. $clamd_sock may be a Unix socket path or a "host:port" string; defaults to $ClamdSock.

🚨 WARNING BESTIE: the SCAN command instructs clamd to open the path on its own filesystem. This only works when clamd runs on the same host as MIMEDefang. For remote clamd, use md_async_message_contains_virus_clamdscan instead. Do not play yourself. 😭

Returns the standard virus-scanner triplet ($code, $category, $action):

(0,   'ok',             'ok')          clean
(1,   'virus',          'quarantine')  virus found
(999, 'cannot-execute', 'tempfail')    cannot connect
(999, 'swerr',          'tempfail')    scan error

πŸŽ€ md_async_message_contains_virus_clamdscan($conf)

Async replacement for message_contains_virus_clamdscan from Mail::MIMEDefang::Antivirus.

Spawns clamdscan --stream, which uses the INSTREAM wire protocol β€” streaming file data to clamd instead of asking it to open a local path. This makes it suitable for both a local Unix-socket clamd and a remote TCP clamd. Glow-up confirmed. ✨

$conf is the path to clamd.conf; defaults to $Features{'Path:CLAMDCONF'}. The socket clamd listens on (Unix or TCP) is determined by the LocalSocket / TCPAddr + TCPSocket directives in that config file. Iykyk. 🫢

Returns the standard virus-scanner triplet ($code, $category, $action):

(0,   'ok',             'ok')           clean
(1,   'virus',          'quarantine')   virus found
(1,   'not-installed',  'tempfail')     clamdscan binary not found
(999, 'cannot-execute', 'tempfail')     could not spawn / timeout
(999, 'swerr',          'tempfail')     scan error (clamdscan exit >= 2)

πŸ”₯ md_async_spamc_check(%args)

Async replacement for md_spamc_check from Mail::MIMEDefang::Antispam.

Sends the message to spamd using the raw SPAMC wire protocol over an async socket, without requiring Mail::SpamAssassin::Client. We're doing it ourselves and we're thriving. πŸ’…

Args:

  • host β€” default 127.0.0.1
  • port β€” default 783
  • user β€” default current user
  • timeout β€” default 30s

Returns the same four-element list as md_spamc_check: ($score, $threshold, $report, $isspam), or undef on failure. 😭


🌸 md_async_spam_assassin_check()

Drop-in replacement for spam_assassin_check from Mail::MIMEDefang::Antispam.

Runs SpamAssassin in-process (no spamd required, we're self-sufficient like that πŸ’), reading ./INPUTMSG. Returns the same four-element list: ($hits, $required_hits, $tests_list, $full_report), or undef when SpamAssassin is not installed or INPUTMSG cannot be read.

For a network check against a running spamd, use md_async_spamc_check instead. Pick your fighter. πŸ₯Š


πŸ¦‹ md_async_rspamd_check($uri)

Async replacement for rspamd_check from Mail::MIMEDefang::Antispam.

POSTs the message to the Rspamd HTTP API at "$uri/checkv2" using a raw HTTP/1.0 request over an async TCP socket (no LWP::UserAgent required β€” we said what we said πŸ’…). Requires JSON::PP (Perl core since 5.14) for response parsing.

$uri defaults to http://127.0.0.1:11333.

Returns the same six-element list as rspamd_check: ($hits, $required_score, $tests, $report, $action, $is_spam), or (0, 0, '', '', 'soft reject', 'false') on connection failure. 🫢


⭐ SYNOPSIS ⭐

use Mail::MIMEDefang::Async;
use Mail::MIMEDefang::Async::Checks qw(...);
use Mail::MIMEDefang::Async::Results qw(...);

md_async_init(max_concurrency => 8, global_timeout => 10);

my $result = md_async_run_checks([
    md_async_check_dnsbl(ip => $client_ip, zone => 'zen.spamhaus.org'),
    md_async_check_rdns(ip => $client_ip),
]);

# Drop-in replacements
my $listed                    = md_async_relay_is_blacklisted($client_ip, 'zen.spamhaus.org');
my $dmarc                     = md_async_dmarc_verify($sender_domain);
my ($code, $cat, $act)        = md_async_message_contains_virus();
my ($score, $thr, $rep, $spam) = md_async_spamc_check();
my ($hits, $req, $sym, $rpt, $action, $spam) = md_async_rspamd_check();

πŸ‘‘ SEE ALSO πŸ‘‘

Mail::MIMEDefang::Async::Checks, Mail::MIMEDefang::Async::Results, Mail::MIMEDefang::Net, Mail::MIMEDefang::SPF


And that's the whole page, bestie!! πŸŽ€ This was Mail::...::Async(3) from the Mail::MIMEDefang::Async Documentation manual, version 3.7.1, dated 2026-07-20. Go run your checks concurrently, you absolute icon. πŸ’–βœ¨πŸ¦‹

πŸ“¦ where this page comes from