无须SMTP服务器中转直接发送电子邮件.docx
- 文档编号:11269825
- 上传时间:2023-02-26
- 格式:DOCX
- 页数:12
- 大小:88.05KB
无须SMTP服务器中转直接发送电子邮件.docx
《无须SMTP服务器中转直接发送电子邮件.docx》由会员分享,可在线阅读,更多相关《无须SMTP服务器中转直接发送电子邮件.docx(12页珍藏版)》请在冰豆网上搜索。
无须SMTP服务器中转直接发送电子邮件
无须SMTP服务器中转直接发送电子邮件
前言
大伙儿一定熟悉Foxmail中的“特快专递”,它能直截了当将电子邮件发送到对方的邮件服务器中,而不需要通过SMTP服务器中转,如此做有什么好处?
第一:
发送速度比较快,不需要等SMTP服务器对邮件进行查毒、派发、验证;第二:
你能够及时把握邮件是否发送成功的信息。
有时我们用Outlook发送一封邮件,到翌日对方都没收到,可我这边确实差不多发送成功了,只好让对方多收几次,到了第三天SMTP服务器回信说“不行意思,你发往XXX的邮件因为XXX缘故未能送达……”,原先邮件被打回来了,专门最近163邮箱专门离谱,我发出去的10封邮件,至少有3封会被无故打回来,说什么“网络连接失败”因此被打回,莫名其妙,可能我是免费邮箱的缘故吧,没方法只好再申请多几个邮箱,我现在差不多有“chrys@21cn、chrys.xie@gmail、hwxie@ust.hk……”好多邮箱了,确实是为了防止给别人发邮件时被无故退回……扯远了,不行意思。
第三:
我们有时需要在程序里将某些敏锐信息发送至公司邮箱,例如:
运算注册码时我们需要用户操作我们的软件将申请注册的信息发送回我们的售后服务邮箱,由我们的工作人员处理来这些邮件。
大伙儿一定会想用SMTP(SimpleMailTransferProtocol)借助SMTP服务器也能通过程序实现邮件发送,然而有一个专门大问题确实是安全问题,专门多闻名的邮件服务器运营商关于用软件方式通过SMTP协议频繁提交邮件转发的申请是不欢迎的,我的163邮箱就曾经深受其害,我那次是在写SMTP客户端发送邮件的程序,顺手就用了163的SMTP服务器,我刚发到第5封邮件时就发送失败了,我再登录163网站一查,原先我的账号被封了,缘故确实是我用软件发送邮件太多了(天啦,才5封而已啊),后来我花了近两个月时刻跟新浪公司又赔礼又道歉,还把身份证过去了我的账号才被复原。
剖析邮件传送过程
废话说太多请别介意,现在言归正传,要直截了当将邮件送到对方(POP或IMAP)服务器上,而不通过SMTP邮件服务器转交,事实上也不难,你只要改用Unix/Linux操作系统,直截了当SendMail命令就能完成,但在Windows下想要实现那个功能可能得花一点心思了。
我们第一要从协议RFC821-SimpleMailTransferProtocol入手来分析。
第一我们看一下Email的递送过程:
邮件原文→编码→SMTP客户端→SMTP转交服务器→远程SMTP服务器(对方邮局)。
“特快专递”的实现思路
邮件编码后被递送到一个SMTP转交服务器上,该服务器对信件分检(到同一邮局的被放在一起)后,依照优先级以及信件的先后次序被发送到远程邮局的SMTP服务器上。
换句话说,只要我们明白了SMTP转交服务器是如何确定远程邮局SMTP服务器的地址的,就能够直截了当递送到远程邮局服务器。
SMTP转交服务器又是明白远程邮局的地址呢?
这确实是域名解析所完成的工作了,就好比我们在IE扫瞄器输入“”那个域名,IE扫瞄器又如何明白目标服务器的IP地址呢?
也是域名解析服务器的功劳。
电子邮件地址由两部分组成,例如:
chrys@163,那个地点的chrys是邮箱名(即用户名,一个用户对应一个邮箱),163是邮箱服务器地址,邮箱名和邮箱服务器地址之间以“@”作为分隔。
我们只要向域名服务器发送查询“163”的远程邮局服务器地址便可找到远程邮局SMTP服务器的IP地址,该查询指令被称作MX(MailExchange)邮件交换服务器的地址查询。
远程邮局SMTP服务器的地址可能不止一个,这时,你可依照信件优先级的不同来选择对应的远程邮局,我为了安全起见会对每一个远程邮局服务器按照等级高低逐一尝试,只要将邮件成功地发送到其中一个邮局我们的任务就完成了。
我们要完成几项编程工作:
本机DNS的猎取、与DNS服务器通信实现MX指令查询、SMTP邮件提交,下面我们一一阐述。
猎取本机DNS
代码中我封装了一个类CnetAdapterInfo,该类能够猎取本机网卡的系列信息,包括本机IP地址、子网掩码、DNS、Wins、网卡MAC地址等相关信息。
第一我们需要调用IPHelpAPI库中的GetAdaptersInfo()函数来猎取系统中所有网卡信息。
DWORDGetAdaptersInfo(
__outPIP_ADAPTER_INFOpAdapterInfo,
__inoutPULONGpOutBufLen
);
该函数有两个参数,pAdapterInfo是一个指针,指向一个用户定义的结构体,一样是用HeapAlloc()申请的内存空间,pOutBufLen传入pAdapterInfo所指空间的大小,传出实际需要的缓冲大小,第一次调用该函数时pOutBufLen传入0,函数将返回ERROR_BUFFER_OVERFLOW表示需要更多的缓冲,并将实际需要的缓冲长度返回,我们依照实际长度用HeapAlloc()函数申请空间再次调用该函数,以下代码是枚举所有网卡并将信息储存到数组m_Ary_NetAdapterInfo中:
#defineMALLOC(bytes):
:
HeapAlloc(:
:
GetProcessHeap(),HEAP_ZERO_MEMORY,(bytes))
#defineFREE(ptr) if(ptr):
:
HeapFree(:
:
GetProcessHeap(),0,ptr)
#defineREMALLOC(ptr,bytes):
:
HeapReAlloc(:
:
GetProcessHeap(),HEAP_ZERO_MEMORY,ptr,bytes)
//
//枚举网络适配器
//return:
------------------------------------------------------------
// -1 - 失败
// >=0- 网络适配器数量
//
intCNetAdapterInfo:
:
EnumNetworkAdapters()
{
DeleteAllNetAdapterInfo();
IP_ADAPTER_INFO*pAdptInfo=NULL;
IP_ADAPTER_INFO*pNextAd =NULL;
ULONGulLen =0;
intnCnt =0;
DWORDdwError=:
:
GetAdaptersInfo(pAdptInfo,&ulLen);
if(dwError!
=ERROR_BUFFER_OVERFLOW)return-1;
pAdptInfo=(IP_ADAPTER_INFO*)MALLOC(ulLen);
dwError=:
:
GetAdaptersInfo(pAdptInfo,&ulLen);
if(dwError!
=ERROR_SUCCESS)return-1;
pNextAd=pAdptInfo;
while(pNextAd)
{
COneNetAdapterInfo*pOneNetAdapterInfo=newCOneNetAdapterInfo(pNextAd);
if(pOneNetAdapterInfo)
{
m_Ary_NetAdapterInfo.Add(pOneNetAdapterInfo);
}
nCnt++;
pNextAd=pNextAd->Next;
}
//freeanymemoryweallocatedfromtheheapbefore
//exit. wewouldn'twannaleavememoryleaksnowwouldwe?
;p
FREE(pAdptInfo);
returnnCnt;
}针对每个网卡信息,我们需要调用GetPerAdapterInfo()函数来猎取指定网卡的DNS信息,使用方法和GetAdaptersInfo()类似。
以下代码猎取网卡差不多信息:
//
//依照传入的pAdptInfo信息来猎取指定网卡的差不多信息
//
BOOLCOneNetAdapterInfo:
:
Init()
{
IP_ADDR_STRING*pNext =NULL;
IP_PER_ADAPTER_INFO*pPerAdapt =NULL;
ULONGulLen =0;
DWORDdwErr=ERROR_SUCCESS;
ASSERT(m_AdptInfo.AddressLength>0);
t_IPINFOiphold;
//将变量清空
m_bInitOk=FALSE;
m_csName.Empty();
m_csDesc.Empty();
m_CurIPInfo.csIP.Empty();
m_CurIPInfo.csSubnet.Empty();
m_Ary_IP.RemoveAll();
m_Ary_DNS.RemoveAll();
m_Ary_Gateway.RemoveAll();
#ifndef_UNICODE
m_csName =m_AdptInfo.AdapterName;
m_csDesc =m_AdptInfo.Description;
#else
USES_CONVERSION;
m_csName =A2W(m_AdptInfo.AdapterName);
m_csDesc =A2W(m_AdptInfo.Description);
#endif
//猎取当前正在使用的IP地址
if(m_AdptInfo.CurrentIpAddress)
{
m_CurIPInfo.csIP =m_AdptInfo.CurrentIpAddress->IpAddress.String;
m_CurIPInfo.csSubnet =m_AdptInfo.CurrentIpAddress->IpMask.String;
}
else
{
m_CurIPInfo.csIP =_T("0.0.0.0");
m_CurIPInfo.csSubnet =_T("0.0.0.0");
}
//猎取本网卡中所有的IP地址
pNext=&(m_AdptInfo.IpAddressList);
while(pNext)
{
iphold.csIP =pNext->IpAddress.String;
iphold.csSubnet =pNext->IpMask.String;
m_Ary_IP.Add(iphold);
pNext=pNext->Next;
}
//猎取本网卡中所有的网关信息
pNext=&(m_AdptInfo.GatewayList);
while(pNext)
{
m_Ary_Gateway.Add(pNext->IpAddress.String);
pNext=pNext->Next;
}
//猎取本网卡中所有的DNS
dwErr=:
:
GetPerAdapterInfo(m_AdptInfo.Index,pPerAdapt,&ulLen);
if(dwErr==ERROR_BUFFER_OVERFLOW)
{
pPerAdapt=(IP_PER_ADAPTER_INFO*)MALLOC(ulLen);
dwErr=:
:
GetPerAdapterInfo(m_AdptInfo.Index,pPerAdapt,&ulLen);
//ifwesucceedthanweneedtodropintoourloop
//andfillthednsarraywillallavailableIP
//addresses.
if(dwErr==ERROR_SUCCESS)
{
pNext=&(pPerAdapt->DnsServerList);
while(pNext)
{
m_Ary_DNS.Add(pNext->IpAddress.String);
pNext=pNext->Next;
}
m_bInitOk=TRUE;
}
//thisisdoneoutsidethedwErr==ERROR_SUCCESjustincase.themacro
//usesNULLpointercheckingsoitisokifpPerAdaptwasneverallocated.
FREE(pPerAdapt);
}
returnm_bInitOk;
}
至此我们差不多猎取到系统中所有DNS服务器地址了。
MX指令查询猎取远程邮局地址
与DNS服务器通信事实上确实是一个简单的UDP网络通信,端口号为53,通信的数据格式如下:
所有的DNS消息差不多上差不多上相同的数据结构,但DNSRR是采纳了其他的数据结构。
QNAME是一个表示域长度的变量,表示每一节有多少字节,例如:
sockets将表示为:
最后的“Additional”通常包含了查询服务器期望被发送的纪录以减少通信量,例如,回应MX查询时通常在“Additional”中包含‘A’纪录。
具体的MX查询过程请参加源代码,以下代码实现了猎取本机所有DNS,然后逐一尝试MX查询的方法:
//
//尝试所有的DNS来查询邮局服务器地址
//
BOOLGetMX(
char*pszQuery, //要查询的域名
OUTt_Ary_MXHostInfos&Ary_MXHostInfos //输出MailExchange主机名
)
{
CNetAdapterInfom_NetAdapterInfo;
m_NetAdapterInfo.Refresh();
intnNetAdapterCount=m_NetAdapterInfo.GetNetCardCount();
for(inti=0;i { COneNetAdapterInfo*pOneNetAdapterInfo=m_NetAdapterInfo.Get_OneNetAdapterInfo(i); if(pOneNetAdapterInfo) { intnDNSCount=pOneNetAdapterInfo->Get_DNSCount(); for(intj=0;j { CStringcsDNS=pOneNetAdapterInfo->Get_DNSAddr(j); if(GetMX(pszQuery,csDNS.GetBuffer(0),Ary_MXHostInfos)) returnTRUE; } } } returnFALSE; } 假如查询“gmail”的邮局服务器地址,将得到如下的结果: gsmtp147.google50 gsmtp183.google50 gmail-smtp-in.l.google5 alt1.gmail-smtp-in.l.google10 alt2.gmail-smtp-in.l.google10 用SMTP协议给远程邮局直截了当发送邮件 SMTP是一个简单邮件传输协议,通过TCP连接服务器的25端口号即可进行数据通信,以下是我用telnet手工发送邮件的过程: 其中红色矩形框起来的是服务器回应的数据,绿色矩形框起来的是我手工输入的数据,那个地点发送的邮件内容为“我是手工发送的电子邮件”,邮件被直截了当发送到chrys.xie@gmail邮箱中,不需要厌恶的SMTP服务器中转,因此,因为这是手工发送的邮件,其内容未通过任何MIME编码,这封邮件能够被Foxmail或Outlook收到,但可能被判为垃圾邮件,因为这封邮件连标题都没有,是无头苍蝇,确信是垃圾,呵呵……关于邮件内容的编码请参考其他相关资料,我有一本书,名叫《VisualC++网络通信协议分析与应用实现》,这本书有详细的电子邮件编码介绍,能够下载电子文档看看。 当我们明白了SMTP通信的全过程,再编写一个TCP网络通信程序处理与SMTP服务器要求就不是难事了。 本代码中的CHwSMTP类差不多封装了整个通信过程,能够发送一般的电子邮件,还能够发送带附件的电子邮件,配合DNS查找,远程邮局地址MX查询便可实现任意邮件直截了当发送到对方邮箱的功能。 软件操作界面介绍 程序执行后界面如下: From中能够输入一个虚假的邮箱地址,也能够输入真实的邮箱地址(如: 21cn的邮箱),但不能输入163的邮箱,否则发送会失败,163邮箱不太欢迎大伙儿用软件方式进行邮件收发,人家要靠那个吃饭嘛。 注意事项 看到那个地点是否差不多专门兴奋了,想着要自己写一个SMTP服务器,甚至想要写一个邮局服务器程序,但我觉得可能还没那么容易,我用那个软件给我的google邮箱(gmail)发送邮件,专门快就能成功收到了,可我尝试过用这种方式给163邮箱和21cn邮箱发送邮件时却失败了,发给163时服务器说你的IP不被承诺,提示信息如下: 550-5.7.1[116.25.186.155]TheIPyou'reusingtosendmailisnotauthorized 550-5.7.1tosendemaildirectlytoourservers.PleaseusetheSMTP 看来163邮箱只接收大牌邮件服务器发过来的邮件,难怪我们这些免费的163用户发送邮件经常会被退回,原先163服务器还认牌子的,faint! 到底要如何样做才能够直截了当给163等闻名的邮局发邮件呢? 不明白用IP欺诈方式能否成功,请有高手明白解决那个问题的一定要告诉我啊,我的邮箱是: chrys@163,先谢过了! 电子邮件在目前的Internet上被广泛地使用,为了安全专门多邮局服务器做了安全认证等诸多限制,我们要想让自己的SMTP服务器能向所有的邮局发邮件,可能还得做更多的努力。 终止语 知识确实是力量,知识共享将具有推动时代进步的力量。 期望我能为中国的软件行业尽一份薄力。 你能够任意修改复制本代码,但请保留版权信息文字不要修改。 由于水平有限,错误再所难免,请知情者原谅并告知,多谢!
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 无须 SMTP 服务器 中转 直接 发送 电子邮件