Add a new -a parameter, to accept an 'A' record in addition to the MX required by default.
Update the man page with the new option.
.SH SYNOPSIS
-\fBemail-validator\fR [\fB\-h\fR] [\fB-i \fIFILE\fR] [\fB-o \fIFILE\fR] \fI<input>\fR
+\fBemail-validator\fR [\fB\-ha\fR] [\fB-i \fIFILE\fR] [\fB-o \fIFILE\fR] \fI<input>\fR
.SH INPUT
.IP \[bu]
We confirm the existence of a MX record for the domain part of the
-address.
+address. This is not required; in fact many domains accept mail via
+an 'A' record for e.g. example.com which is used in lieu of an MX
+record. This behavior can be controlled via the \fR\-a\fR flag.
+
+.P
+These checks are performed in parallel using the number of available
+threads.
.SH OPTIONS
+.IP \fB\-\-accept-a\fR,\ \fB\-a\fR
+Accept an 'A' record for the domain instead of requiring an MX record
+(the default).
+
.IP \fB\-\-input\fR,\ \fB\-i\fR
Specify the input file containing a list of email addresses, rather
than using stdin (the default).
Perform basic syntax and deliverability checks on email addresses.
-executable email_validator
+executable email-validator
build-depends:
base == 4.*,
bytestring == 0.10.*,
-BIN = dist/build/email_validator/email_validator
+BIN = dist/build/email-validator/email-validator
TESTSUITE_BIN = dist/build/testsuite/testsuite
.PHONY : test dist hlint
-- We optionally accept input/output files to use instead of
-- stdin/stdout.
-data Args = Args { input_file :: Maybe FilePath,
+data Args = Args { accept_a :: Bool,
+ input_file :: Maybe FilePath,
output_file :: Maybe FilePath }
deriving (Show, Data, Typeable)
my_summary :: String
my_summary = program_name ++ "-" ++ (showVersion version)
+accept_a_help :: String
+accept_a_help =
+ "Accept an 'A' record for the domain instead of requiring an MX record."
+
input_file_help :: String
input_file_help =
"Path to the input file (default: stdin), one email address per line"
arg_spec :: Mode (CmdArgs Args)
arg_spec =
cmdArgsMode $
- Args { input_file = def &= typFile &= help input_file_help,
+ Args { accept_a = def &= help accept_a_help,
+ input_file = def &= typFile &= help input_file_help,
output_file = def &= typFile &= help output_file_help }
&= program program_name
&= summary my_summary
defaultResolvConf,
makeResolvSeed,
withResolver)
-import Network.DNS.Lookup (lookupMX)
+import Network.DNS.Lookup (lookupA, lookupMX)
import System.Directory (doesFileExist)
import System.Exit (exitWith, ExitCode(..))
import System.IO (
_ -> return True
+-- | Check whether the given domain has a valid A record.
+validate_a :: Resolver -> Domain -> IO Bool
+validate_a resolver domain
+ | domain `elem` common_domains = return True
+ | otherwise = do
+ result <- lookupA resolver domain
+ case result of
+ Nothing -> return False
+ _ -> return True
+
-- | Validate an email address by doing some simple syntax checks and
-- (if those fail) an MX lookup. We don't count an A record as a mail
-- exchanger.
-validate :: Resolver -> Address -> IO (Address, Bool)
-validate resolver address = do
+validate :: Resolver -> Bool -> Address -> IO (Address, Bool)
+validate resolver accept_a address = do
let valid_syntax = validate_syntax address
if valid_syntax then do
let (_,domain) = parts address
mx_result <- validate_mx resolver domain
- return (address, mx_result)
+ if mx_result
+ then return (address, True)
+ else
+ if accept_a
+ then do
+ a_result <- validate_a resolver domain
+ return (address, a_result)
+ else
+ return (address, False)
else
return (address, False)
rs <- makeResolvSeed resolv_conf
withResolver rs $ \resolver -> do
-- Construst a list of [IO (Address, Bool)]
- let actions = map (validate resolver) nonempty_addresses
+ let actions = map (validate resolver accept_a) nonempty_addresses
-- And compute them in parallel.
results <- parallel actions
stopGlobalPool