001 /****************************************************************
002 * Licensed to the Apache Software Foundation (ASF) under one *
003 * or more contributor license agreements. See the NOTICE file *
004 * distributed with this work for additional information *
005 * regarding copyright ownership. The ASF licenses this file *
006 * to you under the Apache License, Version 2.0 (the *
007 * "License"); you may not use this file except in compliance *
008 * with the License. You may obtain a copy of the License at *
009 * *
010 * http://www.apache.org/licenses/LICENSE-2.0 *
011 * *
012 * Unless required by applicable law or agreed to in writing, *
013 * software distributed under the License is distributed on an *
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
015 * KIND, either express or implied. See the License for the *
016 * specific language governing permissions and limitations *
017 * under the License. *
018 ****************************************************************/
019
020 package org.apache.james.protocols.smtp.core.fastfail;
021
022 import java.util.Iterator;
023
024 import org.apache.james.protocols.smtp.SMTPRetCode;
025 import org.apache.james.protocols.smtp.SMTPSession;
026 import org.apache.james.protocols.smtp.dsn.DSNStatus;
027 import org.apache.james.protocols.smtp.hook.HookResult;
028 import org.apache.james.protocols.smtp.hook.HookReturnCode;
029 import org.apache.james.protocols.smtp.hook.RcptHook;
030 import org.apache.mailet.MailAddress;
031
032
033 /**
034 * Abstract base class which implement GreyListing.
035 *
036 *
037 */
038 public abstract class AbstractGreylistHandler implements RcptHook {
039
040 /** 1 hour */
041 private long tempBlockTime = 3600000;
042
043 /** 36 days */
044 private long autoWhiteListLifeTime = 3110400000L;
045
046 /** 4 hours */
047 private long unseenLifeTime = 14400000;
048
049
050
051 public void setUnseenLifeTime(long unseenLifeTime) {
052 this.unseenLifeTime = unseenLifeTime;
053 }
054
055 public void setAutoWhiteListLifeTime(long autoWhiteListLifeTime) {
056 this.autoWhiteListLifeTime = autoWhiteListLifeTime;
057 }
058
059 public void setTempBlockTime(long tempBlockTime) {
060 this.tempBlockTime = tempBlockTime;
061 }
062
063
064 private HookResult doGreyListCheck(SMTPSession session, MailAddress senderAddress, MailAddress recipAddress) {
065 String recip = "";
066 String sender = "";
067
068 if (recipAddress != null) recip = recipAddress.toString();
069 if (senderAddress != null) sender = senderAddress.toString();
070
071 long time = System.currentTimeMillis();
072 String ipAddress = session.getRemoteIPAddress();
073
074 try {
075 long createTimeStamp = 0;
076 int count = 0;
077
078 // get the timestamp when he triplet was last seen
079 Iterator<String> data = getGreyListData(ipAddress, sender, recip);
080
081 if (data.hasNext()) {
082 createTimeStamp = Long.parseLong(data.next());
083 count = Integer.parseInt(data.next());
084 }
085
086 session.getLogger().debug("Triplet " + ipAddress + " | " + sender + " | " + recip +" -> TimeStamp: " + createTimeStamp);
087
088
089 // if the timestamp is bigger as 0 we have allready a triplet stored
090 if (createTimeStamp > 0) {
091 long acceptTime = createTimeStamp + tempBlockTime;
092
093 if ((time < acceptTime) && (count == 0)) {
094 return new HookResult(HookReturnCode.DENYSOFT, SMTPRetCode.LOCAL_ERROR, DSNStatus.getStatus(DSNStatus.TRANSIENT, DSNStatus.NETWORK_DIR_SERVER)
095 + " Temporary rejected: Reconnect to fast. Please try again later");
096 } else {
097
098 session.getLogger().debug("Update triplet " + ipAddress + " | " + sender + " | " + recip + " -> timestamp: " + time);
099
100 // update the triplet..
101 updateTriplet(ipAddress, sender, recip, count, time);
102
103 }
104 } else {
105 session.getLogger().debug("New triplet " + ipAddress + " | " + sender + " | " + recip );
106
107 // insert a new triplet
108 insertTriplet(ipAddress, sender, recip, count, time);
109
110 // Tempory block on new triplet!
111 return new HookResult(HookReturnCode.DENYSOFT, SMTPRetCode.LOCAL_ERROR, DSNStatus.getStatus(DSNStatus.TRANSIENT, DSNStatus.NETWORK_DIR_SERVER)
112 + " Temporary rejected: Please try again later");
113 }
114
115 // some kind of random cleanup process
116 if (Math.random() > 0.99) {
117 // cleanup old entries
118
119 session.getLogger().debug("Delete old entries");
120
121 cleanupAutoWhiteListGreyList(time - autoWhiteListLifeTime);
122 cleanupGreyList(time - unseenLifeTime);
123 }
124
125 } catch (Exception e) {
126 // just log the exception
127 session.getLogger().error("Error on greylist method: " + e.getMessage());
128 }
129 return new HookResult(HookReturnCode.DECLINED);
130 }
131
132 /**
133 * Get all necessary data for greylisting based on provided triplet
134 *
135 * @param ipAddress
136 * The ipAddress of the client
137 * @param sender
138 * The mailFrom
139 * @param recip
140 * The rcptTo
141 * @return data
142 * The data
143 * @throws Exception
144 */
145 protected abstract Iterator<String> getGreyListData(String ipAddress, String sender, String recip) throws Exception;
146
147 /**
148 * Insert new triplet in the store
149 *
150 * @param ipAddress
151 * The ipAddress of the client
152 * @param sender
153 * The mailFrom
154 * @param recip
155 * The rcptTo
156 * @param count
157 * The count
158 * @param createTime
159 * The createTime
160 * @throws SQLException
161 */
162 protected abstract void insertTriplet(String ipAddress, String sender, String recip, int count, long createTime)
163 throws Exception;
164
165 /**
166 * Update the triplet
167 *
168 *
169 * @param ipAddress
170 * The ipAddress of the client
171 * @param sender
172 * The mailFrom
173 * @param recip
174 * The rcptTo
175 * @param count
176 * The count
177 * @param time
178 * the current time in ms
179 * @throws Exception
180 */
181 protected abstract void updateTriplet(String ipAddress, String sender, String recip, int count, long time) throws Exception;
182
183
184 /**
185 * Cleanup the autowhitelist
186 *
187 * @param time
188 * The time which must be reached before delete the records
189 * @throws Exception
190 */
191 protected abstract void cleanupAutoWhiteListGreyList(long time)throws Exception;
192
193 /**
194 * Delete old entries from the Greylist datarecord
195 *
196 * @param time
197 * The time which must be reached before delete the records
198 * @throws Exception
199 */
200 protected abstract void cleanupGreyList(long time) throws Exception;
201
202
203
204 /**
205 * @see org.apache.james.protocols.smtp.hook.RcptHook#doRcpt(org.apache.james.protocols.smtp.SMTPSession, org.apache.mailet.MailAddress, org.apache.mailet.MailAddress)
206 */
207 public HookResult doRcpt(SMTPSession session, MailAddress sender, MailAddress rcpt) {
208 if (!session.isRelayingAllowed()) {
209 return doGreyListCheck(session, sender,rcpt);
210 } else {
211 session.getLogger().info("IpAddress " + session.getRemoteIPAddress() + " is allowed to send. Skip greylisting.");
212 }
213 return new HookResult(HookReturnCode.DECLINED);
214 }
215 }