package org.jonlin.net; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Vector; import java.util.Random; import java.io.FileWriter; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.net.InetAddress; /** * Copyright (c) 2000-2004, Comet Way, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided * that the following conditions are met: * * - Redistributions of source code must retain the * above copyright notice, this list of conditions * and the following disclaimer. * - 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. * - Neither the name of Comet Way, Inc. nor the names of * its contributors may be used to endorse or promote * products derived from this software without specific * priorwritten permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIEDWARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE * REGENTS OR CONTRIBUTORS 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; ORBUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER INCONTRACT, 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. * * Revision history: */ /** * An SMTP server that attempts to trap clients and waste the client's resources. Attempts * are made to trick the client into thinking they are talking to a real SMTP server and * to keep the client talking as long as possible, wasting its resources and keeping the * client from connecting to real open relays to send spam. */ public class SMTPHoneyPot implements Runnable { Thread runThread; Vector smartClients; SimpleDateFormat dateFormat; String displayHost; String displayVersion; String greeting; int delay; int rcptDelay; int smartClientLimit; String logFile; FileWriter logOut; String dateFormatString; static boolean verbose; public SMTPHoneyPot(String[] args) { // parse the arguments. SMTPHoneyPot.start() needs to be called explicity to start the process for(int x=0;x The hostname to display in the SMTP welcome message"); print("-displayVersion The SMTP version/OS to display in the SMTP welcome message"); print("-logfile The log file to log connections"); print("-delay The number of milliseconds of delay between each character sent to"); print(" the connecting client. If this value is too high, the client may timeout"); print("-rcptDelay The delay in milliseconds used after the RCPT command"); print("-greeting The greeting message to use after a successful EHLO from client"); print("-dateFormat The date format string to use in the log file, this must be a format string"); print(" user by the java.text.SimpleDateFormat class"); print("-smartTime The lower bound of the number of milliseconds it takes for a client IP to"); print(" be treated as a \"Smart Client\". These clients will go through the entire"); print(" SMTP dialog."); print("-v Verbose output"); print(""); print("-help Displays this help message"); verbose = false; } /** * Initialize the local variables and start the thread */ public void start() { runThread = new Thread(this); smartClients = new Vector(); if(displayHost == null) { displayHost = "localhost-127.0.0.1"; } if(displayVersion == null) { displayVersion = "6.5/OpenBSD"; } if(greeting == null) { greeting = "Hello bastards"; } if(delay==0) { delay = 500; } if(rcptDelay==0) { rcptDelay = 4000; } if(smartClientLimit==0) { smartClientLimit = 1000000; } if(logFile!=null) { try { logOut = new FileWriter(logFile,true); } catch(Exception e) { error("Could not create logfile: "+logFile,e); logFile = null; } } if(dateFormatString==null) { dateFormatString = "yyyy.MM.dd HH:mm:ss"; } dateFormat = new SimpleDateFormat(dateFormatString); runThread.start(); } /** * Perma loop to accept connections */ public void run() { ServerSocket ss = null; try { ss = new ServerSocket(25); while(true) { try { Socket s = ss.accept(); Handler h = new Handler(s); h.start(); } catch(Exception e) { error("An exception occured while handling client connection",e); } } } catch(Exception e) { error("An exception occured while opening port 25",e); } } /** * This is the handler thread which handles the SMTP dialog to an incoming client * connection. The result of this dialog is logged by the handler. */ class Handler extends Thread { Socket s; OutputStream out = null; BufferedReader in = null; public Handler(Socket client) { s = client; } public void run() { StringBuffer logString = new StringBuffer(); String ipAddress = null; long timestamp = System.currentTimeMillis(); boolean isSmartClient = false; try { s.setSoTimeout(60000); // Set up the IP address of the client, used for logging ipAddress = s.getInetAddress().toString(); print(dateFormat.format(new Date())+" - Connection from: "+ipAddress); if(ipAddress.indexOf("/")!=-1) { ipAddress = ipAddress.substring(ipAddress.indexOf("/")+1); } if(smartClients.contains(ipAddress)) { isSmartClient = true; } logString.append(dateFormat.format(new Date())); logString.append(" - "); logString.append(ipAddress); out = s.getOutputStream(); in = new BufferedReader(new InputStreamReader(s.getInputStream())); // Send the SMTP welcome message write("220 "+displayHost+" ESMTP SMTP-Server; (Version "+displayVersion+") ready.\r\n"); // read the client greeting, HELO or EHLO String input = in.readLine(); if(isSmartClient) { logString.append(" - Flagged as smart client"); write("250 "+displayHost+" "+greeting+"\r\n"); try { while(true) { input = in.readLine(); if(input==null) { break; } input = input.toLowerCase(); if(input.startsWith("mail ")) { write("250 2.1.0 Sender ok\r\n"); } else if(input.startsWith("rcpt ")) { Thread.sleep(rcptDelay); write("250 2.1.5 Recipient ok\r\n"); } else if(input.startsWith("data")) { write("354 Enter mail, end with \".\" on a line by itself\r\n"); try { while(!input.equals(".")) { input = in.readLine(); } write("250 2.0.0 Message accepted for delivery\r\n"); } catch(Exception e) { break; } } else if(input.startsWith("rset")) { write("250 2.0.0 Reset state\r\n"); } else if(input.startsWith("quit")) { // we'll attempt to NOT let the client quit try { while(true) { write("0"); } } catch(Exception e) { break; } } else { write("500 5.5.1 Command unrecognized\r\n"); } } } catch(Exception e) {;} } else { if(!input.toLowerCase().startsWith("ehlo")) { // This isn't an extended HELO, so we just send them zeros logString.append(" - HELO, sending 0's"); try { write("250 "); while(true) { write("0"); } } catch(Exception e) {;} } else { // This is the extended EHLO, we send them infinite extended SMTP commands Random rand = new Random(); char[] letters = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}; write("250-"+displayHost+" "+greeting+"\r\n"); write("250-ENHANCEDSTATUSCODES\r\n"); write("250-PIPELINING\r\n"); write("250-8BITMIME\r\n"); write("250-SIZE\r\n"); write("250-DSN\r\n"); write("250-ETRN\r\n"); write("250-AUTH PLAIN LOGIN GSSAPI KERBEROS_V4\r\n"); write("250-DELIVERBY\r\n"); while(true) { write("250-"); StringBuffer tmp = new StringBuffer(); tmp.append(letters[Math.abs(rand.nextInt())%26]); tmp.append(letters[Math.abs(rand.nextInt())%26]); tmp.append(letters[Math.abs(rand.nextInt())%26]); tmp.append(letters[Math.abs(rand.nextInt())%26]); while(Math.abs(rand.nextInt())%50!=0) { if(Math.abs(rand.nextInt())%10==0) { tmp.append(" "); } tmp.append(letters[Math.abs(rand.nextInt())%26]); } tmp.append("\r\n"); write(tmp.toString()); } } } } catch(Exception e) { // error("Exception while handling request",e); } timestamp = System.currentTimeMillis()-timestamp; logString.append(" - "+timestamp); logString.append("ms"); // If the client disconnects too early, we add them to the smart clients list if(!isSmartClient) { if(timestamp < smartClientLimit) { smartClients.addElement(ipAddress); } } try { out.close(); } catch(Exception e) {;} try { in.close(); } catch(Exception e) {;} try { s.close(); } catch(Exception e) {;} if(logFile!=null) { try { logOut.write(logString.toString()); logOut.write("\n"); logOut.flush(); } catch(Exception e) { error("Cannot write to logfile: "+logFile,e); } } } public void write(String s) throws IOException { for(int x=0;x