Java 发送邮件避开smtp中继服务器直接发送到MX服务器
Java send email avoiding smtp relay server and send directly to MX server
我正在尝试将电子邮件直接发送到目标 MX 服务器,避免中继 smtp 服务器。
从理论上讲,可以让名称服务器列表对 dns 服务器进行查询。
因此,使用 class, http://www.eyeasme.com/Shayne/MAILHOSTS/mailHostsLookup.html ,我可以获得域的邮件交换服务器列表。
那么,一旦我有了它,我该如何继续发送电子邮件?我应该使用 javax.mail 或者如何使用?如果是,我应该如何配置它?
好的,假设我们这样做。
我们执行 DNS 查找以获取收件人域的 MX 记录。下一步是连接到该服务器并传递消息。由于作为 MX 运行的主机必须监听端口 25 并且需要接受未加密的通信,我们可以这样做:
- 获取 MX 主机名
- 创建
Session
并将 mail.smtp.host
设置为所述服务器
- 发送邮件
我们会得到什么?
- 不再需要中继服务器。
我们会失去什么?
- 我们会更慢(DNS 查找,连接到世界各地的目标主机)
- 我们将不得不完整错误处理(如果主机宕机怎么办?我们什么时候重试?)
- 我们必须通过防止垃圾邮件来实现它。所以至少我们的服务器必须解析回我们发送电子邮件的域。
结论:我不会那样做。有一些替代方案(安装本地 sendmail/postfix 任何东西)完全能够为我们完成繁重的 SMTP 工作,同时仍然简化我们需要在 Java 中完成的工作以获取邮件。
工作示例
这是使用 gmail.com 的 DNS 解析 MX 条目向我发送电子邮件的代码。猜猜发生了什么?被分类为 SPAM 因为 google 说 "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;
}
}
我正在尝试将电子邮件直接发送到目标 MX 服务器,避免中继 smtp 服务器。 从理论上讲,可以让名称服务器列表对 dns 服务器进行查询。 因此,使用 class, http://www.eyeasme.com/Shayne/MAILHOSTS/mailHostsLookup.html ,我可以获得域的邮件交换服务器列表。
那么,一旦我有了它,我该如何继续发送电子邮件?我应该使用 javax.mail 或者如何使用?如果是,我应该如何配置它?
好的,假设我们这样做。
我们执行 DNS 查找以获取收件人域的 MX 记录。下一步是连接到该服务器并传递消息。由于作为 MX 运行的主机必须监听端口 25 并且需要接受未加密的通信,我们可以这样做:
- 获取 MX 主机名
- 创建
Session
并将mail.smtp.host
设置为所述服务器 - 发送邮件
我们会得到什么?
- 不再需要中继服务器。
我们会失去什么?
- 我们会更慢(DNS 查找,连接到世界各地的目标主机)
- 我们将不得不完整错误处理(如果主机宕机怎么办?我们什么时候重试?)
- 我们必须通过防止垃圾邮件来实现它。所以至少我们的服务器必须解析回我们发送电子邮件的域。
结论:我不会那样做。有一些替代方案(安装本地 sendmail/postfix 任何东西)完全能够为我们完成繁重的 SMTP 工作,同时仍然简化我们需要在 Java 中完成的工作以获取邮件。
工作示例
这是使用 gmail.com 的 DNS 解析 MX 条目向我发送电子邮件的代码。猜猜发生了什么?被分类为 SPAM 因为 google 说 "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;
}
}