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 {

  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 = "0.3";
  static {
    try {
      BLOCKED_VALUE = (Inet4Address) InetAddress.getByName("127.0.0.1");
    } catch (Exception e) {
    }
   
   
    if( System.getProperty( "rbl.log" ) == null ){
      System.setProperty( "rbl.log", "/var/log/jwall-rbld.log");
    }
  }
 
  /* The global logger for this class */
  public Logger log = LoggerFactory.getLogger(RblServer.class);

  /** 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.setDaemon(true);
  }

  /**
   * 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.debug("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.info("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()) {
          config = ef.getAbsolutePath();
          break;
        }
      }
    }

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

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

    server.log.debug("RblServer.run()");
    server.run();
    server.log.debug("RBL server exiting.");
  }
}
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.