/*******************************************************************************
* 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;
/**
* <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 {
/* 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;
}
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" ) ) {
RblServer.log.info( "Client connect from {} rejected. Allowing localhost connections only!", client.getInetAddress().getHostAddress() );
client.close();
return;
}
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;
Socket socket;
boolean running = true;
public ClientHandler( Socket socket, RblServer server ){
this.setDaemon( true );
this.socket = socket;
this.server = server;
}
public void run(){
try {
BufferedReader r = 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 = r.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;
}
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();
}
}
}
}