From d4992c14617d75464730affbe66be45fa07463e4 Mon Sep 17 00:00:00 2001 From: adam Date: Sat, 2 Dec 2006 08:13:30 +0000 Subject: [PATCH] added graylisting darcs-hash:20061202081330-5007d-71851f791b75341e5bc3a02aa0749f1b39c0b84f.gz --- src/org/ibex/mail/Graylist.java | 70 ++++++++++++++++++++++++++++++++++ src/org/ibex/mail/protocol/SMTP.java | 32 +++++++++++++++- 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/org/ibex/mail/Graylist.java diff --git a/src/org/ibex/mail/Graylist.java b/src/org/ibex/mail/Graylist.java new file mode 100644 index 0000000..0628062 --- /dev/null +++ b/src/org/ibex/mail/Graylist.java @@ -0,0 +1,70 @@ +package org.ibex.mail; +// todo: periodically flush out old graylist entries + +import java.sql.*; +import java.net.*; +import java.io.*; +import java.util.*; +import java.sql.Timestamp; + +public class Graylist { + + private Connection conn; + + public Graylist(String filename) { + try { + Class.forName("org.sqlite.JDBC"); + conn = DriverManager.getConnection("jdbc:sqlite:"+filename); + conn.prepareStatement("create table if not exists "+ + "'whitelist' (ip unique)").executeUpdate(); + conn.prepareStatement("create table if not exists "+ + "'graylist' (ip,fromaddr,toaddr,date, primary key(ip,fromaddr,toaddr))").executeUpdate(); + } + catch (SQLException e) { throw new RuntimeException(e); } + catch (ClassNotFoundException e) { throw new RuntimeException(e); } + } + + public synchronized void addWhitelist(InetAddress ip) { + try { + PreparedStatement add = conn.prepareStatement("insert or replace into 'whitelist' values(?)"); + add.setString(1, ip.getHostAddress()); + add.executeUpdate(); + } catch (SQLException e) { throw new RuntimeException(e); } + } + + public synchronized boolean isWhitelisted(InetAddress ip) { + try { + PreparedStatement check = conn.prepareStatement("select * from 'whitelist' where ip=?"); + check.setString(1, ip.getHostAddress()); + ResultSet rs = check.executeQuery(); + return !rs.isAfterLast(); + } catch (SQLException e) { throw new RuntimeException(e); } + } + + public synchronized long getGrayListTimestamp(InetAddress ip, String from, String to) { + try { + PreparedStatement check = + conn.prepareStatement("select date from graylist where ip=? and fromaddr=? and toaddr=?"); + check.setString(1, ip.getHostAddress()); + check.setString(2, from); + check.setString(3, to); + ResultSet rs = check.executeQuery(); + if (rs.isAfterLast()) return 0; + return rs.getTimestamp(1).getTime(); + } catch (SQLException e) { throw new RuntimeException(e); } + } + + public synchronized void setGrayListTimestamp(InetAddress ip, String from, String to, long date) { + try { + PreparedStatement check = + conn.prepareStatement("insert or replace into graylist (ip,fromaddr,toaddr,date) values(?,?,?,?)"); + check.setString(1, ip.getHostAddress()); + check.setString(2, from); + check.setString(3, to); + check.setTimestamp(4, new Timestamp(date)); + check.executeUpdate(); + } catch (SQLException e) { throw new RuntimeException(e); } + } + +} + diff --git a/src/org/ibex/mail/protocol/SMTP.java b/src/org/ibex/mail/protocol/SMTP.java index 2ba9fdc..be67a0e 100644 --- a/src/org/ibex/mail/protocol/SMTP.java +++ b/src/org/ibex/mail/protocol/SMTP.java @@ -31,6 +31,16 @@ public class SMTP { public static final SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z"); public static final int numOutgoingThreads = 5; + + public static final int GRAYLIST_MINWAIT = 1000 * 60 * 60; // one hour + public static final int GRAYLIST_MAXWAIT = 1000 * 60 * 60 * 24 * 5; // five days + + public static final Graylist graylist = + new Graylist(Mailbox.STORAGE_ROOT+"/graylist.sqlite"); + + public static final int MAX_MESSAGE_SIZE = + Integer.parseInt(System.getProperty("org.ibex.mail.smtp.maxMessageSize", "-1")); + private static final Mailbox spool = FileBasedMailbox.getFileBasedMailbox(Mailbox.STORAGE_ROOT,false).slash("spool",true).slash("smtp",true); @@ -119,7 +129,27 @@ public class SMTP { } else if (c.startsWith("DATA")) { //if (from == null) { conn.println("503 MAIL FROM command must precede DATA"); continue; } if (to == null || to.size()==0) { conn.println("503 RCPT TO command must precede DATA"); continue; } - conn.println("354 Enter message, ending with \".\" on a line by itself"); + if (!graylist.isWhitelisted(conn.getRemoteAddress()) && !conn.getRemoteAddress().isLoopbackAddress()) { + long when = graylist.getGrayListTimestamp(conn.getRemoteAddress(), from+"", to+""); + if (when == 0 || System.currentTimeMillis() - when > GRAYLIST_MAXWAIT) { + graylist.setGrayListTimestamp(conn.getRemoteAddress(), from+"", to+"", System.currentTimeMillis()); + conn.println("451 you are graylisted; please try back in one hour to be whitelisted"); + Log.warn(conn.getRemoteAddress().toString(), "451 you are graylisted; please try back in one hour to be whitelisted"); + conn.flush(); + continue; + } else if (System.currentTimeMillis() - when > GRAYLIST_MINWAIT) { + graylist.addWhitelist(conn.getRemoteAddress()); + conn.println("354 (you have been whitelisted) Enter message, ending with \".\" on a line by itself"); + Log.warn(conn.getRemoteAddress().toString(), "has been whitelisted"); + } else { + conn.println("451 you are still graylisted (since "+new java.util.Date(when)+")"); + conn.flush(); + Log.warn(conn.getRemoteAddress().toString(), "451 you are still graylisted (since "+new java.util.Date(when)+")"); + continue; + } + } else { + conn.println("354 Enter message, ending with \".\" on a line by itself"); + } conn.flush(); try { StringBuffer buf = new StringBuffer(); -- 1.7.10.4