PageRenderTime 55ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/bin/src/artica-policy/filter.pas

https://github.com/dtouzeau/artica-1.5
Pascal | 487 lines | 384 code | 83 blank | 20 comment | 54 complexity | bf4b74f8a5b0546c7d5ac075e4319fd8 MD5 | raw file
  1. unit filter;
  2. {$MODE DELPHI}
  3. //{$mode objfpc}{$H+}
  4. {$LONGSTRINGS ON}
  5. interface
  6. uses
  7. Classes, SysUtils,variants, Process,IniFiles,oldlinux,md5,RegExpr in 'RegExpr.pas',logs,ldap,strutils,synautil,dnssend,synamisc;
  8. type
  9. TStringDynArray = array of string;
  10. type
  11. TTMailInfo = record
  12. client_address:string;
  13. client_name:string;
  14. reverse_client_name:string;
  15. sender:string;
  16. sender_name:string;
  17. recipient:string;
  18. size:string;
  19. ldap_uid:string;
  20. ldap_ou:string;
  21. senderdomain:string;
  22. Filter_result:string;
  23. mx_record:string;
  24. md:string;
  25. end;
  26. type
  27. Tfilter=class
  28. private
  29. function Explode(const Separator, S: string; Limit: Integer = 0):TStringDynArray;
  30. function COMMANDLINE_PARAMETERS(FoundWhatPattern:string):boolean;
  31. GLOBAL_INI:TIniFile;
  32. function get_ARTICA_PHP_PATH():string;
  33. function BlackList(Email:TTMailInfo):TTMailInfo;
  34. function ArticaFilterQueuePath():string;
  35. function MYSQL_ACTION_QUERY(sql:string):boolean;
  36. procedure SendSqlQuery(email:TTmailInfo);
  37. function DNSMX(Email:TTMailInfo):TTMailInfo;
  38. function DSNMASQ_RUN():boolean;
  39. infos:TTMailInfo;
  40. DNSMASQ:boolean;
  41. public
  42. constructor Create;
  43. destructor Destroy; override;
  44. MESSAGE_IDA:string;
  45. function ParseLines(receive:string):string;
  46. function MD5FromString(values:string):string;
  47. end;
  48. implementation
  49. //-------------------------------------------------------------------------------------------------------
  50. //##############################################################################
  51. constructor Tfilter.Create;
  52. begin
  53. forcedirectories('/etc/artica-postfix');
  54. DNSMASQ:=DSNMASQ_RUN();
  55. end;
  56. //##############################################################################
  57. destructor Tfilter.Destroy;
  58. begin
  59. inherited Destroy;
  60. end;
  61. //##############################################################################
  62. function Tfilter.ParseLines(receive:string):string;
  63. const
  64. CR = #$0d;
  65. LF = #$0a;
  66. //CRLF = CR + LF;
  67. CRLF = #$0D + #$0A;
  68. var
  69. RegExpr:TRegExpr;
  70. response:string;
  71. LOGS:Tlogs;
  72. Table:TStringDynArray;
  73. S:TStringList;
  74. i:integer;
  75. verbose:boolean;
  76. BEGIN
  77. LOGS:=Tlogs.create;
  78. RegExpr:=TRegExpr.Create;
  79. RegExpr.Expression:='(.+?)=(.+)';
  80. Table:=Explode(#10,receive);
  81. for i:=0 to length(Table)-1 do begin
  82. if RegExpr.Exec(Table[i]) then begin
  83. if LowerCase(RegExpr.Match[1])='client_address' then infos.client_address:=RegExpr.Match[2];
  84. if LowerCase(RegExpr.Match[1])='client_name' then infos.client_name:=RegExpr.Match[2];
  85. if LowerCase(RegExpr.Match[1])='recipient' then infos.recipient:=RegExpr.Match[2];
  86. if LowerCase(RegExpr.Match[1])='sender' then infos.sender:=RegExpr.Match[2];
  87. if LowerCase(RegExpr.Match[1])='size' then infos.size:=RegExpr.Match[2];
  88. end;
  89. end;
  90. verbose:=COMMANDLINE_PARAMETERS('verbose');
  91. logs.logs('artica-policy:: initialize');
  92. if verbose then logs.logs('artica-policy:: verbose mode=true');
  93. RegExpr.free;
  94. infos.md:=MD5FromString(receive);
  95. infos.Filter_result:='send';
  96. if verbose then logs.logs('artica-policy:: --> BlackList()');
  97. infos:=BlackList(infos);
  98. if infos.Filter_result='send' then begin
  99. if verbose then logs.logs('artica-policy:: --> DNSMX()');
  100. infos:=DNSMX(infos);
  101. end;
  102. logs.logs('artica-policy:: ' + infos.md+' :: From <' + infos.sender + '> To <'+infos.recipient + '> (' + infos.client_address + ' [' + infos.client_name + '])=' + infos.Filter_result);
  103. s:=TStringList.Create;
  104. if infos.Filter_result='blacklist' then begin
  105. SendSqlQuery(infos);
  106. s.Add('action=REJECT 571 Delivery not authorized, message refused');
  107. s.Add('');
  108. s.Add('');
  109. result:=s.Text;
  110. s.free;
  111. LOGS.Free;
  112. exit;
  113. end;
  114. if infos.Filter_result='DNS' then begin
  115. SendSqlQuery(infos);
  116. s.Add('action=REJECT 571 Delivery not authorized, message refused');
  117. s.Add('');
  118. s.Add('');
  119. result:=s.Text;
  120. s.free;
  121. LOGS.Free;
  122. exit;
  123. end;
  124. s.Add('action=DUNNO');
  125. s.Add('');
  126. s.Add('');
  127. result:=s.Text;
  128. s.free;
  129. LOGS.Free;
  130. exit;
  131. end;
  132. //##############################################################################
  133. function Tfilter.DNSMX(Email:TTMailInfo):TTMailInfo;
  134. var
  135. l:TstringList;
  136. LOGS:Tlogs;
  137. s:string;
  138. RegExpr:TRegExpr;
  139. I:Integer;
  140. ldap:Tldap;
  141. parameters:string;
  142. verbose:boolean;
  143. begin
  144. verbose:=COMMANDLINE_PARAMETERS('verbose');
  145. ldap:=Tldap.Create;
  146. LOGS:=TLOGS.Create;
  147. result:=Email;
  148. if length(Email.senderdomain)=0 then begin
  149. RegExpr:=TRegExpr.Create;
  150. RegExpr.Expression:='(.+?)@(.+)';
  151. if RegExpr.Exec(Email.sender) then begin
  152. Email.senderdomain:=RegExpr.Match[2];
  153. email.sender_name:=RegExpr.Match[1];
  154. if verbose then logs.logs('domain --> '+Email.senderdomain);
  155. end;
  156. RegExpr.Free;
  157. end;
  158. if email.sender_name='mailman-bounces' then exit;
  159. if length(trim(email.ldap_ou))=0 then email.ldap_ou:=ldap.OU_From_eMail(Email.recipient);
  160. if length(trim(email.ldap_ou))=0 then begin
  161. ldap.Free;
  162. logs.Free;
  163. exit(Email);
  164. end;
  165. if verbose then logs.logs('artica-policy:: --> ldap.ArticaDenyNoMXRecordsOu() --> '+email.ldap_ou);
  166. parameters:=Ldap.ArticaDenyNoMXRecordsOu(email.ldap_ou);
  167. LOGS.LOGS('artica-policy:: parameters="' +parameters + '"');
  168. if parameters='pass' then begin
  169. ldap.free;
  170. Logs.free;
  171. exit(email);
  172. end;
  173. if ldap.ISRegisteredDomain(Email.senderdomain) then begin
  174. if verbose then logs.logs('artica-policy:: --> ldap.ISRegisteredDomain() --> TRUE');
  175. ldap.free;
  176. Logs.free;
  177. exit(email);
  178. end;
  179. ldap.free;
  180. l := TStringList.create;
  181. if not DNSMASQ then begin
  182. try
  183. s := GetDNS;
  184. LOGS.LOGS('artica-policy:: DNSMX:: servers="' +s + '"');
  185. l.commatext := s;
  186. if l.count > 0 then
  187. begin
  188. s := l[0];
  189. LOGS.LOGS('artica-policy:: DNSMX:: request for="' +s + '" =>' + Email.senderdomain);
  190. GetMailServers(s, Email.senderdomain, l);
  191. end;
  192. finally
  193. end;
  194. end else begin
  195. LOGS.LOGS('artica-policy:: DNSMX:: request for="127.0.0.1" =>' + Email.senderdomain);
  196. GetMailServers('127.0.0.1', Email.senderdomain, l);
  197. end;
  198. if l.count>0 then begin
  199. for i:=0 to l.Count -1 do begin
  200. LOGS.LOGS('artica-policy:: DNSMX::(' + InttoStr(i) + ') "' +l.Strings[i] + '"');
  201. end;
  202. end else begin
  203. LOGS.LOGS('artica-policy:: No mx for ' + Email.senderdomain);
  204. Email.Filter_result:='DNS';
  205. end;
  206. l.free;
  207. result:=Email;
  208. end;
  209. //##############################################################################
  210. function Tfilter.DSNMASQ_RUN():boolean;
  211. var pid:string;
  212. begin
  213. result:=false;
  214. if FileExists('/usr/sbin/dnsmasq') then result:=true;
  215. if FileExists('/usr/local/sbin/dnsmasq') then result:=true;
  216. end;
  217. //##############################################################################
  218. //##############################################################################
  219. function Tfilter.BlackList(Email:TTMailInfo):TTMailInfo;
  220. var
  221. LDAP:Tldap;
  222. trouve:boolean;
  223. i:integer;
  224. ou,uid:string;
  225. RegExpr:TRegExpr;
  226. Sender_domain:string;
  227. LOGS:TLogs;
  228. D:boolean;
  229. begin
  230. D:=false;
  231. LDAP:=TLdap.Create();
  232. LOGS:=TLOGS.Create;
  233. D:=COMMANDLINE_PARAMETERS('debug');
  234. Email.ldap_uid:=ldap.EmailFromAliase(Email.recipient);
  235. if D then writeln('BlackList:: uid=' + Email.ldap_uid);
  236. if length(trim(email.ldap_ou))=0 then begin
  237. ou:=ldap.OU_From_eMail(Email.recipient);
  238. email.ldap_ou:=ou;
  239. end else begin
  240. ou:=Email.ldap_ou;
  241. end;
  242. RegExpr:=TRegExpr.Create;
  243. RegExpr.Expression:='(.+?)@(.+)';
  244. if RegExpr.Exec(Email.sender) then begin
  245. Sender_domain:=RegExpr.Match[2];
  246. email.sender_name:=RegExpr.Match[1];
  247. Email.senderdomain:=Sender_domain;
  248. end;
  249. RegExpr.Free;
  250. if length(ou)>0 then begin
  251. if D then writeln('BlackList:: OU=' + ou);
  252. if ldap.IsOuDomainBlackListed(ou,Email.senderdomain) then begin
  253. LOGS.Logs('artica-send::Global BlackList::' +ou + ' as black listed '+Sender_domain);
  254. LDAP.Free;
  255. LOGS.FREE;
  256. email.Filter_result:='blacklist';
  257. exit(email);
  258. end;
  259. RegExpr.Free;
  260. end;
  261. if D then writeln('BlackList:: IsOuDomainBlackListed:: false');
  262. if LDAP.IsBlackListed(Email.sender,Email.recipient) then begin
  263. LOGS.Logs('artica-send:: BlackList::' +email.recipient + ' as black listed '+Email.sender);
  264. LDAP.Free;
  265. LOGS.FREE;
  266. email.Filter_result:='blacklist';
  267. exit(email);
  268. end;
  269. if LDAP.IsBlackListed(Email.sender,Email.recipient) then begin
  270. LOGS.Logs('artica-send:: BlackList::' + email.recipient+ ' as black listed '+Email.sender);
  271. LDAP.Free;
  272. LOGS.FREE;
  273. email.Filter_result:='blacklist';
  274. exit(email);
  275. end;
  276. LDAP.Free;
  277. LOGS.FREE;
  278. exit(email);
  279. end;
  280. //#########################################################################################
  281. procedure Tfilter.SendSqlQuery(email:TTmailInfo);
  282. var sql,Subject:string;
  283. begin
  284. Subject:='No Subject';
  285. sql:='INSERT INTO messages (MessageID,mail_from,mailfrom_domain,mail_to,subject,zDate,received_date,SpamRate,message_path,filter_action,ou,MailSize,SpamInfos,quarantine) ';
  286. sql:=sql + 'VALUES("'+ email.md+'","' + email.sender+'","' + email.senderdomain+'","' + email.recipient +'","'+Subject+'",';
  287. sql:=sql + 'DATE_FORMAT(NOW(),''%Y-%m-%d %H:%I:%S''),DATE_FORMAT(NOW(),''%Y-%m-%d %H:%I:%S''),"0","'+email.md+'","' +email.Filter_result+'","' +email.ldap_ou+'","' + email.size + '","NONE","0")';
  288. MYSQL_ACTION_QUERY(sql);
  289. end;
  290. //#########################################################################################
  291. function Tfilter.Explode(const Separator, S: string; Limit: Integer = 0):TStringDynArray;
  292. var
  293. SepLen : Integer;
  294. F, P : PChar;
  295. ALen, Index : Integer;
  296. begin
  297. SetLength(Result, 0);
  298. if (S = '') or (Limit < 0) then
  299. Exit;
  300. if Separator = '' then
  301. begin
  302. SetLength(Result, 1);
  303. Result[0] := S;
  304. Exit;
  305. end;
  306. SepLen := Length(Separator);
  307. ALen := Limit;
  308. SetLength(Result, ALen);
  309. Index := 0;
  310. P := PChar(S);
  311. while P^ <> #0 do
  312. begin
  313. F := P;
  314. P := StrPos(P, PChar(Separator));
  315. if (P = nil) or ((Limit > 0) and (Index = Limit - 1)) then
  316. P := StrEnd(F);
  317. if Index >= ALen then
  318. begin
  319. Inc(ALen, 5); // mehrere auf einmal um schneller arbeiten zu können
  320. SetLength(Result, ALen);
  321. end;
  322. SetString(Result[Index], F, P - F);
  323. Inc(Index);
  324. if P^ <> #0 then
  325. Inc(P, SepLen);
  326. end;
  327. if Index < ALen then
  328. SetLength(Result, Index); // wirkliche Länge festlegen
  329. end;
  330. //####################################################################################
  331. function Tfilter.MD5FromString(values:string):string;
  332. var StACrypt,StCrypt:String;
  333. Digest:TMD5Digest;
  334. begin
  335. Digest:=MD5String(values);
  336. exit(MD5Print(Digest));
  337. end;
  338. //####################################################################################
  339. function Tfilter.COMMANDLINE_PARAMETERS(FoundWhatPattern:string):boolean;
  340. var
  341. i:integer;
  342. s:string;
  343. RegExpr:TRegExpr;
  344. begin
  345. result:=false;
  346. if ParamCount>0 then begin
  347. for i:=0 to ParamCount do begin
  348. s:=s + ' ' +ParamStr(i);
  349. end;
  350. end;
  351. RegExpr:=TRegExpr.Create;
  352. RegExpr.Expression:=FoundWhatPattern;
  353. if RegExpr.Exec(s) then begin
  354. RegExpr.Free;
  355. result:=True;
  356. end;
  357. end;
  358. //####################################################################################
  359. function Tfilter.get_ARTICA_PHP_PATH():string;
  360. var path:string;
  361. begin
  362. if not DirectoryExists('/usr/share/artica-postfix') then begin
  363. path:=ParamStr(0);
  364. path:=ExtractFilePath(path);
  365. path:=AnsiReplaceText(path,'/bin/','');
  366. exit(path);
  367. end else begin
  368. exit('/usr/share/artica-postfix');
  369. end;
  370. end;
  371. //##############################################################################
  372. function Tfilter.MYSQL_ACTION_QUERY(sql:string):boolean;
  373. var root,commandline,password,cmd_result,pass:string;
  374. i:integer;
  375. D:boolean;
  376. RegExpr:TRegExpr;
  377. found:boolean;
  378. logs:Tlogs;
  379. MyRes:TstringList;
  380. QueuePath:string;
  381. FileTemp:string;
  382. database:string;
  383. begin
  384. database:='artica_filter';
  385. D:=COMMANDLINE_PARAMETERS('debug');
  386. FileTemp:=MD5FromString(sql+database)+'.sql';
  387. QueuePath:=ArticaFilterQueuePath() +'/sql_queue';
  388. ForceDirectories(QueuePath);
  389. MyRes:=TstringList.Create;
  390. MyRes.Add('<database>'+database + '</database>');
  391. MyRes.Add('<sqlquery>'+ sql + '</sqlquery>');
  392. MyRes.SaveToFile(QueuePath + '/'+FileTemp );
  393. myRes.Free;
  394. end;
  395. //##############################################################################
  396. function Tfilter.ArticaFilterQueuePath():string;
  397. var ini:TIniFile;
  398. begin
  399. ini:=TIniFile.Create('/etc/artica-postfix/artica-filter.conf');
  400. result:=ini.ReadString('INFOS','QueuePath','');
  401. if length(trim(result))=0 then result:='/usr/share/artica-filter';
  402. end;
  403. //##############################################################################
  404. end.