Mega Code Archive

 
Categories / Delphi / Examples
 

How to use dns blocklists to detect spam

One of the solutions to spam is a -frequent updated- database with IP addresses that appear to misbehave. This database should, preferably, be random accessible at high speed. DNS offers a solution for that. Introduction. Fortunately internet has found answers for the immens growing spam problem. A solution is a -frequent updated- database with IP addresses that appear to misbehave. This database should, preferably, be random accessible at high speed. DNS offers a solution for that. DNS entries can be updated quickly. Also, mostly there is no need to download a copy of the (potentially large) database frequently. If a mail server can detect, in a fraction of a second, that the sending IP is currently blacklisted, for example because it was infected by a virus and used as open relay, it can effictively block reception. A well-known DNSBL provider is spamhouse.org . For a (complete) list of DNSBL providers, look here Usage Both SMTP servers and mail clients (or mailbox cleaners) can use this method. The path a mail message went is always stored, so you can always retrieve the originating IP. This is an essential part of the SMTP protocol. Using the POP3 protocol, you can watch a mail box, top (top means: fetch only the message header) headers of messages, and see if they are blacklisted by looking at the "Received: from somehost (1.2.3.4)" lines. Since these spam databases can be updated frequently, they can effectively detect a large amount (>50% ?) of spam. How does it work Basically, what you need to do is verify against a DNSBL (DNS BlockList) source, like www.spamhaus.org. This is done in the following way: Suppose you want to check if IP adress 60.70.80.90 is a spammer, you just perform a DNS query to sbl.spamhaus.org, with the (reversed) ip address inserted, like query dns: 90.80.70.60.sbl.spamhaus.org if you get back a A record, this is a spammer. if you get back nothing, this ip is not on the spam list. Test it You can easily verify this using the 'ping' command. if you would do: ping 90.80.70.60.sbl.spamhaus.org, then there are two options: * you get 'unknown host' message. This is ok, the IP is not blacklisted. * You get '127.0.0.x', where x>1, like 127.0.0.2. X represents a status code. Generally, 2 is used for (semi)permanent netblocks, and 4 is used for 'open proxies' (like: machines infected by a virus). Example i use this unit succesfully in a mail client. Lubos has integrated this unit successfully in a SMTP/POP3 server suite. you can use this unit with or without synapse tcp/ip library by setting the {$DEFINE SYNAPSE} directive. spamchck.pas unit spamchck; interface //Query's the spamhaus.org database of spammers uses Classes, SysUtils, {$IFDEF SYNAPSE}SynaUtil, SynSock{$ELSE}WinSock{$ENDIF}; type TSpamCheck = class (TObject) protected public FDNSBL:String; //DNS BlockList constructor Create; function IsSpammer (IP:String):Integer; overload; function IsSpammer (MailHeader:TStrings):Integer; overload; end; implementation { TSpamCheck } constructor TSpamCheck.Create; begin inherited; FDNSBL := 'sbl-xbl.spamhaus.org'; // alternatively use sbl.spamhaus.org (spam) or // xbl.spamhaus.org (open relays, proxys) // or an alternative source DNSBL source. // the sbl-xbl is the combined list. end; function TSpamCheck.IsSpammer(IP: String): Integer; var RevIP:String; i:Integer; p:PHostEnt; begin //Query the database //First, reverse the IP Result := -1; {$IFDEF SYNAPSE} if IsIP (IP) then {$ENDIF} begin //Reverse the IP RevIP := ''; for i:=0 to 2 do begin RevIP := '.'+Copy (IP, 1, pos ('.', IP)-1) + RevIP; IP := Copy (IP, pos('.', IP)+1, maxint); end; RevIP := IP + RevIP; //Now, query the database: RevIP := RevIP + '.' + FDNSBL; p := GetHostByName (PChar(RevIP)); if Assigned (p) then begin //Results come back as 127.0.0.x where x > 1 // 127.0.0.2 = spam // 127.0.0.4 = open relay etc. Result := byte(p^.h_addr^.S_un_b.s_b4); end else //no dns entry found, mark it as safe: Result := 0; end; end; function TSpamCheck.IsSpammer(MailHeader: TStrings): Integer; var v,ip:String; i,r:Integer; begin //Parse a email header //Look for 'Received' header //extract IP address, assuming form 'Received: from (a.b.c.d) by (w.x.y.z) //Validate this IP address at spamhaus. i := 0; Result := -1; while i<MailHeader.Count do begin if pos ('received: ', lowercase (MailHeader[i])) = 1 then begin v := MailHeader[i]; //search for additional headers: while ((i+1)<MailHeader.Count) and (MailHeader[i+1]<>'') and (MailHeader[i+1][1]=' ') do begin inc (i); v := v+MailHeader[i]; end; //v now contains one line, find from ip address: v := lowercase (v); //searching for: //Received: from somehost.com (1.2.3.4). v := copy (v, pos ('from', v)+4, maxint); v := copy (v, pos ('(', v)+1, maxint); v := copy (v, 1, pos (')', v)-1); if pos ('[', v)>0 then //valid format is also: //Received: from somehost.com (somehost.com [1.2.3.4]) begin v := copy (v, pos ('[', v)+1, maxint); v := copy (v, 1, pos (']', v)-1); end; Result := IsSpammer (v); //a single received line is sufficient if Result > 0 then break; // end; inc (i); end; end; end.