Package org.jwall.rbl

Source Code of org.jwall.rbl.RblServer

/*******************************************************************************
* 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;

import java.io.File;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.util.Properties;

import org.jwall.rbl.data.RBList;
import org.jwall.rbl.data.RBListEntry;
import org.jwall.rbl.data.RblFile;
import org.jwall.rbl.data.RblSettings;
import org.jwall.rbl.dns.Query;
import org.jwall.rbl.dns.QueryHandler;
import org.jwall.rbl.dns.RblSecurityManager;
import org.jwall.rbl.dns.Response;
import org.jwall.rbl.net.AdminHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
* <p>
* This class implements a very simple DNS server for running a
* real-time blackhole list. The server only responds to A records.
* Support for AAAA record queries is planned.
* </p>
*
* @author Christian Bockermann &lt;chris@jwall.org&gt;
*
*/
public class RblServer
extends Thread
{

  /* The global logger for this class */
  public static Logger log = LoggerFactory.getLogger( RblServer.class );

  public final static String RBL_HOME = "RBL_HOME";
  public final static String RBL_PORT = "rbl.port";
  public final static String RBL_ADDRESS = "rbl.address";
  public final static String RBL_ADMIN_PORT = "rbl.admin.port";
  public final static String RBL_FILE = "rbl.file";
  public final static String RBL_DOMAIN = "rbl.domain";
  public final static String RBL_PERMISSIONS = "rbl.permission.file";


  /**  These properties can be overwritten using system properties (command line)   */
  public final static String[] PROPERTY_NAMES = {
    RBL_PORT, RBL_ADDRESS, RBL_ADMIN_PORT, RBL_FILE, RBL_DOMAIN, RBL_PERMISSIONS
  };


  public static Inet4Address BLOCKED_VALUE = null;
  public final static String VERSION = "v0.1";
  static {
    try {
      BLOCKED_VALUE = (Inet4Address) InetAddress.getByName( "127.0.0.1" );
    } catch (Exception e){}
  }

  /** The block list which is served by this server */
  RBList rbl;

  /** The DNS domain name which this server handles queries for */
  String domain = "rbl.localnet";

  /** The UDP server socket for receiving queries */
  DatagramSocket socket;

  long queryCount = 0L;

  boolean running = true;
  int adminPort = -1;
  AdminHandler adminInterface;

  /** This is the query handler, which creates responses for queries */
  QueryHandler queryHandler;

  /** This is a reference to the security manager, validating block/unblock request on IP basis... */
  RblSecurityManager securityManager;
 

  /**
   * Create a new RblServer with the provided set of properties.
   *
   * @param p
   * @throws Exception
   */
  public RblServer( Properties p ) throws Exception {

    String addr = "127.0.0.1";
    Integer port = 15353;
    File rblFile = new File( File.separator + "var" + File.separator + "lib" + File.separator + "jwall-rbl" + File.separator + "local.rbl" );

    try {
      if( p.getProperty( "rbl.port" ) != null )
        port = new Integer( p.getProperty( "rbl.port" ) );
    } catch (Exception e) {
      throw new Exception( "Invalid port '" + p.getProperty( "rbl.port" ) + "' specified!" );
    }

    try {
      if( p.getProperty( "rbl.address" ) != null )
        addr = InetAddress.getByName( p.getProperty( "rbl.address" ) ).getHostAddress();
    } catch (Exception e) {
      throw new Exception( "Failed to set address '" + p.getProperty( "rbl.address" ) + "'!" );
    }

    if( p.getProperty( RBL_DOMAIN ) != null )
      domain = p.getProperty( RBL_DOMAIN );

    try {
      if( p.getProperty( "rbl.admin.port" ) != null )
        adminPort = new Integer( p.getProperty( "rbl.admin.port" ) );
    } catch (Exception e) {
    }

    try {
      if( p.getProperty( "rbl.file" ) != null )
        rblFile = new File( p.getProperty( "rbl.file" ) );
      rbl = new RblFile( rblFile );
    } catch (Exception e) {
      throw new Exception( "Failed to read rbl-list from file '" + "': " + e.getMessage() );
    }


    File policyFile = new File( File.separator + "etc" + File.separator + "jwall-rbld.permissions" );
    try {
      if( p.getProperty( RBL_PERMISSIONS ) != null )
        policyFile = new File( p.getProperty( RBL_PERMISSIONS ) );

      if( policyFile.isFile() )
        RblSecurityManager.getInstance().readPermissions( policyFile );
      else
        log.debug( "Permission file {} does not exist, updates of DNS is disabled.", policyFile.getAbsolutePath() );
    } catch (Exception e) {
      throw new Exception( "Failed to load permissions from '" + policyFile.getAbsolutePath() + "': " + e.getMessage() );
    }


    log.debug( "Starting jwall-rbld version {} for domain '{}'", VERSION, getDomain() );
    log.debug( "Listening on UDP port {}:{}", addr, port );
    queryHandler = new QueryHandler( this );
    socket = new DatagramSocket( port, InetAddress.getByName( addr ) );
    /*
    DatagramChannel channel = DatagramChannel.open();
    socket = channel.socket();
    channel.configureBlocking( true );
    socket.connect( InetAddress.getByName( addr ), port );
     */
  }


  /**
   * This method creates a new RBL server listening at the specified address
   * on the given port.
   *
   * @param addr
   * @param port
   * @throws Exception
   */
  public RblServer( InetAddress addr, Integer port ) throws Exception {
    log.debug( "Creating RblServer listening at {}, port {}", addr.getHostAddress(), port );
    queryHandler = new QueryHandler( this );
    /*
    DatagramChannel channel = DatagramChannel.open();
    socket = channel.socket();
    channel.configureBlocking( true );
    socket.connect( addr, port );
     */
    socket = new DatagramSocket( port, addr );
  }

  public void setRblSecurityManager( RblSecurityManager manager ){
    securityManager = manager;
  }
 
  public RblSecurityManager getRblSecurityManager(){
 
    if( this.securityManager != null )
      return securityManager;
   
    return RblSecurityManager.getInstance();
  }
 

  /**
   * Sets the block list for this server.
   *
   * @param list
   */
  public void setBlockList( RBList list ){
    this.rbl = list;
  }

  public RBList getBlockList(){
    return this.rbl;
  }

  public String getDomain(){
    return this.domain;
  }


  public void block( String address, Integer ttl ){

    String key = QueryHandler.getKeyForAddress( address, getDomain() );
    log.debug( "Adding rbl-entry with key = '{}'", key );
    RBListEntry entry = new RBListEntry( null, key );
    entry.setName( address );
    entry.setCreated( System.currentTimeMillis() );
    entry.setLifetime( ttl );
    getBlockList().add(entry);
  }


  public void unblock( String address ){
    String key = QueryHandler.getKeyForAddress( address, getDomain() );
    log.debug( "Removing rbl-entry with key = '{}'", key );
    getBlockList().remove( key );
  }


  /**
   * @see java.lang.Thread#run()
   */
  @Override
  public void run() {

    try {
      if( adminPort > 0 ){
        log.debug( "Starting admin interface on port {}", adminPort );
        adminInterface = new AdminHandler( this, "localhost", adminPort );
        adminInterface.start();
      } else
        log.debug( "No admin-port defined, not starting admin interface!" );
    } catch (Exception e){
      e.printStackTrace();
    }

    try {
      socket.setSoTimeout( 1000 );
    } catch (Exception e) {}
   
    while( running ){
      try {
        DatagramPacket packet = new DatagramPacket( new byte[1024], 1024 );
        log.trace( "Waiting for incoming DNS queries..." );
        socket.receive(packet);
        log.debug( "Received packet {}... size is {}", packet.getAddress() + ":" + packet.getPort(), packet.getLength() );
        byte[] data = packet.getData();
        log.debug( "Received packet of size {} bytes: {}", data.length, new String(data) );
       
        Query q = Query.parse( data, 0 );
        Response response = queryHandler.process( packet.getAddress(), q );

        byte[] re = response.toByteArray();
        DatagramPacket answer = new DatagramPacket( re, 0 );
        answer.setLength( re.length );
        answer.setAddress( packet.getAddress() );
        answer.setPort( packet.getPort() );
        socket.send( answer );
        queryCount++;
      } catch (SocketTimeoutException ste){
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
   
    log.debug( "Closing UDP socket..." );
    socket.close();
  }



  /**
   * This method is called upon server shutdown, e.g. by the
   * VM's ShutdownHook. It will save the
   *
   */
  public void shutdown(){
    log.info( "Received shutdown signal!" );
    rbl.store();

    // if there is an admin handler running, we need to stop
    // that one as well
    //
    if( adminInterface != null ){
      adminInterface.shutdown();
      try {
        adminInterface.join( 1000 );
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    log.info( "Setting running=false" );
    running = false;
   
    log.info( "Sending interrupt() signal..." );
    interrupt();
   
    log.info( "Disconnecting socket..." );
    socket.disconnect();
  }


  /**
   * <p>
   * This is the server's main entry point. It will read the configuration from
   * <code>${RBL_HOME}/etc/jwall-rbld.conf</code> if that file exists or from
   * a configuration file specified as first argument at the command line.
   * </p>
   *
   * @param args
   * @throws Exception
   */
  public static void main( String[] args ) throws Exception {

    String base = System.getenv( RBL_HOME );
    if( base == null )
      base = "";

    String config = base + File.separator + "etc" + File.separator + "jwall-rbld.conf";
    if( args.length > 0 )
      config = args[0];
    else {
      String[] etcs = new String[]{ base + "/etc/jwall-rbld.conf", "/etc/jwall-rbld.conf", "/opt/modsecurity/etc/jwall-rbld.conf" };

      for( String etc : etcs ){
        File ef = new File( etc );
        if( ef.exists() ){
          log.info( "Reading configuration from {}", ef.getAbsolutePath() );
          config = ef.getAbsolutePath();
          break;
        }
      }
    }

    final RblSettings p = RblSettings.read( config );
    final RblServer server = new RblServer( p );
    server.start();

    //
    // register a shutdown hook which will fire as this process
    // receives a TERM signal
    //
    Runtime.getRuntime().addShutdownHook( new Thread(){
      public void run(){
        server.shutdown();
      }
    });
  }
}
TOP

Related Classes of org.jwall.rbl.RblServer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.