/*******************************************************************************
* Copyright (C) 2010 Christian Bockermann <chris@jwall.org>
*
* This file is part of the jwall-rbld program. jwall-rbld is an implementation
* of a simple DNS server for running a local, customized real time block-list.
* More information and documentation for the jwall-rbld can be found at
*
* http://www.jwall.org/jwall-rbld
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this
* program; if not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package org.jwall.rbl.net;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import org.jwall.rbl.RblServer;
import org.jwall.rbl.data.RBListEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>
* This thread is a simple text-based tcp client, which accepts connections from
* localhost only and provides a very small set of simple commands to manage the
* RB-list of this server.
* </p>
*
* @author Christian Bockermann <chris@jwall.org>
*
*/
public class AdminHandler extends Thread {
static Logger log = LoggerFactory.getLogger(AdminHandler.class);
/* The rbl-server this handler manages */
RblServer server;
/* The server socket */
ServerSocket socket;
/* A flag indicating whether the handler is running or not */
boolean running = true;
/**
* Creates a new AdminHandler for the specified server, listening on the
* given address and port.
*
* @param server
* @param addr
* @param port
* @throws Exception
*/
public AdminHandler(RblServer server, String addr, int port)
throws Exception {
this.socket = new ServerSocket(port);
this.server = server;
this.setDaemon(true);
}
public void run() {
while (running) {
try {
Socket client = socket.accept();
String remote = client.getInetAddress().getHostAddress();
if (!(remote.startsWith("127.0.0") || remote
.equals("0:0:0:0:0:0:0:1")) || remote.equals("::1")) {
log.info("Client connect from {} rejected. Allowing localhost connections only!",
client.getInetAddress().getHostAddress());
client.close();
} else {
ClientHandler handler = new ClientHandler(client, server);
handler.start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void shutdown() {
running = false;
this.interrupt();
}
/**
* This is a per-connection thread for handling a connection.
*
* @author Christian Bockermann <chris@jwall.org>
*
*/
public class ClientHandler extends Thread {
RblServer server;
PrintStream out;
BufferedReader reader;
Socket socket;
boolean running = true;
public ClientHandler(Socket socket, RblServer server) {
this.setDaemon(true);
this.socket = socket;
this.server = server;
}
public void run() {
try {
reader = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
out = new PrintStream(socket.getOutputStream());
out.println("\n jwall.org RBL Server " + RblServer.VERSION);
out.print(" ---------------------");
for (int i = 0; i < RblServer.VERSION.length(); i++)
out.print("-");
out.println("\n");
out.println(" Welcome to the jwall-rbl server, Version "
+ RblServer.VERSION);
out.println(" This interface allows you to manage the block-list of this server.");
out.println(" Type 'help' to get an overview of the available commands!");
while (running && socket.isConnected()) {
out.print("\n> ");
String line = reader.readLine();
if (line != null) {
eval(line.trim());
}
}
out.println("# Bye!");
out.flush();
out.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void eval(String command) {
String[] args = command.split("\\s+");
if (args.length == 0)
return;
if ("help".startsWith(args[0])) {
doHelp();
return;
}
if ("list".startsWith(args[0])) {
doList(args);
return;
}
if ("search".startsWith(args[0])) {
doSearch(args);
return;
}
if ("block".startsWith(args[0])) {
doBlock(args);
return;
}
if ("unblock".startsWith(args[0])) {
doUnblock(args);
return;
}
if ("quit".startsWith(args[0]) || "exit".startsWith(args[0])) {
running = false;
return;
}
if ("shutdown".startsWith(args[0])) {
doShutdown(args);
}
out.println("Unknown command '" + args[0] + "'!");
}
public void doHelp() {
out.println(" This is a very simple interface for adding/removing entries");
out.println(" from the server's block list.");
out.println(" Any changes to the list will immediately affect the server's");
out.println(" response, thus it may be used for instant blocking.\n");
out.println(" The following commands are availble:\n");
out.println(" 'search' - simply lists all active entries, matching a given");
out.println(" expression. '*' and '?' can be used as wildcards\n");
out.println(" 'list' - Lists all entries currently active.\n");
out.println(" 'block' - Creates a new list-entry. By default an entry will");
out.println(" be blocked for 60 seconds. You may additionally specify");
out.println(" any other number of seconds before this entry expires.\n");
out.println(" 'unblock' - Removes an entry from the list.");
}
public void doList(String[] args) {
doSearch(new String[] { "search", "*" });
return;
}
public void doBlock(String[] args) {
if (args.length == 1) {
out.println("");
out.println(" You need to specify an additional IP address to block!");
out.println(" Example:");
out.println(" block 172.16.0.1\n");
out.println(" You may also specify the number of seconds until the block ");
out.println(" expires, e.g.");
out.println(" block 172.16.0.1 180\n");
return;
}
try {
String[] tok = args[1].split("\\.");
StringBuffer key = new StringBuffer();
for (int i = tok.length - 1; i >= 0; i--) {
key.append(tok[i]);
if (i > 0)
key.append(".");
}
if (!server.getDomain().startsWith("."))
key.append(".");
key.append(server.getDomain());
RBListEntry entry = new RBListEntry(null, key.toString());
entry.setName(args[1]);
entry.setCreated(System.currentTimeMillis());
try {
if (args.length > 2)
entry.setLifetime(new Integer(args[2]));
} catch (Exception e) {
entry.setLifetime(60);
}
entry.setComment("");
server.getBlockList().add(entry);
out.println("# Address '" + entry.getName()
+ "' added to block-list.");
} catch (Exception e) {
out.println("# Failed to create address '" + args[1] + "' - "
+ e.getMessage());
e.printStackTrace();
return;
}
}
public void doUnblock(String[] args) {
try {
String[] tok = args[1].split("\\.");
StringBuffer key = new StringBuffer();
for (int i = tok.length - 1; i >= 0; i--) {
key.append(tok[i]);
if (i > 0)
key.append(".");
}
if (!server.getDomain().startsWith("."))
key.append(".");
key.append(server.getDomain());
server.getBlockList().remove(key.toString());
out.println("# Removed '" + key.toString()
+ "' from block list.");
} catch (Exception e) {
out.println("Failed to remove address from block list: "
+ e.getMessage());
e.printStackTrace();
}
}
public void doSearch(String[] args) {
if (args.length == 1) {
out.println("\n The 'search' command needs an additional query string!");
return;
}
try {
List<RBListEntry> results = server.getBlockList().search(
args[1]);
for (RBListEntry entry : results) {
long expired = (entry.getExpiresAt() - System
.currentTimeMillis()) / 1000;
String expires = " expires in " + expired + " seconds.";
if (expired < 0) {
expires = " expired " + Math.abs(expired)
+ " seconds ago.";
if (Math.abs(expired) > 120) {
server.getBlockList().remove(entry.getName());
continue;
}
}
out.println(" " + entry.getName() + " " + expires);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void doShutdown(String args[]) {
out.print("Do you really want to shut down the RBL daemon? (Y/N) ");
try {
String line = reader.readLine();
if (line != null && line.trim().matches("(Y|y)")) {
out.println();
out.println("Sending 'shutdown' command to server...");
server.shutdown();
running = false;
} else {
out.println("\nYou need to enter 'Y' or 'y' to shut down the RBL server.");
}
} catch (Exception e) {
log.error("Error: {}", e.getMessage());
if (log.isDebugEnabled())
e.printStackTrace();
}
}
}
}