Validation 电子邮件SMTP验证程序

Validation 电子邮件SMTP验证程序,validation,email,Validation,Email,我需要发送数百份新闻稿,但想先检查服务器上是否存在电子邮件。根据我在互联网上的研究,这叫做验证,至少我认为是这样 有几个库可以做到这一点,还有一个在()中包含开源代码的页面,但我很难阅读ASP Classic,而且它似乎使用了一些第三方库 是否有一些用于C#中SMTP验证的代码和/或其工作原理的一般说明?真正的(TM)电子邮件验证是尝试向该地址发送内容,并查看其是否被拒绝/反弹。因此,您只需将它们发送出去,并从邮件列表中删除失败的地址。SMTP是通过TCP/IP传输的基于文本的协议 您的验证程序

我需要发送数百份新闻稿,但想先检查服务器上是否存在电子邮件。根据我在互联网上的研究,这叫做验证,至少我认为是这样

有几个库可以做到这一点,还有一个在()中包含开源代码的页面,但我很难阅读ASP Classic,而且它似乎使用了一些第三方库


是否有一些用于C#中SMTP验证的代码和/或其工作原理的一般说明?

真正的(TM)电子邮件验证是尝试向该地址发送内容,并查看其是否被拒绝/反弹。因此,您只需将它们发送出去,并从邮件列表中删除失败的地址。

SMTP是通过TCP/IP传输的基于文本的协议

您的验证程序需要打开到服务器端口25(SMTP)的TCP/IP连接,写几行并读取答案。验证在“RCTP TO”行和“VFRY”行上完成(但并非始终如此)

描述了其工作原理(请参见Green@Beta.ARPA下面,S是客户端发送的行,R是从服务器接收的行):

SMTP过程的示例 此SMTP示例显示Smith在主机Alpha.ARPA上发送的邮件, 致主机Beta.ARPA上的琼斯、格林和布朗。这里我们假设 该主机Alpha直接与主机Beta联系。 S:邮寄地址: R:250可以 S:RCPT至: R:250可以 S:RCPT至: R:550这里没有这样的用户 请注意,由于垃圾邮件保护的原因,大多数MTA(邮件传输代理)都会关闭VRFY命令,如果您连续尝试几个RCPT,它们甚至可能会阻止您(请参阅)。所以,即使你找到了一个库来进行验证,它也不会值很多钱。以实玛利是对的,要真正找到答案,唯一的办法就是发一封电子邮件,看看它是否反弹


@是的,我建议你监视被拒绝的电子邮件。但是:并非所有被退回的邮件都会自动出现在您的“不存在”列表中,您还必须区分临时错误(例如邮箱已满)和永久错误。

请不要误会,但现在向少数人发送时事通讯是一件相当严重的事情。是的,您需要监视反弹(拒绝的电子邮件),反弹可以在SMTP发送期间同步发生(通常如果您连接的SMTP服务器是权威的),也可以作为系统生成的电子邮件异步发生,在SMTP发送成功后一段时间内发生

在发送这些电子邮件时,还应牢记《反垃圾邮件法》,并遵守法律;你必须提供一个不明嫌犯链接和一个实际的街道地址(以识别你和t0,允许用户通过蜗牛邮件发送不明嫌犯请求,如果他们选择的话)


如果不这样做,你的IP空地址最多只能被路由,最坏只能被起诉。

虽然许多域确实会因为滥用而返回误报,但仍然有一些优秀的组件可以执行SMTP验证之外的多个级别的验证。例如,首先检查是否至少存在该域是值得的。我正在编制与此问题相关的我自己的资源列表,您可以在此处跟踪:

(断开的链接)

对于那些可能想添加到此列表的人,我还将在此处包括我目前拥有的内容:

对于防弹表单和良好的用户体验,尽可能多地验证电子邮件地址是有帮助的。我可以从验证器中看到他们检查:

  • 语法
  • 根据错误电子邮件地址列表删除电子邮件
  • 根据坏域列表创建域
  • 邮箱域的列表
  • 域是否存在
  • 域是否有MX记录
  • 最后通过SMTP检查邮箱是否存在
这是管理员可以绕过的最后一步,基本上所有帐户验证请求都返回true,但在大多数情况下,如果用户故意输入了错误地址,则已被捕获。如果地址的域部分出现用户错误,也会被捕获

当然,将此类服务用于注册屏幕或表单的最佳实践是将此类验证与验证过程相结合,以确保电子邮件地址有效。在验证过程之前使用电子邮件验证器的好处在于,它将有助于获得更好的总体用户体验。

您可能需要它

下面是代码示例:


   // Create a new instance of the EmailValidator class.
   EmailValidator em = new EmailValidator();
   em.MessageLogging += em_MessageLogging;
   em.EmailValidated += em_EmailValidationCompleted;
   try
   {
       string[] list = new string[3] { "test1@testdomain.com", "test2@testdomain.com", "test3@testdomain.com" };
       em.ValidateEmails(list);
   }
   catch (EmailValidatorException exc2)
   {
       Console.WriteLine("EmailValidatorException: " + exc2.Message);
   }

您可以尝试下面的代码,它对我来说很好:

public class EmailTest {
    private static int hear(BufferedReader in) throws IOException {
        String line = null;
        int res = 0;

        while ((line = in.readLine()) != null) {
            String pfx = line.substring(0, 3);
            try {
                res = Integer.parseInt(pfx);
            } catch (Exception ex) {
                res = -1;
            }
            if (line.charAt(3) != '-')
                break;
        }

        return res;
    }

    private static void say(BufferedWriter wr, String text) throws IOException {
        wr.write(text + "\r\n");
        wr.flush();

        return;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private static ArrayList getMX(String hostName) throws NamingException {
        // Perform a DNS lookup for MX records in the domain
        Hashtable env = new Hashtable();
        env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
        DirContext ictx = new InitialDirContext(env);
        Attributes attrs = ictx.getAttributes(hostName, new String[] { "MX" });
        Attribute attr = attrs.get("MX");

        // if we don't have an MX record, try the machine itself
        if ((attr == null) || (attr.size() == 0)) {
            attrs = ictx.getAttributes(hostName, new String[] { "A" });
            attr = attrs.get("A");
            if (attr == null)
                throw new NamingException("No match for name '" + hostName + "'");
        }
        /*
         Huzzah! we have machines to try. Return them as an array list
         NOTE: We SHOULD take the preference into account to be absolutely
         correct. This is left as an exercise for anyone who cares.
         */
        ArrayList res = new ArrayList();
        NamingEnumeration en = attr.getAll();

        while (en.hasMore()) {
            String mailhost;
            String x = (String) en.next();
            String f[] = x.split(" ");
            // THE fix *************
            if (f.length == 1)
                mailhost = f[0];
            else if (f[1].endsWith("."))
                mailhost = f[1].substring(0, (f[1].length() - 1));
            else
                mailhost = f[1];
            // THE fix *************
            res.add(mailhost);
        }
        return res;
    }

    @SuppressWarnings("rawtypes")
    public static boolean isAddressValid(String address) {
        // Find the separator for the domain name
        int pos = address.indexOf('@');

        // If the address does not contain an '@', it's not valid
        if (pos == -1)
            return false;

        // Isolate the domain/machine name and get a list of mail exchangers
        String domain = address.substring(++pos);
        ArrayList mxList = null;
        try {
            mxList = getMX(domain);
        } catch (NamingException ex) {
            return false;
        }

        /*
        Just because we can send mail to the domain, doesn't mean that the
        address is valid, but if we can't, it's a sure sign that it isn't
        */
        if (mxList.size() == 0)
            return false;

        /* 
        Now, do the SMTP validation, try each mail exchanger until we get
        a positive acceptance. It *MAY* be possible for one MX to allow
        a message [store and forwarder for example] and another [like
        the actual mail server] to reject it. This is why we REALLY ought
        to take the preference into account.
        */
        for (int mx = 0; mx < mxList.size(); mx++) {
            boolean valid = false;
            try {
                int res;
                //
                Socket skt = new Socket((String) mxList.get(mx), 25);
                BufferedReader rdr = new BufferedReader(new InputStreamReader(skt.getInputStream()));
                BufferedWriter wtr = new BufferedWriter(new OutputStreamWriter(skt.getOutputStream()));

                res = hear(rdr);
                if (res != 220)
                    throw new Exception("Invalid header");
                say(wtr, "EHLO rgagnon.com");

                res = hear(rdr);
                if (res != 250)
                    throw new Exception("Not ESMTP");

                // validate the sender address
                say(wtr, "MAIL FROM: <tim@orbaker.com>");
                res = hear(rdr);
                if (res != 250)
                    throw new Exception("Sender rejected");

                say(wtr, "RCPT TO: <" + address + ">");
                res = hear(rdr);

                // be polite
                say(wtr, "RSET");
                hear(rdr);
                say(wtr, "QUIT");
                hear(rdr);
                if (res != 250)
                    throw new Exception("Address is not valid!");

                valid = true;
                rdr.close();
                wtr.close();
                skt.close();
            } catch (Exception ex) {
                // Do nothing but try next host
                ex.printStackTrace();
            } finally {
                if (valid)
                    return true;
            }
        }
        return false;
    }

    public static void main(String args[]) {
        String testData[] = { "rahul.saraswat@techblue.com", "rahul.saraswat@techblue.co.uk", "srswt.rahul12345@gmail.com",
        "srswt.rahul@gmail.com" };
        System.out.println(testData.length);
        for (int ctr = 0; ctr < testData.length; ctr++) {
            System.out.println(testData[ctr] + " is valid? " + isAddressValid(testData[ctr]));
        }
        return;
    }
}
公共类电子邮件测试{
私有静态int-hear(BufferedReader-in)引发IOException{
字符串行=null;
int res=0;
而((line=in.readLine())!=null){
字符串pfx=行。子字符串(0,3);
试一试{
res=整数.parseInt(pfx);
}捕获(例外情况除外){
res=-1;
}
如果(第3行字符)!='-')
打破
}
返回res;
}
私有静态void say(BufferedWriter wr,字符串文本)引发IOException{
wr.write(text+“\r\n”);
wr.flush();
返回;
}
@SuppressWarnings({“rawtypes”,“unchecked”})
私有静态ArrayList getMX(字符串主机名)引发NamingException{
//对域中的MX记录执行DNS查找
Hashtable env=新的Hashtable();
put(“java.naming.factory.initial”、“com.sun.jndi.dns.DnsContextFactory”);
DirContext ictx=新的初始DirContext(env);
Attributes attrs=ictx.getAttributes(主机名,新字符串[]{“MX”});
属性attr=attrs.get(“MX”);
//如果我们没有MX记录,请尝试机器本身
如果
public class EmailTest {
    private static int hear(BufferedReader in) throws IOException {
        String line = null;
        int res = 0;

        while ((line = in.readLine()) != null) {
            String pfx = line.substring(0, 3);
            try {
                res = Integer.parseInt(pfx);
            } catch (Exception ex) {
                res = -1;
            }
            if (line.charAt(3) != '-')
                break;
        }

        return res;
    }

    private static void say(BufferedWriter wr, String text) throws IOException {
        wr.write(text + "\r\n");
        wr.flush();

        return;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private static ArrayList getMX(String hostName) throws NamingException {
        // Perform a DNS lookup for MX records in the domain
        Hashtable env = new Hashtable();
        env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
        DirContext ictx = new InitialDirContext(env);
        Attributes attrs = ictx.getAttributes(hostName, new String[] { "MX" });
        Attribute attr = attrs.get("MX");

        // if we don't have an MX record, try the machine itself
        if ((attr == null) || (attr.size() == 0)) {
            attrs = ictx.getAttributes(hostName, new String[] { "A" });
            attr = attrs.get("A");
            if (attr == null)
                throw new NamingException("No match for name '" + hostName + "'");
        }
        /*
         Huzzah! we have machines to try. Return them as an array list
         NOTE: We SHOULD take the preference into account to be absolutely
         correct. This is left as an exercise for anyone who cares.
         */
        ArrayList res = new ArrayList();
        NamingEnumeration en = attr.getAll();

        while (en.hasMore()) {
            String mailhost;
            String x = (String) en.next();
            String f[] = x.split(" ");
            // THE fix *************
            if (f.length == 1)
                mailhost = f[0];
            else if (f[1].endsWith("."))
                mailhost = f[1].substring(0, (f[1].length() - 1));
            else
                mailhost = f[1];
            // THE fix *************
            res.add(mailhost);
        }
        return res;
    }

    @SuppressWarnings("rawtypes")
    public static boolean isAddressValid(String address) {
        // Find the separator for the domain name
        int pos = address.indexOf('@');

        // If the address does not contain an '@', it's not valid
        if (pos == -1)
            return false;

        // Isolate the domain/machine name and get a list of mail exchangers
        String domain = address.substring(++pos);
        ArrayList mxList = null;
        try {
            mxList = getMX(domain);
        } catch (NamingException ex) {
            return false;
        }

        /*
        Just because we can send mail to the domain, doesn't mean that the
        address is valid, but if we can't, it's a sure sign that it isn't
        */
        if (mxList.size() == 0)
            return false;

        /* 
        Now, do the SMTP validation, try each mail exchanger until we get
        a positive acceptance. It *MAY* be possible for one MX to allow
        a message [store and forwarder for example] and another [like
        the actual mail server] to reject it. This is why we REALLY ought
        to take the preference into account.
        */
        for (int mx = 0; mx < mxList.size(); mx++) {
            boolean valid = false;
            try {
                int res;
                //
                Socket skt = new Socket((String) mxList.get(mx), 25);
                BufferedReader rdr = new BufferedReader(new InputStreamReader(skt.getInputStream()));
                BufferedWriter wtr = new BufferedWriter(new OutputStreamWriter(skt.getOutputStream()));

                res = hear(rdr);
                if (res != 220)
                    throw new Exception("Invalid header");
                say(wtr, "EHLO rgagnon.com");

                res = hear(rdr);
                if (res != 250)
                    throw new Exception("Not ESMTP");

                // validate the sender address
                say(wtr, "MAIL FROM: <tim@orbaker.com>");
                res = hear(rdr);
                if (res != 250)
                    throw new Exception("Sender rejected");

                say(wtr, "RCPT TO: <" + address + ">");
                res = hear(rdr);

                // be polite
                say(wtr, "RSET");
                hear(rdr);
                say(wtr, "QUIT");
                hear(rdr);
                if (res != 250)
                    throw new Exception("Address is not valid!");

                valid = true;
                rdr.close();
                wtr.close();
                skt.close();
            } catch (Exception ex) {
                // Do nothing but try next host
                ex.printStackTrace();
            } finally {
                if (valid)
                    return true;
            }
        }
        return false;
    }

    public static void main(String args[]) {
        String testData[] = { "rahul.saraswat@techblue.com", "rahul.saraswat@techblue.co.uk", "srswt.rahul12345@gmail.com",
        "srswt.rahul@gmail.com" };
        System.out.println(testData.length);
        for (int ctr = 0; ctr < testData.length; ctr++) {
            System.out.println(testData[ctr] + " is valid? " + isAddressValid(testData[ctr]));
        }
        return;
    }
}