개발자의 길

mx를 이용한 smtp 메일존재여부 EmailValidator체크 본문

4. JAVA

mx를 이용한 smtp 메일존재여부 EmailValidator체크

자르르 2010. 5. 24. 11:57



 

전자메일을 검증하기 위한 몇가지 방법이 있다.

 

1. 가장 간단한 @ 또는 .이 있는지 정규식으로 검사하는 방법

 

2. E-mail로 인증코드를 보내서 재확인하는 방법

 

3. E-mail이 존재하는지 실제로 MX서버에서 체크하는 방법 

 

이렇게 있다.

 

1, 2는 간단한 코딩으로 끝나고 자주 이용하지만, 3번을 하려면 막막하다...

이미 입력된 이용자의 e-mail을 검증하기 위한 방법도 3번이 가장 적합하다.

 

이럴때, 3번을 구현해서 사용하면 매우 편할것이다.

다음 소스는 http://www.rgagnon.com에서 가져다가 버그가 생길 소지가 있는 부분을 수정해 놨으며, 대충 주석도 번역해 놓았다...

 

유용하게 쓰자~!

package com;

import java.io.*;
import java.net.*;
import java.util.*;
import javax.naming.*;
import javax.naming.directory.*;

public class EMailValidator {
    /**
     * SMTP 서버의 메시지 스트림에서 PREFIX (결과값) 내용을 읽어온다.
     * 
     * @param in
     * @return
     * @throws IOException
     */
    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;
    }

    /**
     * 소켓에 메시지를 보낸다.
     * 
     * @param wr
     * @param text
     * @throws IOException
     */
    private static void say(BufferedWriter wr, String text) throws IOException {
        wr.write(text + "\r\n");
        wr.flush();
    }

    private static ArrayList getMX(String hostName) throws NamingException {
        // 도메인에서 MX레코드를 찾기를 시도
        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");

        // 만약 MX레코드가 없으면, 그 자신 MX서버인지 시도해 본다.
        if ((attr == null) || (attr.size() == 0)) {
            attrs = ictx.getAttributes(hostName, new String[] { "A" });
            attr = attrs.get("A");
            if (attr == null)
                throw new NamingException("호스트명이 잘못되었습니다. '" + hostName + "'");
        }
        // 발견한 경우에 다음을 수행한다.
        ArrayList res = new ArrayList();
        NamingEnumeration en = attr.getAll();

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

    /**
     * 메일이 유효한가 여부를 확인하는 메서드
     * 
     * @param address
     * @return
     */
    public static boolean isAddressValid(String address) {
        // 도메인네임 구분자'@'위치를 찾는다.
        int pos = address.indexOf('@');

        // 없다면, 잘못된 e-mail
        if (pos == -1)
            return false;
        // 메일 주소를 찾기위해서 도메인명 만을 구한다.
        String domain = address.substring(++pos);
        ArrayList mxList = null;
        try {
            // DNS에서 MX레코드를 찾는다.
            mxList = getMX(domain);
        } catch (NamingException ex) {
            return false;
        }

        if (mxList.size() == 0)
            return false;
        // 각각의 MX에 SMTP 유효성 체크를 한다.
        for (int mx = 0; mx < mxList.size(); mx++) {
            boolean valid = false;
            Socket skt = null;
            BufferedReader rdr = null;
            BufferedWriter wtr = null;
            try {
                int res;
                skt = new Socket((String) mxList.get(mx), 25);
                rdr = new BufferedReader(new InputStreamReader(skt
                        .getInputStream()));
                wtr = new BufferedWriter(new OutputStreamWriter(skt
                        .getOutputStream()));
                res = hear(rdr);
                if (res != 220) {
                    throw new Exception("SMTP 메시지 Header가 잘못되었습니다.");
                }
                say(wtr, "EHLO " + domain);
                res = hear(rdr);
                if (res == 500) {
                    System.out.println("HELO로 재시도합니다.");
                    say(wtr, "HELO " + domain);
                    res = hear(rdr);
                    if (res != 250)
                        throw new Exception("ESMTP가 아닙니다.");
                }
                if (res != 250) {
                    throw new Exception("ESMTP가 아닙니다.");
                }
                say(wtr, "MAIL FROM: <" + address + ">");
                res = hear(rdr);
                if (res != 250) {
                    throw new Exception("발송 거부되었습니다.");
                }
                say(wtr, "RCPT TO: <" + address + ">");
                res = hear(rdr);
                say(wtr, "RSET");
                try {
                    hear(rdr);
                } catch (Exception e) {
                }
                say(wtr, "QUIT");
                // hear(rdr); // quit하는 경우 수신을 하지 않아도 무방하다.
                if (res != 250) {
                    throw new Exception("메일주소가 잘못되었습니다. (서버에서 수신자 없음 메시지 리턴)");
                }
                valid = true;
            } catch (Exception ex) {
                System.err.println(ex.getMessage());
            } finally {
                if (rdr != null) {
                    try {
                        rdr.close();
                    } catch (IOException e) {
                    }
                }
                if (wtr != null) {
                    try {
                        wtr.close();
                    } catch (IOException e) {
                    }
                }
                if (skt != null) {
                    try {
                        skt.close();
                    } catch (IOException e) {
                    }
                }
            }
            if (valid)
                return true;
        }
        return false;
    }

    public static void main(String args[]) {
        String testData[] = { "pigmon@kornet.net" };

        for (int ctr = 0; ctr < testData.length; ctr++) {
            System.out.println(testData[ctr] + " : "
                    + isAddressValid(testData[ctr]));
        }
        return;

    }
}



이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
공유하기 링크
Comments