어떻게 메일 주소가 존재하는지 정확하게 판단합니까

10337 단어 SMTPJava
나는 몇 가지 우편물이 재발송되고 누출되는 해석을 총결하였다.인터넷;2. 방화벽;3. 서버의 자기 보호, 예를 들어 대량의 발송을 방지할 때 끊거나 스팸메일을 보내는 것을 방지한다. 나는 세 번째 해석이 믿을 만하다고 생각한다. 이런 문제에 대해 아래의 글에서 보완 조치를 제시했다.
회사 메일박스는 현재 Zimbra를 사용하고 있으며 이 메일 서버는 현재 매우 불안정하여 재발급, 누출 문제가 자주 발생한다.테스트 결과, 100통의 메일마다 98통 정도만 성공적으로 발송할 수 있으며, 다음은 테스트 데이터입니다.
테스트 용례 1:100봉, 총 사용 시 약:16min;실수 97통, 실패 3회, 오류 메시지 3회 모두:javax.mail.MessagingException: Could not connect to SMTP host
테스트 용례 2:100봉, 총 사용 시간: 16min;실수 100통, 실패 2회, 실수 동상.실패 재발급 메커니즘을 추가하고 실패 후 10s 재발급을 기다리며 최대 3회 재발급합니다.
테스트 용례 3: 한 통씩 보내고 10s에 머무르며 총 사용 시 32min;실수 100통, 실패 1회, 오류 동일;재발 메커니즘 동용례 2.
MessagingException에 대한 질문은 다음을 참조하십시오.
javax.mail.MessagingException: Could not connect to SMTP host
이런 문제에 대해 나는 메일 재발급을 늘렸다.

if(sendHtmlMail_(mail)){
    return true;
    } else{
    int i = 0;
    // , 
    boolean isNeedRe = isNeedRe(mail);
    while(!sendHtmlMail_(mail) && isNeedRe && i < 10){
    try {
    i++;
    Thread.sleep(1000*60);
    } catch (InterruptedException e) {
    LOGGER.error("resend mail error", e);
    }
    }
    return true;
    }
그러나 이런 메커니즘은 또 새로운 문제가 생겼다. 메일 서버의 불안정으로 인해 한 번만 보내는 상황에서도 메일 수신자에게 메일을 보낼 수 있고 같은 메일의 수신자(베끼기, 밀송 포함)는 일부 메일을 받을 수 있고 일부 메일을 받지 못할 수 있다.
상기 문제에 대하여 우리는 재발급 메커니즘을 제거하고 비합법적인 메일(즉 서버에 존재하지 않는 메일 주소)만 제거하고 제거한 후에 발송할 것이다.다른 원인으로 인한 메일 발송 실패에 대해서는 재발급을 하지 않는다. (이 문제는 메일 서버 운영 부서를 통해 제조업체에 반영될 것이다.)
다음은 메일의 합법성 여부를 판단하는 논리입니다.
1. SMTP는 두 가지 상황에서 작동한다. 하나는 전자메일이 클라이언트에서 서버로 전송되는 것이다.둘째, 한 서버에서 다른 서버로 전송
2. SMTP는 요청/응답 프로토콜로 명령과 응답은 ASCII 텍스트를 기반으로 하고 CR과 LF 문자로 끝납니다.응답은 되돌아오는 상태를 나타내는 세 자리 숫자 코드를 포함한다
3. SMTP는 TCP 프로토콜 25번 포트에서 연결 요청을 수신합니다.
4. 연결 및 전송 프로세스
SMTP 프로토콜은 복잡하지도 복잡하지도 않다, 간단하다, 만약 당신이 Socket을 안다면.그러나 지금은 우리가 이용하는 것이 첫 번째 조항에서 말한 바와 같다. 클라이언트에서 서버로 전송하면 우리가 한 서버에 메일을 보낼 때 메일 서버는 먼저 메일 주소가 본 서버에 실제로 존재하는지 검증한다. 
5단계는 다음과 같습니다.
서버에 연결된 25 포트 (메일 서비스가 없으면 연결도 백련)
helo 인사 보내기
메일from 명령을 보냅니다. 250을 되돌려주면 이 서버에 연결할 수 있습니다. 그렇지 않으면 서버가 보내는 사람의 검증을 필요로 합니다. 
rcpt to 명령을 보내고 250으로 되돌아오면 이메일이 존재합니다
quit 명령 보내기, 연결 종료
위의 논리에 근거하여 우리는 메일 서버를 봉인하여 Socket을 형성하고 명령을 발송하며 되돌아오는 값에 따라 메일 주소가 합법적인지 판단한다.
구체적인 코드는 다음과 같습니다.

import java.io.*;
import java.net.*;
import java.util.*;
import javax.naming.*;
import javax.naming.directory.*;
 
public class SMTPMXLookup {
  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
" ); wr.flush(); return; } 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; } 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: <[email protected]>" ); 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[] = { "[email protected]", "[email protected]", "[email protected]", // Invalid domain name "[email protected]", // Invalid address "[email protected]" // Failure of this method }; for ( int ctr = 0 ; ctr < testData.length ; ctr++ ) { System.out.println( testData[ ctr ] + " is valid? " + isAddressValid( testData[ ctr ] ) ); } return; } }
이상은 메일 주소가 합법적인지 아닌지를 판단하는 논리입니다. 만약 메일 주소가 합법적이지 않으면 메일 주소를 수신인 목록에서 제거합니다.

private static String[] removeInvalidateAddress(String[] addresses, String mailFrom) 
  {   
    ArrayList<String> validateAddresses = new ArrayList<String>(); 
    String normalAddress = null; 
    int code; 
      
    SMTPTransport smptTrans = null; 
    if(StringUtils.isEmpty(mailFrom) || null == addresses) 
    { 
      return new String[0]; 
    } 
    String sendCmd = "MAIL FROM:" + normalizeAddress(mailFrom); 
    try 
    { 
    smptTrans = (SMTPTransport)sendSession.getTransport("smtp"); 
    smptTrans.connect(); 
    code = smptTrans.simpleCommand(sendCmd); 
    if(code != 250 && code != 251) 
    { 
      logger.error("send from invalidate" + mailFrom); 
    } 
    else 
    { 
      for(String address : addresses) 
      { 
        normalAddress = normalizeAddress(address); 
        String cmd = "RCPT TO:" + normalAddress; 
    code = smptTrans.simpleCommand(cmd); 
    if(code == 250 || code == 251) 
    { 
      validateAddresses.add(address); 
    } 
      } 
    } 
    } 
    catch(MessagingException e) 
    { 
      logger.error("Validate mail address error. send from " + mailFrom, e); 
    } 
      
    String[] result = validateAddresses.toArray(new String[validateAddresses.size()]); 
    return result; 
  } 
    
  private static String normalizeAddress(String addr)  
  { 
    if ((!addr.startsWith("<")) && (!addr.endsWith(">"))) 
      return "<" + addr + ">"; 
    else 
      return addr; 
  }

이상은 본문의 전체 내용입니다. 여러분이 이해해 주시면 도움이 될 것입니다.

좋은 웹페이지 즐겨찾기