从2012年注册以来,一直潜水到现在,现在,最终还是决定回到看雪妈妈的怀抱,在社区发表一些文章。
首先说明一下,现在大多数人都用oracle的javamail的jar来实现发邮件,在这里我不得不承认javamail确实是一款强大的工具,但是,oracle对它进行了底层封装,对于开发者来说是透明的,因此,也许是出于安全的考虑,你是不能够随意设置mail from的。那该怎么办呢?对于聪明的程序员来说,这段问题是难不倒的,各位看官看看我是怎么实现的。
一封邮件的底层实现过程是怎么样的呢?
在此,以windows系统为例:
1)进入字符模式
2)启动telnet程序:telnet smtp.163.com 25
登录成功后邮件服务器会反馈一个信息“RESPONSE:220 xxxx ESMTP sendmail xxxxx”
3)输入:HELO abc.cn(想要伪造邮件的地址域名,比如163邮箱:163.com.cn)
系统会反馈一个信息“Response: 250 abc.cn Hello [xxx.xxx.xxx.xxx],pleased to meet you”表示邮件系统认可
4)输入:MAIL FROM :xxxx@163.com (不能缺少“:”号,下面同样,邮件发送者的地址)
系统反馈信息“Response: 250 2.1.0 <xxxx@163.com> …. Sender ok”
注意:此处的地址不能伪造,不然通不过邮箱服务器的认证。
5)输入: RCPT TO :xxxx@qq.com (邮件接收者的地址)
系统反馈信息“Response: 250 2.1.0 <xxxx@qq.com> …. Recipient ok”
6)输入:DATA 回车
from:xxx(这里可以随意伪造)
to:xxx(这里可以随意伪造)
(开始输入邮件正文,完成后一定要“回车”之后输入“.”号)
.(注意是英文状态下的“.”号,表示邮件输入完毕,最后再“回车”)
7)输入:
QUIT (邮件内容输入完成,退出)
系统反馈信息“Response: 221 2.0.0 ab.cn closing connection”
到此为止邮件发送完成
下面就是我用java代码来进行的底层实现:
//邮件
class MyMail {
String from;
String to;
String subject;
String content;
String userName;
String pwd;
public MyMail(String from, String to, String subject, String content, String userName, String pwd) {
this.from = from;
this.to = to;
this.subject = subject;
this.content = content;
this.userName = this.toBASE64(userName);
this.pwd = this.toBASE64(pwd);
}
/**
* 在 MyMail 类中进行用户名、密码的转码工作
*/
private String toBASE64(String str) {
return (new sun.misc.BASE64Encoder().encode(str.getBytes()));
}
}
//简单的邮件发送端类,实现发送功能
public class FakeMailSender {
private String smtpServer;
private int port = 25;
private Socket socket;
BufferedReader br;
PrintWriter pw;
/**
* 根据发件人的邮箱地址确定SMTP邮件服务器
*/
private void initServer(String from) {
if(from.contains("@163")) {
this.smtpServer = "smtp.163.com";
}else if(from.contains("@126")) {
this.smtpServer = "smtp.126.com";
}else if(from.contains("@sina")) {
this.smtpServer = "smtp.sina.com";
}else if(from.contains("@qq")) {
this.smtpServer = "smtp.qq.com";
}
}
public void sendEmail(MyMail email) {
try {
this.initServer(email.from);
this.socket = new Socket(smtpServer, port);
this.br = this.getReader(socket);
this.pw = this.getWriter(socket);
// 开始组装发送邮件的命令序列
send_Receive(null); // 接收连接SMTP服务器成功的信息
send_Receive("ehlo hao");
send_Receive("auth login");
send_Receive(email.userName);
send_Receive(email.pwd);
send_Receive("mail from:<" + email.from + ">");
send_Receive("rcpt to:<" + email.to + ">");
send_Receive("data");
// 邮件内容
pw.println("from:" + email.from);
pw.println("to:" + email.to);
// 主题与正文之间一定要空一行,即加上"\r\n"
pw.println("subject:" + email.subject + "\r\n");
// 在控制台打印邮件内容
System.out.println("from:" + email.from);
System.out.println("to:" + email.to);
System.out.println("subject:" + email.subject + "\r\n");
System.out.println(email.content);
// 邮件正文
pw.println(email.content);
// 一定记得正文以"."结束
send_Receive(".");
send_Receive("quit");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (socket != null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 每发送一条命令,必须在命令后面加上"\r\n",
* 则同时打印出smtp邮件服务器的相应状态码
* @param command
*/
private void send_Receive(String command) throws IOException{
if(command != null) {
// 向SMTP邮件服务器发送命令,一定要记得加上"\r\n"
pw.print(command + "\r\n");
pw.flush();
System.out.println("用户 >> " + command);
}
char [] response = new char[1024];
br.read(response);
System.out.println(response);
}
/**
* 获取 Socket 的输出流
*/
private PrintWriter getWriter(Socket socket) throws IOException {
OutputStream socketOut = socket.getOutputStream();
return new PrintWriter(socketOut, true);
}
/**
* 获取 Socket 的输入流
*/
private BufferedReader getReader(Socket socket) throws IOException {
InputStream socketIn = socket.getInputStream();
return new BufferedReader(new InputStreamReader(socketIn));
}
// 测试
public static void main(String[] args) {
MyMail email = new MyMail("xxxx@163.com", "xxxxx@qq.com", "test", "this is a joke for fun!", "xxxx", "xxxxx");
new FakeMailSender().sendEmail(email);
}
}
到此就结束了哈!!!
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课