Java send email avoiding smtp relay server and sen

2019-05-30 23:26发布

问题:

I'm trying to send an email directly to the destination MX server, avoiding the relay smtp server. Teorically it could be possible getting the name servers list doing a query to a dns server. So, using this class, http://www.eyeasme.com/Shayne/MAILHOSTS/mailHostsLookup.html , I can get a list of the mail exchange servers of a domain.

So, once I have that, how can I proceed to send the email? I should use javax.mail or how? And if is it, how I should configure it?

回答1:

Okay, so suppose we do that.

We do DNS-Lookup to fetch MX records for recipient domain. Next step would be to connect to that server and deliver the message. As hosts operating as MX have to listen on port 25 and need to accept unencrypted communication, we could do it like that:

  • get MX host name
  • create Session with mail.smtp.host set to said server
  • send mail

What would we gain?

  • No more need for relay server.

What would we lose?

  • We will be slower (DNS-Lookup, connections to target host around the world)
  • We will have to do full error-handling (What if host is down? When do we retry?)
  • We will have to make it through spam prevention. So at the very least our server has to resolve back to the domain we send our emails from.

Conclusion: I woudn't do that. There are alternatives (install local sendmail/postfix whatever) that are perfectly able to do the hard SMTP work for us while still simplifying the work we need to do in Java to get the mail on its way.

Working example

Here's code that worked in sending me an email by using DNS resolved MX entry for gmail.com. Guess what happend? Got classified as SPAM because google said "it's most likely not from Jan"

import java.util.*;
import javax.mail.*;
import javax.mail.internet.*;
import javax.mail.internet.MimeMessage.RecipientType;
import javax.naming.*;
import javax.naming.directory.*;

public class DirectMail {

    public static void main(String[] args) {
        try {
            String[] mx = getMX("gmail.com");
            for(String mxx : mx) {
                System.out.println("MX: " + mxx);
            }
            Properties props = new Properties();
            props.setProperty("mail.smtp.host", mx[0]);
            props.setProperty("mail.debug", "true");
            Session session = Session.getInstance(props);
            MimeMessage message = new MimeMessage(session);
            message.setFrom("XXXXXXXXXXXXXXXXXXXX@gmail.com");
            message.addRecipient(RecipientType.TO, new InternetAddress("XXXXXXXXXXXXXXXXXXXX@gmail.com"));
            message.setSubject("SMTP Test");
            message.setText("Hi Jan");
            Transport.send(message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String[] getMX(String domainName) throws NamingException {
        Hashtable<String, Object> env = new Hashtable<String, Object>();

        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
        env.put(Context.PROVIDER_URL, "dns:");

        DirContext ctx = new InitialDirContext(env);
        Attributes attribute = ctx.getAttributes(domainName, new String[] {"MX"});
        Attribute attributeMX = attribute.get("MX");
        // if there are no MX RRs then default to domainName (see: RFC 974)
        if (attributeMX == null) {
            return (new String[] {domainName});
        }

        // split MX RRs into Preference Values(pvhn[0]) and Host Names(pvhn[1])
        String[][] pvhn = new String[attributeMX.size()][2];
        for (int i = 0; i < attributeMX.size(); i++) {
            pvhn[i] = ("" + attributeMX.get(i)).split("\\s+");
        }

        // sort the MX RRs by RR value (lower is preferred)
        Arrays.sort(pvhn, (o1, o2) -> Integer.parseInt(o1[0]) - Integer.parseInt(o2[0]));

        String[] sortedHostNames = new String[pvhn.length];
        for (int i = 0; i < pvhn.length; i++) {
            sortedHostNames[i] = pvhn[i][1].endsWith(".") ? 
                pvhn[i][1].substring(0, pvhn[i][1].length() - 1) : pvhn[i][1];
        }
        return sortedHostNames;     
    }
}