From da16d4008b48e054104d0d186f47c6382f280b06 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 28 Aug 2022 12:44:16 -0400 Subject: [PATCH 01/10] email-validator.cabal: update to v1.0.1. --- email-validator.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/email-validator.cabal b/email-validator.cabal index c899d8c..24287a7 100644 --- a/email-validator.cabal +++ b/email-validator.cabal @@ -1,6 +1,6 @@ cabal-version: 3.0 name: email-validator -version: 1.0.0 +version: 1.0.1 author: Michael Orlitzky maintainer: Michael Orlitzky homepage: http://michael.orlitzky.com/code/email-validator.xhtml -- 2.43.2 From 62e8d2df29b616854b97eb703dcddc796e2c88ca Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 24 Apr 2024 19:31:19 -0400 Subject: [PATCH 02/10] src/Main.hs: add a few more common email domains --- src/Main.hs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Main.hs b/src/Main.hs index 1cc4c73..c220aaa 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -41,13 +41,25 @@ import EmailAddress( resolv_conf :: ResolvConf resolv_conf = defaultResolvConf { resolvTimeout = 10 * 1000 * 1000 } --- | A list of common domains, there's no need to waste MX lookups --- on these. +-- | A list of common domains, there's no need to waste MX lookups on +-- these. This is a very limited list; I don't want to be in the +-- business of monitoring a million domains for MX record updates. common_domains :: [Domain] common_domains = map BS.pack [ "aol.com", "comcast.net", + "cox.net", "gmail.com", + "gmx.de", + "googlemail.com", + "hotmail.com", + "icloud.com", + "live.com", + "me.com", "msn.com", + "outlook.com", + "proton.me", + "protonmail.ch", + "protonmail.com", "yahoo.com", "verizon.net" ] -- 2.43.2 From 5d4713ed8f92e5ba78566f67dc8ff3cf9900c885 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 24 Apr 2024 19:31:50 -0400 Subject: [PATCH 03/10] makefile: disable -Wmissing-kind-signatures You need a GHC extension to fix this. --- makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/makefile b/makefile index e77b227..5052789 100644 --- a/makefile +++ b/makefile @@ -13,6 +13,7 @@ HCFLAGS += -Weverything \ -Wno-prepositive-qualified-module \ -Wno-missing-safe-haskell-mode \ -Wno-missing-deriving-strategies \ + -Wno-missing-kind-signatures \ -rtsopts \ -threaded -- 2.43.2 From 84dadb632d7fd7f968b59f1317cf9e0b562fbe79 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 24 Apr 2024 19:32:14 -0400 Subject: [PATCH 04/10] email-validator.cabal: http -> https --- email-validator.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/email-validator.cabal b/email-validator.cabal index 24287a7..c2635fe 100644 --- a/email-validator.cabal +++ b/email-validator.cabal @@ -3,7 +3,7 @@ name: email-validator version: 1.0.1 author: Michael Orlitzky maintainer: Michael Orlitzky -homepage: http://michael.orlitzky.com/code/email-validator.xhtml +homepage: https://michael.orlitzky.com/code/email-validator.xhtml bug-reports: mailto:michael@orlitzky.com category: Utils license: AGPL-3.0-only @@ -110,5 +110,5 @@ test-suite doctests source-repository head type: git - location: http://gitweb.michael.orlitzky.com/email-validator.git + location: https://gitweb.michael.orlitzky.com/email-validator.git branch: master -- 2.43.2 From 56e2bcb85bc0df2c40b62821e1a85dd01def5b20 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 24 Apr 2024 19:33:12 -0400 Subject: [PATCH 05/10] doc/COPYING,email-validator.cabal: use AGPL-3.0+, include COPYING --- doc/COPYING | 15 +++++++++++++++ email-validator.cabal | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 doc/COPYING diff --git a/doc/COPYING b/doc/COPYING new file mode 100644 index 0000000..dcca366 --- /dev/null +++ b/doc/COPYING @@ -0,0 +1,15 @@ +email-validator: basic syntax and deliverability checks on email addresses +Copyright (C) 2024 Michael Orlitzky + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . diff --git a/email-validator.cabal b/email-validator.cabal index c2635fe..6627128 100644 --- a/email-validator.cabal +++ b/email-validator.cabal @@ -6,10 +6,11 @@ maintainer: Michael Orlitzky homepage: https://michael.orlitzky.com/code/email-validator.xhtml bug-reports: mailto:michael@orlitzky.com category: Utils -license: AGPL-3.0-only +license: AGPL-3.0-or-later license-file: doc/LICENSE build-type: Simple extra-source-files: + doc/COPYING doc/man1/email-validator.1 synopsis: Perform basic syntax and deliverability checks on email addresses. -- 2.43.2 From 432089f7b63e96b6e15ad7895f0e4b6aa1a52efc Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 24 Apr 2024 20:07:38 -0400 Subject: [PATCH 06/10] src/Main.hs: support NULLMX (RFC7505) We now reject NULLMX records, i.e. those that contain a single dot. This is only a partial solution since we should be rejecting these domains even if --accept-a was given. --- src/Main.hs | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/Main.hs b/src/Main.hs index c220aaa..74789e7 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -64,14 +64,35 @@ common_domains = map BS.pack [ "aol.com", "verizon.net" ] --- | Check whether the given domain has a valid MX record. +-- | Check whether the given domain has a valid MX record. NULLMX +-- (RFC7505) records consisting of a single period must not be +-- accepted. +-- +-- Two points about NULLMX: +-- +-- * RFC7505 states that a domain MUST NOT have any other MX records +-- if it has a NULLMX record. We don't enforce this. If you have a +-- NULLMX record and some other MX record, we will reluctantly +-- consider the second one valid. +-- +-- * RFC7505 also states that a NULLMX record must have a priority +-- of 0. We do not enforce this either. We ignore any records +-- containing an empty label (i.e. a single dot). Such a record will +-- not be deliverable anyway, and in light of the first item, means +-- that we will not \"incorrectly\" reject batshit-crazy domains +-- that have a NULLMX record (but with a non-zero priority) in +-- addition to other, valid MX records. +-- + validate_mx :: Resolver -> Domain -> IO Bool validate_mx resolver domain | domain `elem` common_domains = return True | otherwise = do result <- lookupMX resolver domain - case result of - -- A list of one or more elements? + let nullmx = BS.pack "." :: Domain + let non_null = (\(mx,_) -> mx /= nullmx) :: (Domain,Int) -> Bool + let non_null_mxs = fmap (filter non_null) result + case non_null_mxs of Right (_:_) -> return True _ -> return False -- 2.43.2 From 03eb76c0dc4238bc8c579beddb0e65fbed64d28d Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 24 Apr 2024 20:33:40 -0400 Subject: [PATCH 07/10] src/Main.hs: support NULLMX fully We now ignore --accept-a for domains that have NULLMX records. --- src/Main.hs | 89 +++++++++++++++++++++++++++++------------------------ 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/src/Main.hs b/src/Main.hs index 74789e7..ab5f93a 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -64,38 +64,40 @@ common_domains = map BS.pack [ "aol.com", "verizon.net" ] --- | Check whether the given domain has a valid MX record. NULLMX --- (RFC7505) records consisting of a single period must not be --- accepted. +-- | Check whether the given domain has a valid MX record. -- --- Two points about NULLMX: +-- NULLMX (RFC7505) records consisting of a single period must not +-- be accepted. Moreover, the existence of a NULLMX must be reported +-- back to the caller because the whole point of a NULLMX is that +-- its existence should preempt an @A@ record check. We abuse the +-- return type for this, and return @Nothing@ in the event of a +-- NULLMX. Otherwise we return @Just True@ or @Just False@ to +-- indicate the existence (or not) of MX records. -- --- * RFC7505 states that a domain MUST NOT have any other MX records --- if it has a NULLMX record. We don't enforce this. If you have a --- NULLMX record and some other MX record, we will reluctantly --- consider the second one valid. +-- RFC7505 states that a domain MUST NOT have any other MX records +-- if it has a NULLMX record. We enforce this. If you have a NULLMX +-- record and some other MX record, we consider the set invalid. -- --- * RFC7505 also states that a NULLMX record must have a priority --- of 0. We do not enforce this either. We ignore any records --- containing an empty label (i.e. a single dot). Such a record will --- not be deliverable anyway, and in light of the first item, means --- that we will not \"incorrectly\" reject batshit-crazy domains --- that have a NULLMX record (but with a non-zero priority) in --- addition to other, valid MX records. --- - -validate_mx :: Resolver -> Domain -> IO Bool +validate_mx :: Resolver -> Domain -> IO (Maybe Bool) validate_mx resolver domain - | domain `elem` common_domains = return True + | domain `elem` common_domains = return $ Just True | otherwise = do result <- lookupMX resolver domain - let nullmx = BS.pack "." :: Domain - let non_null = (\(mx,_) -> mx /= nullmx) :: (Domain,Int) -> Bool - let non_null_mxs = fmap (filter non_null) result - case non_null_mxs of - Right (_:_) -> return True - _ -> return False - + case result of + Left _ -> + return $ Just False + Right mxs -> + case mxs of + [] -> return $ Just False + _ -> if any (is_null) mxs + then return Nothing + else return $ Just True + where + nullmx :: Domain + nullmx = BS.pack "." + + is_null :: (Domain,Int) -> Bool + is_null (mx,prio) = mx == nullmx && prio == 0 -- | Check whether the given domain has a valid A record. validate_a :: Resolver -> Domain -> IO Bool @@ -109,26 +111,33 @@ validate_a resolver domain -- | 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. +-- (if those fail) an MX lookup. We don't count an @A@ record as a mail +-- exchanger unless @accept_a@ is True. And even then, the existence +-- of a NULLMX record will preclude the acceptance of an @A@ record. +-- The domain @example.com@ is a great test case for this behavior. validate :: Resolver -> Bool -> Bool -> Address -> IO (Address, Bool) validate resolver accept_a rfc5322 address = do let valid_syntax = validate_syntax rfc5322 address if valid_syntax then do let (_,domain) = parts address mx_result <- validate_mx resolver domain - 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) - + case mx_result of + Nothing -> + -- NULLMX, don't fall back to 'A' records under any + -- circumstances. + return (address, False) + Just mxr -> + if mxr + 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) main :: IO () -- 2.43.2 From 742becd4a0aa2ca87935e2793700bef03e0ea823 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 24 Apr 2024 20:33:58 -0400 Subject: [PATCH 08/10] doc,email-validator: document the new NULLMX handling --- doc/man1/email-validator.1 | 10 ++++++---- email-validator.cabal | 11 +++++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/doc/man1/email-validator.1 b/doc/man1/email-validator.1 index 1e0e144..605b570 100644 --- a/doc/man1/email-validator.1 +++ b/doc/man1/email-validator.1 @@ -33,10 +33,12 @@ grammar (see the \fB\-\-rfc5322\fR option). .IP \[bu] Confirmation of the existence of an \fIMX\fR record for the domain -part of the address. This is not required; in fact many domains accept -mail via an \fIA\fR record for e.g. example.com which is used in lieu -of an \fIMX\fR record. This behavior can be controlled via the -\fR\-\-accept\-a\fR flag. +part of the address. NULLMX (RFC7505) records are not accepted. This +is not required; in fact many domains accept mail via an \fIA\fR +record for (say) example.com which is used in lieu of an \fIMX\fR +record. This behavior can be controlled via the \fR\-\-accept\-a\fR +flag, but note that \fR\-\-accept\-a\fR is ignored for domains that +have NULLMX records. .P These checks are performed in parallel using the number of available diff --git a/email-validator.cabal b/email-validator.cabal index 6627128..3e975f3 100644 --- a/email-validator.cabal +++ b/email-validator.cabal @@ -23,10 +23,13 @@ description: * A syntax check using a regular expression, or the full RFC 5322 grammar (see the @--rfc5322@ option). - * Confirmation of the existence of an @MX@ record for the domain part of - the 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 @--accept-a@ flag. + * Confirmation of the existence of an @MX@ record for the domain + part of the address. NULLMX (RFC7505) records are not + accepted. This is not required; in fact many domains accept mail + via an @A@ record for (say) example.com which is used in lieu of + an @MX@ record. This behavior can be controlled via the + @--accept-a@ flag, but note that @--accept-a@ is ignored for + domains that have NULLMX records. These checks are performed in parallel using the number of available threads. To increase the number of threads, you can pass the -- 2.43.2 From 978ee4745448322213d039e5678abd0819d5a377 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 24 Apr 2024 20:36:25 -0400 Subject: [PATCH 09/10] email-validator.cabal: drop the redundant description field RTFM :) --- email-validator.cabal | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/email-validator.cabal b/email-validator.cabal index 3e975f3..29e8159 100644 --- a/email-validator.cabal +++ b/email-validator.cabal @@ -19,36 +19,13 @@ description: * Ensuring that the length of local and domain parts is within the RFC-specified limits. - * A syntax check using a regular expression, or the full RFC 5322 - grammar (see the @--rfc5322@ option). - - * Confirmation of the existence of an @MX@ record for the domain - part of the address. NULLMX (RFC7505) records are not - accepted. This is not required; in fact many domains accept mail - via an @A@ record for (say) example.com which is used in lieu of - an @MX@ record. This behavior can be controlled via the - @--accept-a@ flag, but note that @--accept-a@ is ignored for - domains that have NULLMX records. - - These checks are performed in parallel using the number of available - threads. To increase the number of threads, you can pass the - appropriate flag to the GHC runtime. - - This will set the number of threads to 25: - - @ - $ email-validator +RTS -N25 < addresses.csv - @ - - /Input/ - - The @input@ (via stdin) should be a list of email addresses, - one per line. Empty lines will be ignored. - - /Output/ + grammar. + * Confirmation of valid @MX@ records (or, optionally, @A@ + records) for the domain. - Valid email addresses will be written to stdout, one per line. + A complete description, options, and examples can be found in the + man page. executable email-validator -- 2.43.2 From 3eab8e60aec66c4cb915d14cdb8b98618582e1d9 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Wed, 24 Apr 2024 20:36:54 -0400 Subject: [PATCH 10/10] email-validator.cabal: bump to version 1.1.0 --- email-validator.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/email-validator.cabal b/email-validator.cabal index 29e8159..94c14f4 100644 --- a/email-validator.cabal +++ b/email-validator.cabal @@ -1,6 +1,6 @@ cabal-version: 3.0 name: email-validator -version: 1.0.1 +version: 1.1.0 author: Michael Orlitzky maintainer: Michael Orlitzky homepage: https://michael.orlitzky.com/code/email-validator.xhtml -- 2.43.2