/*
* @(#)RBLScanner.java 12/12/2004
*
* Copyright (c) 2004, 2005 jASEN.org
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the distribution.
*
* 3. The names of the authors may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 4. Any modification or additions to the software must be contributed back
* to the project.
*
* 5. Any investigation or reverse engineering of source code or binary to
* enable emails to bypass the filters, and hence inflict spam and or viruses
* onto users who use or do not use jASEN could subject the perpetrator to
* criminal and or civil liability.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JASEN.ORG,
* OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package org.jasen.plugins;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import org.apache.log4j.Logger;
import org.jasen.core.ProbabilityTestResult;
import org.jasen.core.engine.Jasen;
import org.jasen.error.JasenException;
import org.jasen.error.JasenParseException;
import org.jasen.interfaces.DNSResolver;
import org.jasen.interfaces.JasenMessage;
import org.jasen.interfaces.JasenPlugin;
import org.jasen.interfaces.JasenTestResult;
import org.jasen.interfaces.ParserData;
import org.jasen.interfaces.ReceivedHeaderParser;
import org.jasen.interfaces.ReceivedHeaderParserData;
import org.jasen.util.DNSUtils;
/**
* Checks if an SMTP server is listed on a Realtime Blackhole List (RBL).
* The RBL servers are listed in the associated property file.
*
* @author Andrew Bruno
*/
public class RBLScanner implements JasenPlugin {
static final float EMPTY = -1.0f;
private float unknown = 0.5f;
private float max = unknown;
private Vector rblRelays = null;
private Map responseMap;
private boolean checkAll = false;
private boolean absolute = false;
private int maxRbls = 20;
static Logger logger = Logger.getLogger(RBLScanner.class);
/**
*
* <p>
* Represents a response obtained from a RBL server.
* </p>
*
* @author Jason Polites
*/
public static final class RBLResponse {
String responseName;
String responseCode;
public static final RBLResponse OPEN_RELAY = new RBLResponse("OPEN_RELAY", "127.0.0.2");
public static final RBLResponse DIALUP_SPAM = new RBLResponse("DIALUP_SPAM", "127.0.0.3");
public static final RBLResponse SPAM_SOURCE = new RBLResponse("SPAM_SOURCE", "127.0.0.4");
public static final RBLResponse SMART_HOST = new RBLResponse("SMART_HOST", "127.0.0.5");
public static final RBLResponse SPAM_WARE = new RBLResponse("SPAM_WARE", "127.0.0.6");
public static final RBLResponse LIST_SERVER = new RBLResponse("LIST_SERVER", "127.0.0.7");
public static final RBLResponse FORM_MAIL = new RBLResponse("FORM_MAIL", "127.0.0.8");
public static final RBLResponse OPEN_PROXY = new RBLResponse("OPEN_PROXY", "127.0.0.9");
public static final RBLResponse UNKNOWN = new RBLResponse("UNKNOWN", null);
public static final RBLResponse[] RBL_RESPONSES = {OPEN_RELAY,
DIALUP_SPAM,
SPAM_SOURCE,
SMART_HOST,
SPAM_WARE,
LIST_SERVER,
FORM_MAIL,
OPEN_PROXY};
private RBLResponse(String responseName, String responseCode) {
this.responseName = responseName;
this.responseCode = responseCode;
}
/**
* @return Returns the responseCode.
*/
public String getResponseCode() {
return responseCode;
}
/**
* @return Returns the responseName.
*/
public String getResponseName() {
return responseName;
}
}
/* (non-Javadoc)
* @see org.jasen.interfaces.JasenPlugin#init(java.util.Properties)
*/
public void init(Properties properties) throws JasenException {
if (properties != null) {
String strUnknown = properties.getProperty("med-prob");
String strMaxRBLs = properties.getProperty("max-rbls");
String strCheckAll = properties.getProperty("check-all-headers");
String strAbsolute = properties.getProperty("absolute");
if (strUnknown != null)
unknown = Float.parseFloat(strUnknown);
if (strMaxRBLs != null)
maxRbls = Integer.parseInt(strMaxRBLs);
if (strCheckAll != null)
checkAll = new Boolean(strCheckAll).booleanValue();
if (strAbsolute != null)
absolute = new Boolean(strAbsolute).booleanValue();
responseMap = new HashMap(RBLResponse.RBL_RESPONSES.length, 1.0f);
String strResponseProb = null;
// Get the probabilities assigned to each response
float tmp = unknown;
for (int i = 0; i < RBLResponse.RBL_RESPONSES.length; i++) {
strResponseProb = properties.getProperty( RBLResponse.RBL_RESPONSES[i].getResponseName() );
if(strResponseProb != null) {
// Make sure it's a float
tmp = Float.parseFloat(strResponseProb);
if(tmp > max) max = tmp;
responseMap.put(RBLResponse.RBL_RESPONSES[i].getResponseCode(), strResponseProb);
}
}
//Get list of RBL Servers
rblRelays = new Vector(maxRbls);
String rbl = null;
for (int i = 1; i <= maxRbls; i++) {
rbl = properties.getProperty("rbl." + i);
if (rbl != null) {
rblRelays.add(rbl.trim());
}
}
}
}
/*
* (non-Javadoc)
* @see org.jasen.interfaces.JasenPlugin#destroy()
*/
public void destroy() throws JasenException {}
/* (non-Javadoc)
* @see org.jasen.interfaces.JasenPlugin#test(org.jasen.core.Jasen, javax.mail.internet.MimeMessage, org.jasen.interfaces.JasenMessage, org.jasen.interfaces.ParserData, org.jasen.interfaces.ReceivedHeaderParser)
*/
public JasenTestResult test(Jasen engine, MimeMessage rawMessage,JasenMessage parsedMessage, ParserData data,ReceivedHeaderParser parser) throws JasenException {
String[] headers;
ReceivedHeaderParserData pdata = null;
float fResult = unknown;
boolean isAbsolute = false;
ProbabilityTestResult result = new ProbabilityTestResult();
try {
headers = rawMessage.getHeader("Received");
if(headers != null) {
String header = headers[0];
if(checkAll) {
// parse all headers
for (int i = 0; i < headers.length; i++) {
if(fResult < max) {
header = headers[i];
try {
pdata = parser.parse(header,engine.getInetAddressResolver());
fResult = checkSender(pdata, engine);
if(fResult != EMPTY) {
isAbsolute = absolute;
if(isAbsolute) {
break;
}
}
}
catch (JasenParseException e) {
// couldn't parse.. just ignore it
}
}
else
{
// We have found a max result...
break;
}
}
}
else
{
header = headers[0];
// Parse the single header
try {
pdata = parser.parse(header,engine.getInetAddressResolver());
fResult = checkSender(pdata, engine);
if(fResult != EMPTY) {
isAbsolute = absolute;
}
}
catch (JasenParseException e) {
// couldn't parse.. just ignore it
}
}
}
}
catch (MessagingException e) {
throw new JasenException(e);
}
if(fResult == EMPTY) {
fResult = unknown;
}
result.setProbability(fResult);
result.setAbsolute(isAbsolute);
logger.debug("RBLScanner returned " + fResult + ", Absolute: " + isAbsolute);
return result;
}
/**
* Checks the given hostname or IP Address against the list of RBL servers defined
* @param host
* @return
* @throws JasenException
*/
protected float checkSender(ReceivedHeaderParserData data, Jasen engine) {
float response = EMPTY;
if(data != null) {
// We want to check the IP address first...
String senderIPAddress = data.getSenderIPAddress();
if(senderIPAddress != null && DNSUtils.isIPAddress(senderIPAddress)) {
// Reverse the IP
senderIPAddress = DNSUtils.invertIPAddress(senderIPAddress);
String rbl = null;
String lookup = null;
String strResult = null;
Attributes result = null;
Attribute attr = null;
DNSResolver resolver = engine.getDnsResolver();
for (int i = 0; i < rblRelays.size(); i++) {
rbl = (String)rblRelays.get(i);
lookup = senderIPAddress + "." + rbl;
try {
// Lookup
logger.debug("Looking up " + lookup);
result = resolver.lookup(lookup, "A");
if(result != null) {
// Get the returned address
attr = result.get("A");
if(attr.size() > 0 && attr.get(0) != null) {
strResult = attr.get(0).toString();
// Test the result against our list...
if(responseMap.get(strResult) != null) {
// Return the result
return Float.parseFloat((String)responseMap.get(strResult));
}
}
}
}
catch (NamingException e) {
// We couldn't find the entry...ignore this error
// This simply means the sender server is not known
// to the RBL server
}
}
}
}
return response;
}
}