Documentation

NAME

Mail::MIMEDefang::Async - Asynchronous I/O engine for MIMEDefang external checks

DESCRIPTION

Mail::MIMEDefang::Async provides concurrent DNS, socket-based, and process-based checks for use in MIMEDefang filter callbacks. All checks share a single AnyEvent event loop and the call blocks until every check has completed or the global timeout fires.

Requires the optional modules AnyEvent, AnyEvent::DNS, AnyEvent::Socket, and AnyEvent::Util from CPAN.

METHODS

  • md_async_init(%opts)

    Initialise the async engine. Call once per process (e.g. at the top of your mimedefang-filter, after use Mail::MIMEDefang::Async).

    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 list of check descriptors concurrently. Blocks until all checks complete or the global timeout fires. 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.

  • md_async_relay_is_blacklisted($addr, $zone)

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

    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.

  • 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), 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. Returns the same values as md_spf_verify. Returns undef immediately if Mail::SPF is not installed.

  • 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.

  • 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.

    Note: 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.

    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 rather than asking it to open a local path. This makes it suitable for both a local Unix-socket clamd and a remote TCP clamd. $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.

    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.

    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), 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.

  • 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). 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