R
riceball
Unregistered / Unconfirmed
GUEST, unregistred user!
许多时候,我想不通为什么不能直接将信件送到对方(POP或IMAP)服务器上,
为什么非要通过一个莫名其妙的SMTP邮件服务器转交一次,实在不服气,如果
使用UNIX,这个问题就很好解决,用sendmail就能完成递送任务,但在Windows
下呢?同样的,一定也有办法饶开SMTP Server直接递送到对方的远程邮局服务
器上,难得住别人,难得住我们程序员吗?分析相关协议(RFC2645,RFC821,
RFC1846,RFC1939,RFC1725,RFC1730-RFC1733 etc.),我们会知道要完成直接递
送其实相当简单。
首先我们看一下Email的递送过程:
Email(Encode) -> a SMTP Relay Server -> Remote SMTP Server(远程邮局)。
非常简单,邮件编码后被递送到一个SMTP转交服务器上,该服务器对信件分检
(到同一邮局的被放在一起)后,根据优先级以及信件的先后次序被发送到远程
邮局的SMTP服务器上。换句话说,只要我们知道了SMTP转交服务器是如何确定远
程邮局SMTP服务器的地址的,就可以轻松地将饶开SMTP Relay Server直接递送
到远程邮局服务器。
SMTP Relay Server是如何确定远程邮局服务器的地址的呢?如果你熟悉域名解
析,就知道是怎么回事了,我们知道电子邮件的地址由两部分构成postbox@address.com,
邮箱(postbox)和地址(address.com),给域名服务器发送指令查询“address.com”
的远程邮局服务器的地址即可找到远程邮局SMTP服务器的IP 地址,该指令查询是
被称作MX(Mail Exchange)邮件交换服务器的地址查询。远程邮局SMTP服务器的地
址可能不止一个,这时,你可根据信件优先级的不同,将对应优先级的信件发到
对应地址的远程邮局SMTP服务器,当然,你也可以不管三七二十一,随便选一个
SMTP服务器发送,见后附“域名解析结果样例”。简单吧。这下,自己编写一个
SMTP Server不难了吧!
下面我将结合一个实例介绍:
说明
域名查询使用 Indy 的 TIdDNSResolver 元件实现,至于邮件发送使用 INDY 的 SMTP Sender(TIdSMTP)元件。Delphi 5 下测试通过。
注:Indy 8.006B 存在 bug 使得TIdDNSResolver.ResolveDomain方法没有工作,在8.007b修复了该错误。
如何进行域名查询
长话短说,很简单,在窗体上放上一个 TIdDNSResolver(DnsREsolver) 元件,在程序中设置它的查询类型属性为邮件服务器 RequestedRecords := [cMX],执行ResolveDomain('xxx.com')方法即可获得域名查询结果,如果没有出错,域名查询结果就会被放在DNSAnList、DNSARList、DNSNSList属性中。
域名服务器查询类型,注意,下面的查询类型不是所有的域名服务器都支持:
cA = 1; // 查询主机地址
cNS = 2; // 查询域名服务器
cMD = 3; // A mail destination obsolete use MX (已废弃)
cMF = 4; // A mail forwarder obsolete use MX (已废弃)
cName = 5; // The canonical name for an alias
cSOA = 6; // Marks the start of a zone of authority
cMB = 7; // A mail box domain name (Experimental)
cMG = 8; // A mail group member (Experimental)
cMR = 9; // A mail Rename Domain Name (Experimental)
cNULL = 10; // RR (Experimental)
cWKS = 11; // A well known service description
cPTR = 12; // A Domain Name Pointer;
cHINFO = 13; // Host Information;
cMINFO = 14; // Mailbox or Mail List Information;
cMX = 15; // 邮件服务器
cTXT = 16; // Text String;
cAXFR = 252; // A Request for the Transfer of an entire zone;
cMAILB = 253; // A request for mailbox related records (MB MG OR MR}
cMAILA = 254; // A request for mail agent RRs (已废弃 see MX)
cStar = 255; // A Request for all Records
DNSAnList means an Answer List, DNSARList means an Additional Response List, DNSNSList means an Authority List. 下面我们通过一个例子来看看如何进行域名查询:
unit ResolverTest;
interface
uses
Windows,
Messages,
SysUtils,
Classes,
Graphics,
Controls,
Forms,
Dialogs,
StdCtrls,
Buttons,
ExtCtrls,
ComCtrls,
IdDNSResolver;
type
TResolverForm = class(TForm)
ConnectBtn: TBitBtn;
Memo: TMemo;
Edit1: TEdit;
QueryTypeListBox: TListBox;
Panel1: TPanel;
SelectedQueryLabel: TPanel;
WarningMemo: TMemo;
DNSLabel: TPanel;
StatusBar: TStatusBar;
DNSResolver: TIdDNSResolver;
procedure ConnectBtnClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure QueryTypeListBoxClick(Sender: TObject);
private
{ Private declarations }
InProgress: boolean;
aQueryType: integer;
procedure ShowQueryType;
procedure DisplayResults;
public
{ Public declarations }
end;
var
ResolverForm: TResolverForm;
implementation
{$R *.DFM}
procedure TResolverForm.FormCreate(Sender: TObject);
begin { FormCreate }
aQueryType := 1;
DnsResolver.Host := 'arl-name-svc-1.compuserve.com';
QueryTypeListBox.ItemIndex := 0;
ShowQueryType;
StatusBar.SimpleText := 'Current Domain Name Server is : ' + DnsResolver.Host;
end; { FormCreate }
procedure TResolverForm.ShowQueryType;
begin { ShowQueryType }
with SelectedQueryLabel do
begin
Alignment := taCenter;
Caption := 'Selected Query Type is ' + GetQTypeStr(aQueryType);
Alignment := taCenter;
end;
if aQueryType = cPTR then DnsLabel.Caption := 'Enter IP Address'
else
DnsLabel.Caption := 'Enter Domain Name';
end; { ShowQueryType }
procedure TResolverForm.QueryTypeListBoxClick(Sender: TObject);
begin { QueryTypeListBoxClick }
with QueryTypelistBox do
begin
if ItemIndex > -1 then
begin
aQueryType := Succ(ItemIndex);
end;
if aQueryType > 16 then
begin
aQueryType := aQueryType + 235;
end;
ShowQueryType;
end;
end; { QueryTypeListBoxClick }
procedure TResolverForm.DisplayResults;
var
DnsResource: TIdDNSResourceItem;
Idx: integer;
procedure ShowaList;
procedure ShowSoa;
begin { ShowSoa }
with DnsResource.Rdata.SOA do
begin
Memo.Lines.Add('Primary Data Src : ' + MName);
Memo.Lines.Add('Responsible Mailbox : ' + Rname);
Memo.Lines.Add('Serial : ' + IntToStr(Serial) + ' Refresh : ' +
IntToStr(REfresh) + ' Retry : ' + IntToStr(Retry));
Memo.Lines.Add('Expire : ' + IntToStr(Expire) + ' Minimum : ' +
IntToStr(Minimum));
end;
end; { ShowSoa }
begin { ShowAList }
Memo.Lines.Add('Resource name is : ' + DnsResource.Name);
Memo.Lines.Add('Type is : ' + GetQTypeStr(DnsResource.aType) +
' Class is : ' + GetQClassStr(DnsResource.AClass));
case DnsResource.aType of
cA: Memo.Lines.Add('IP Address is : ' + DnsResource.Rdata.DomainName);
cSoa: ShowSoa;
cPtr: Memo.Lines.Add('Domain name is : ' + DnsResource.Rdata.DomainName);
cMx: Memo.Lines.Add('MX Priority : ' + IntToStr(DnsResource.Rdata.MX.Preference) +
' MX Server : ' + DNsResource.Rdata.MX.Exchange);
else
Memo.Lines.Add('Domain name is : ' + DnsResource.Rdata.DomainName);
end;
Memo.Lines.Add('');
end; { ShowAList }
begin { DisplayResults }
with DNSResolver do
begin
if DnsAnList.Count > 0 then
begin
Memo.Lines.Add('Answer List' + #13 + #10);
for Idx := 0 to DnsAnList.Count - 1 do
begin
DnsResource := DnsAnList[Idx];
ShowAList;
end;
end;
Memo.Lines.Add('');
if DnsNsList.Count > 0 then
begin
Memo.Lines.Add('Authority List' + #13 + #10);
for Idx := 0 to DnsNsList.Count - 1 do
begin
DnsResource := DnsNsList[Idx];
ShowAList;
end;
end;
Memo.Lines.Add('');
if DnsArList.Count > 0 then
begin
Memo.Lines.Add('Additional Response List' + #13 + #10);
for Idx := 0 to DnsArList.Count - 1 do
begin
DnsResource := DnsArList[Idx];
ShowAList;
end;
end;
end;
end; { DisplayResults }
procedure TResolverForm.ConnectBtnClick(Sender: TObject);
begin { ConnectBtnClick }
if InProgress then Exit;
InProgress := True;
try
Memo.Clear;
DnsREsolver.RequestedRecords := [aQueryType];
DnsREsolver.ResolveDomain(Edit1.Text);
Memo.Lines.Add('Answers Count=' + IntToStr(DnsREsolver.Answers.Count));
Memo.Lines.Add('AnList Count=' + IntToStr(DnsREsolver.DNSAnList.Count));
DisplayResults;
finally
InProgress := False;
end;
end; { ConnectBtnClick }
end.
直接发送邮件就非常简单了,根据取得的邮件服务器IP地址(AHost):AHost := DnsResolver.Answers[0].Exchange,使用Indy的TIdSMTP控件:
TIdSMTP.QuickSend (AHost, ASubject, ATo, AFrom, AText)就轻松搞定了。
顺便说明一句:我已经编写好了一套辅助SMTP Server 开发的元件包【大部分已经完成】,你在 http://sourceforge.net/projects/smtpsvr/ 上可以下载,也欢迎你加入开发的队伍!
包括了如下的控件:
TIdSMTPServer 与Indy 自带的SMTPServer控件类似,自认为我的这个OO结构更加合理。
TIdSMTPRelay 邮件转递控件,如果你想直接发送邮件,你就需要它啦!
TIdSMTPRawSender 底层邮件发送
TIdMXResolver 邮件服务器地址解析控件
TIdMXResolverCache: 邮件服务器地址解析 Cache
TIdSMTPRelayServer 邮件转递服务器控件【没完成!】
<完>
为什么非要通过一个莫名其妙的SMTP邮件服务器转交一次,实在不服气,如果
使用UNIX,这个问题就很好解决,用sendmail就能完成递送任务,但在Windows
下呢?同样的,一定也有办法饶开SMTP Server直接递送到对方的远程邮局服务
器上,难得住别人,难得住我们程序员吗?分析相关协议(RFC2645,RFC821,
RFC1846,RFC1939,RFC1725,RFC1730-RFC1733 etc.),我们会知道要完成直接递
送其实相当简单。
首先我们看一下Email的递送过程:
Email(Encode) -> a SMTP Relay Server -> Remote SMTP Server(远程邮局)。
非常简单,邮件编码后被递送到一个SMTP转交服务器上,该服务器对信件分检
(到同一邮局的被放在一起)后,根据优先级以及信件的先后次序被发送到远程
邮局的SMTP服务器上。换句话说,只要我们知道了SMTP转交服务器是如何确定远
程邮局SMTP服务器的地址的,就可以轻松地将饶开SMTP Relay Server直接递送
到远程邮局服务器。
SMTP Relay Server是如何确定远程邮局服务器的地址的呢?如果你熟悉域名解
析,就知道是怎么回事了,我们知道电子邮件的地址由两部分构成postbox@address.com,
邮箱(postbox)和地址(address.com),给域名服务器发送指令查询“address.com”
的远程邮局服务器的地址即可找到远程邮局SMTP服务器的IP 地址,该指令查询是
被称作MX(Mail Exchange)邮件交换服务器的地址查询。远程邮局SMTP服务器的地
址可能不止一个,这时,你可根据信件优先级的不同,将对应优先级的信件发到
对应地址的远程邮局SMTP服务器,当然,你也可以不管三七二十一,随便选一个
SMTP服务器发送,见后附“域名解析结果样例”。简单吧。这下,自己编写一个
SMTP Server不难了吧!
下面我将结合一个实例介绍:
说明
域名查询使用 Indy 的 TIdDNSResolver 元件实现,至于邮件发送使用 INDY 的 SMTP Sender(TIdSMTP)元件。Delphi 5 下测试通过。
注:Indy 8.006B 存在 bug 使得TIdDNSResolver.ResolveDomain方法没有工作,在8.007b修复了该错误。
如何进行域名查询
长话短说,很简单,在窗体上放上一个 TIdDNSResolver(DnsREsolver) 元件,在程序中设置它的查询类型属性为邮件服务器 RequestedRecords := [cMX],执行ResolveDomain('xxx.com')方法即可获得域名查询结果,如果没有出错,域名查询结果就会被放在DNSAnList、DNSARList、DNSNSList属性中。
域名服务器查询类型,注意,下面的查询类型不是所有的域名服务器都支持:
cA = 1; // 查询主机地址
cNS = 2; // 查询域名服务器
cMD = 3; // A mail destination obsolete use MX (已废弃)
cMF = 4; // A mail forwarder obsolete use MX (已废弃)
cName = 5; // The canonical name for an alias
cSOA = 6; // Marks the start of a zone of authority
cMB = 7; // A mail box domain name (Experimental)
cMG = 8; // A mail group member (Experimental)
cMR = 9; // A mail Rename Domain Name (Experimental)
cNULL = 10; // RR (Experimental)
cWKS = 11; // A well known service description
cPTR = 12; // A Domain Name Pointer;
cHINFO = 13; // Host Information;
cMINFO = 14; // Mailbox or Mail List Information;
cMX = 15; // 邮件服务器
cTXT = 16; // Text String;
cAXFR = 252; // A Request for the Transfer of an entire zone;
cMAILB = 253; // A request for mailbox related records (MB MG OR MR}
cMAILA = 254; // A request for mail agent RRs (已废弃 see MX)
cStar = 255; // A Request for all Records
DNSAnList means an Answer List, DNSARList means an Additional Response List, DNSNSList means an Authority List. 下面我们通过一个例子来看看如何进行域名查询:
unit ResolverTest;
interface
uses
Windows,
Messages,
SysUtils,
Classes,
Graphics,
Controls,
Forms,
Dialogs,
StdCtrls,
Buttons,
ExtCtrls,
ComCtrls,
IdDNSResolver;
type
TResolverForm = class(TForm)
ConnectBtn: TBitBtn;
Memo: TMemo;
Edit1: TEdit;
QueryTypeListBox: TListBox;
Panel1: TPanel;
SelectedQueryLabel: TPanel;
WarningMemo: TMemo;
DNSLabel: TPanel;
StatusBar: TStatusBar;
DNSResolver: TIdDNSResolver;
procedure ConnectBtnClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure QueryTypeListBoxClick(Sender: TObject);
private
{ Private declarations }
InProgress: boolean;
aQueryType: integer;
procedure ShowQueryType;
procedure DisplayResults;
public
{ Public declarations }
end;
var
ResolverForm: TResolverForm;
implementation
{$R *.DFM}
procedure TResolverForm.FormCreate(Sender: TObject);
begin { FormCreate }
aQueryType := 1;
DnsResolver.Host := 'arl-name-svc-1.compuserve.com';
QueryTypeListBox.ItemIndex := 0;
ShowQueryType;
StatusBar.SimpleText := 'Current Domain Name Server is : ' + DnsResolver.Host;
end; { FormCreate }
procedure TResolverForm.ShowQueryType;
begin { ShowQueryType }
with SelectedQueryLabel do
begin
Alignment := taCenter;
Caption := 'Selected Query Type is ' + GetQTypeStr(aQueryType);
Alignment := taCenter;
end;
if aQueryType = cPTR then DnsLabel.Caption := 'Enter IP Address'
else
DnsLabel.Caption := 'Enter Domain Name';
end; { ShowQueryType }
procedure TResolverForm.QueryTypeListBoxClick(Sender: TObject);
begin { QueryTypeListBoxClick }
with QueryTypelistBox do
begin
if ItemIndex > -1 then
begin
aQueryType := Succ(ItemIndex);
end;
if aQueryType > 16 then
begin
aQueryType := aQueryType + 235;
end;
ShowQueryType;
end;
end; { QueryTypeListBoxClick }
procedure TResolverForm.DisplayResults;
var
DnsResource: TIdDNSResourceItem;
Idx: integer;
procedure ShowaList;
procedure ShowSoa;
begin { ShowSoa }
with DnsResource.Rdata.SOA do
begin
Memo.Lines.Add('Primary Data Src : ' + MName);
Memo.Lines.Add('Responsible Mailbox : ' + Rname);
Memo.Lines.Add('Serial : ' + IntToStr(Serial) + ' Refresh : ' +
IntToStr(REfresh) + ' Retry : ' + IntToStr(Retry));
Memo.Lines.Add('Expire : ' + IntToStr(Expire) + ' Minimum : ' +
IntToStr(Minimum));
end;
end; { ShowSoa }
begin { ShowAList }
Memo.Lines.Add('Resource name is : ' + DnsResource.Name);
Memo.Lines.Add('Type is : ' + GetQTypeStr(DnsResource.aType) +
' Class is : ' + GetQClassStr(DnsResource.AClass));
case DnsResource.aType of
cA: Memo.Lines.Add('IP Address is : ' + DnsResource.Rdata.DomainName);
cSoa: ShowSoa;
cPtr: Memo.Lines.Add('Domain name is : ' + DnsResource.Rdata.DomainName);
cMx: Memo.Lines.Add('MX Priority : ' + IntToStr(DnsResource.Rdata.MX.Preference) +
' MX Server : ' + DNsResource.Rdata.MX.Exchange);
else
Memo.Lines.Add('Domain name is : ' + DnsResource.Rdata.DomainName);
end;
Memo.Lines.Add('');
end; { ShowAList }
begin { DisplayResults }
with DNSResolver do
begin
if DnsAnList.Count > 0 then
begin
Memo.Lines.Add('Answer List' + #13 + #10);
for Idx := 0 to DnsAnList.Count - 1 do
begin
DnsResource := DnsAnList[Idx];
ShowAList;
end;
end;
Memo.Lines.Add('');
if DnsNsList.Count > 0 then
begin
Memo.Lines.Add('Authority List' + #13 + #10);
for Idx := 0 to DnsNsList.Count - 1 do
begin
DnsResource := DnsNsList[Idx];
ShowAList;
end;
end;
Memo.Lines.Add('');
if DnsArList.Count > 0 then
begin
Memo.Lines.Add('Additional Response List' + #13 + #10);
for Idx := 0 to DnsArList.Count - 1 do
begin
DnsResource := DnsArList[Idx];
ShowAList;
end;
end;
end;
end; { DisplayResults }
procedure TResolverForm.ConnectBtnClick(Sender: TObject);
begin { ConnectBtnClick }
if InProgress then Exit;
InProgress := True;
try
Memo.Clear;
DnsREsolver.RequestedRecords := [aQueryType];
DnsREsolver.ResolveDomain(Edit1.Text);
Memo.Lines.Add('Answers Count=' + IntToStr(DnsREsolver.Answers.Count));
Memo.Lines.Add('AnList Count=' + IntToStr(DnsREsolver.DNSAnList.Count));
DisplayResults;
finally
InProgress := False;
end;
end; { ConnectBtnClick }
end.
直接发送邮件就非常简单了,根据取得的邮件服务器IP地址(AHost):AHost := DnsResolver.Answers[0].Exchange,使用Indy的TIdSMTP控件:
TIdSMTP.QuickSend (AHost, ASubject, ATo, AFrom, AText)就轻松搞定了。
顺便说明一句:我已经编写好了一套辅助SMTP Server 开发的元件包【大部分已经完成】,你在 http://sourceforge.net/projects/smtpsvr/ 上可以下载,也欢迎你加入开发的队伍!
包括了如下的控件:
TIdSMTPServer 与Indy 自带的SMTPServer控件类似,自认为我的这个OO结构更加合理。
TIdSMTPRelay 邮件转递控件,如果你想直接发送邮件,你就需要它啦!
TIdSMTPRawSender 底层邮件发送
TIdMXResolver 邮件服务器地址解析控件
TIdMXResolverCache: 邮件服务器地址解析 Cache
TIdSMTPRelayServer 邮件转递服务器控件【没完成!】
<完>