开发者

Look up if Mail Server exists for list of emails

is there a simple way to look up if a domain has a MX record or not using Delphi? I have a list of emails that I wish to verify work, I want to check each of the domains and see if a MX server even exists.

Thanks.

Edit: The email addresses I have are all from bounced email messages of error code: 5.4.0. But too many servers don't follow any standards and 5.4.0 error code itself can mean too much. I don't want to just remove all the email addresses found with that error code erroraneously, so I figure a better way is t开发者_开发技巧o first check if the domain or mx record don't exist and remove those for sure.


You can use the windows DnsQuery API to check the MX records for a given server name. Unfortunately I didn't find a proper Delphi translation for the headers, so I made a partial (but workable) translation myself. It only supports MX and IpV4 A records, nothing else. Adding support for IpV6 address should be trivial.

Here's my translation, including a simple ServerHasMxRecords(const ServerName:string):Boolean function that returns True if any MX records are found:

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.


It is actually good to have an e-mail checker. If nothing else you can clean you e-mail base and avoid sending over and over to non existing mails. Or you can use it as means of verifying user mails when they sign on to your application.

Here is a part of the code in my mail checking class.

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;

Basically you do it in three steps:

  1. Check the mail syntax.
  2. Check the domain and validate MX server
  3. Validate the user mailbox


The only way to check if you can deliver an email is to actually deliver it, and check the zillion ways you can get a bounce response.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜