2011-06-22 40 views
2

有沒有簡單的方法來查看域名是否有MX記錄或不使用Delphi?我有一個我希望驗證工作的電子郵件列表,我想檢查每個域並查看是否存在MX服務器。查找郵件服務器是否存在電子郵件列表

謝謝。

編輯:我有的電子郵件地址全部來自錯誤代碼:5.4.0的退回電子郵件。但太多的服務器不遵循任何標準,5.4.0錯誤代碼本身可能意味着太多。我不想僅僅刪除所有使用該錯誤代碼發現的電子郵件地址,所以我想一個更好的方法是首先檢查域或mx記錄是否不存在,然後刪除這些地址。

+2

千萬牢記的是(A)僅僅因爲這個域有一個MX記錄,它不意味着指定的服務器將接收您感興趣的地址的郵件,並且(b)缺少MX記錄並不意味着無法發送到該地址的郵件(大多數MTA會回退到嘗試將域名解析爲IP地址。) –

+1

哇,這聽起來像是一個垃圾郵件發送者會問的問題。 :-) –

+1

這實際上是一個通訊。但是,是的,我猜想的前提是一樣的。 –

回答

1

實際上有一個電子郵件檢查器是很好的。如果沒有其他的東西可以清理你的電子郵件庫,並避免一遍又一遍地發送到非現有的郵件。或者,您可以將它用作驗證用戶郵件的方式,當他們登錄到您的應用程序。

這是我的郵件檢查課程的一部分代碼。

procedure TMailValidator.ResolveEmailAddress(const Address: TEMailAddress; const DNSServer: string); 
var 
    I: Integer; 
    MXEmpty: Boolean; 
    DomainName: string; 
    DNSResolver: TIdDNSResolver; 
begin 
    DNSResolver := TIdDNSResolver.Create(nil); 
    try 
    DomainName := StrAfter('@', string(Address)); 
    MXEmpty := True; 

    DNSResolver.Host := DNSServer; 
    {$IFNDEF IT_UseIndy9} 
     DNSResolver.QueryType := [qtMx]; 
    {$ELSE} 
     DNSResolver.QueryRecords := [qtMx]; 
    {$ENDIF} // IT_UseIndy9 
    try 
     {$IFNDEF IT_UseIndy9} 
     DNSResolver.WaitingTime := FDNSResolveTimeout; 
     {$ELSE} 
     DNSResolver.ReceiveTimeout := FDNSResolveTimeout; 
     {$ENDIF} // IT_UseIndy10 
     DNSResolver.Resolve(DomainName); 

     for I := 0 to DNSResolver.QueryResult.Count - 1 do 
     begin 
     if DNSResolver.QueryResult[I].RecType = qtMX then 
     begin 
      MXEmpty := False; 
      CheckEmailAddress(Address, TMXRecord(DNSResolver.QueryResult[I]).ExchangeServer); 

      // were we successfull 
      if CheckSMTPExitErrorCode then 
      Exit; 
     end; 
     end; 

     // check for servers flag 
     if FFoundMailServer then 
     begin 
     SendLogMessage(Format('Address "%s" is not valid on domain "%s"', [Address, DNSServer])); 
     SetLastError(cUserErrorCodeBase + 5); 
     end 
     else 
     begin 
     if MXEmpty then 
     begin 
      SendLogMessage(Format('No valid mail(MX) server could be found for domain "%s"', [DomainName])); 
      CheckEmailAddress(Address, DomainName); 
     end 
     else 
     begin 
      SendLogMessage(Format('Mail server did not respond on domain "%s"', [DomainName])); 
      SetLastError(cUserErrorCodeBase + 3); 
     end; 
     end; 
    except 
     on E: Exception do 
     begin 
     SendLogMessage(Format('Address "%s" validation failed for domain "%s": %s', [Address, 
                        DomainName, 
                        E.Message])); 
     SetLastError(cUserErrorCodeBase + 4, E.Message); 
     end; 
    end; 
    finally 
    DNSResolver.Free; 
    end; 
end; 


procedure TMailValidator.CheckEmailAddress(const Address: TEMailAddress; const MailServer: string); 
var 
    SMTP: TIdSMTP; 
begin 
    SendLogMessage(Format('Validating address "%s" on server "%s"', [Address, MailServer])); 

    if (FCheckStep = csAddress) or (FCheckStep = csDomain) then 
    begin 
    // finish if flags in [FLAG_CheckLocal, FLAG_CheckDomain] 
    SendLogMessage(Format('Address "%s" successfuly validated.', [Address])); 
    Exit; 
    end; 

    SMTP := TIdSMTP.Create(nil); 
    try 
    FCurrentStep := csMailBox; 
    try 
     SMTP.ReadTimeout := FSMTPReadTimeout; 
     {$IFNDEF IT_UseIndy9} 
     SMTP.AuthType := satNone; 
     {$ELSE} 
     SMTP.AuthenticationType := atNone; 
     {$ENDIF} // IT_UseIndy9 
     SMTP.Host := MailServer; 
     SMTP.Port := 25; 

     SMTP.Connect; 
     try 
     FFoundMailServer := True; 

     try 
      SMTP.SendCmd('Helo ' + FQueryingServer, 250); 
      SMTP.SendCmd('Rset'); 
      SMTP.SendCmd('Mail from:<' + string(Address) + '>', 250); 
      SMTP.SendCmd('RCPT to:<' + string(Address) + '>', [250, 251]); 

      SendLogMessage(Format('Address "%s" successfuly validated on server "%s".', [FEMailAddress, 
                         MailServer])); 
     except 
      on E: Exception do 
      begin 
      SendLogMessage(Format('Address "%s" validation failed on server "%s": %s', [Address, 
                         MailServer, 
                         E.Message])); 
      SetLastError(SMTP.LastCmdResult.NumericCode, E.Message); 
      Exit; 
      end; 
     end 
     finally 
     SMTP.Disconnect; 
     end; 
    except 
     // handle all other exceptions 
     on E: Exception do 
     begin 
     SendLogMessage(Format('CheckMail [%s] : Failure (Server) "%s" [%s]', [Address, 
                       MailServer, 
                       E.Message])); 
     SetLastError(Max(cUserErrorCodeBase + 6, SMTP.LastCmdResult.NumericCode), E.Message); 
     end; 
    end; 
    finally 
    SMTP.Free; 
    end; 
end; 

基本上你做的三個步驟:

  1. 檢查電子郵件語法。
  2. 檢查域和驗證MX服務器
  3. 驗證用戶郵箱
+1

成功完成步驟#3並不意味着郵箱有效,而只是表示郵件服務器可能接受該地址的郵件。郵件服務器可能會在發送數據後拒絕該郵件,或者可能會愉快地接受來自您的郵件,然後再轉向並刪除它。 – afrazier

+1

另外,特別挑剔的人可能不喜歡你使用異常來進行常見錯誤控制/代碼流的目的。錯誤的電子郵件地址,失敗的主機,完整的郵箱等不是*例外情況。 :-)(我不打算聲稱自己完全清楚那個特定代碼的味道) – afrazier

+0

我沒有提出異常,只設置了最後的錯誤代碼。最後你只檢查整個過程的錯誤代碼。此外,我必須捕捉Indy異常,因爲Indy使用異常來處理代碼流。我也不喜歡那樣。我只是在做Windows API所做的事情 – Runner

1

檢查您是否可以發送電子郵件的唯一方法是實際發送郵件,並檢查您可以獲得反彈響應的方式。

+1

不,你實際上沒有發送數據。你只是檢查收件人。 – Runner

+0

實際上:您必須發送數據,收件人檢查有時會返回OK,而實際發送確實有效。 –

+0

我不知道。但它似乎實際上發送的數據會過度它:)沒有100%的方式來確認一封電子郵件總之短實際發送一封電子郵件和獲得答覆:) – Runner

2

你可以使用windows API DnsQuery檢查MX記錄給定的服務器名稱。不幸的是,我沒有找到適合的頭文件的Delphi翻譯,所以我自己做了部分(但可行)的翻譯。它只支持MX和IpV4 A記錄,沒有別的。添加對IpV6地址的支持應該是微不足道的。

這裏是我的翻譯,包括一個簡單的ServerHasMxRecords(const ServerName:string):Boolean函數返回True如果任何MX記錄發現:

unit DnsMxCheck; 

interface 

uses Windows, Classes; 

type 
    DNS_STATUS = Integer; 
    IP4_ADDRESS = DWORD; 

    _DNS_RECORD_FLAGS = packed record 
    case Boolean of 
    True: (DW: DWORD); 
    False: (DNS_RECORD_FLAGS: DWORD); 
    end; 

    DNS_A_DATA = packed record 
    case Boolean of 
     True: (IpAddress: IP4_ADDRESS); 
     False: (Bytes:array[0..3] of Byte); 
    end; 

    DNS_MX_DATA = packed record 
    pNameExchange: PWChar; 
    wPreference: Word; 
    Pad: Word; 
    end; 

    _DNS_RECORD_DATA_UNION = packed record 
    case Integer of 
     0: (A: DNS_A_DATA); 
     1: (MX1, MX2, AFSDB1, AFSDB2, RT1, RT2: DNS_MX_DATA); 
     999: (Filler: array[0..1024] of Byte); // I have no idea what the true size of the record shoud be! 
    end; 

    PDNS_RECORD = ^DNS_RECORD; 
    DNS_RECORD = packed record 
    NextRecord: PDNS_RECORD; 
    pName: PWChar; 
    wType: Word; 
    wDataLength: Word; 
    Flags: _DNS_RECORD_FLAGS; 
    dwTtl: DWORD; 
    dwReserved: DWORD; 
    Data: _DNS_RECORD_DATA_UNION; 
    end; 

const DNS_TYPE_A = $0001; 
     DNS_TYPE_MX = $000f; 

function DnsQuery_W(lpstrName: PWChar; wType: Word; Options: DWORD; pExtra: Pointer; out ppQueryResultsSet: PDNS_RECORD; pReserved: Pointer): DNS_STATUS;stdcall;external 'dnsapi.dll'; 

function ServerHasMxRecords(const ServerName:string):Boolean; 

implementation 

function ServerHasMxRecords(const ServerName:string):Boolean; 
var DNS_REC: PDNS_RECORD; 
begin 
    if DnsQuery_W(PWChar(ServerName), DNS_TYPE_MX, 0, nil, DNS_REC, nil) = 0 then 
    begin 
    while Assigned(DNS_REC) do 
    begin 
     if DNS_REC.wType = DNS_TYPE_MX then 
     begin 
     Exit(True); 
     end; 
     DNS_REC := DNS_REC.NextRecord; 
    end; 
    end; 
    Result := False; 
end; 

end.