#!/usr/bin/perl
# This file is part of PWHOIS.
# #
# # The PWHOIS software provided in this Distribution is
# # Copyright 2005 VOSTROM Holdings, Inc.
# #
# # The full text of our legal notices is contained in the file called
# # COPYING, included with this Distribution
use strict;
use DBI;
use Time::Format qw(time_format time_strftime time_manip);
use Getopt::Long;
use Log::Dispatch;
use Log::Dispatch::Screen;
use Log::Dispatch::File;
use Net::Patricia;
use Net::Socket::NonBlock;
use POSIX;

package Pwhoisd;

# globals
my $DEBUG = 'false';		# force debugging on: automatically turned on if verbose>=2
my $VERSION = '1.1.0.32';	# the version number to print out at various screens
my $PROGNAME = 'Prefix WhoIs';	# title of our program
my $COPYRIGHT = 'Copyright (c) 2005 VOSTROM Holdings, Inc.'; # copyright holder

# DO NOT MODIFY UNLESS YOU KNOW WHAT YOU ARE DOING: EDIT THE CONFIG FILE IF POSSIBLE
# See: /etc/pwhois/pwhoisd.conf or -c option to specify different config file

my $DEFAULT_CONFIG = '/etc/pwhois/pwhoisd.conf';
my $DEFAULT_WHOIS_PORT=43;
my $DEFAULT_MAX_QUERIES=5000;
my $DEFAULT_PIDFILE='/var/run/pwhoisd.pid';
my $DEFAULT_DATABASE_TYPE = 'postgres';
my $DEFAULT_DATABASE_SERVER = '';
my $DEFAULT_DATABASE_USER = 'pwhois';
my $DEFAULT_DATABASE_PASSWD = '';
my $DEFAULT_DATABASE_NAME = 'pwhois';
my $DEFAULT_UID=65334;
my $DEFAULT_GID=65334;
my $DEFAULT_ROUTER_ID=0;	# used to control which (view) of the data this pwhois server looks at

# DO NOT EDIT BELOW THIS LINE

my $dbh;
my $dsn = 'NA';		# do not edit: these are changed later from defaults or config
my $user = 'NA';	# do not edit: these are changed later from defaults or config
my $password = 'NA';    # do not edit: these are changed later from defaults or config

my $select_from_bgp_routes_sth;
my $select_from_bgp_routes_by_routerid_sth;
my $select_from_bgp_routes_by_prefix_sth;
my $select_from_bgp_routes_by_prefix_bestonly_sth;
my $select_from_bgp_routes_by_prefix_by_routerid_sth;
my $select_from_bgp_routes_by_prefix_by_routerid_bestonly_sth;
my $select_from_bgp_routes_by_prefix_nocidr_sth;
my $select_from_bgp_routes_by_prefix_nocidr_bestonly_sth;
my $select_from_bgp_routes_by_prefix_nocidr_by_routerid_sth;
my $select_from_bgp_routes_by_prefix_nocidr_by_routerid_bestonly_sth;
my $select_from_bgp_routes_by_source_as_sth;
my $select_from_bgp_routes_by_source_as_bestonly_sth;
my $select_from_bgp_routes_by_source_as_by_routerid_sth;
my $select_from_bgp_routes_by_source_as_by_routerid_bestonly_sth;
my $select_from_pwhois_acl_sth;
my $select_from_bgp_routes_next_hops_sth;
my $select_from_bgp_routes_next_hops_by_routerid_sth;
my $select_orgname_by_as_sth;
my $select_netname_by_route_sth;
my $select_netblock_by_sourceas_sth;
my $select_netblock_by_orgid_sth;
my $select_netblock_by_netname_sth;
my $select_netblock_by_nethandle_sth;
my $select_registry_details_by_orgid_sth;
my $select_registry_details_by_orgname_sth;
my $select_registry_details_by_sourceas_sth;
my $select_registry_details_by_pochandle_sth;

my $server_connections_paused=0;

my %routes = ();  # routing table (hash)
my %peers = ();   # peer routers
my %config = ();  # configuration options

my $pt = new Net::Patricia;	# RIB/FIB (fast search)
my %owner = ();
my %connections = ();		# connection tracking
my %requests = ();		# request tracking (sessions)
my $socket;
my $last_table_update;		# date of last RIB update
my $program_start_date;		# date program started
my %counters = ();		# keeps track of the number of different statistics
# initialize some of the counters -- does not include hosts that are in the ACL
$counters{'whois'} = 0;					# number of queries for whois requests
$counters{'routeview.prefix'} = 0;		# number of queries for routeview prefix requests
$counters{'routeview.source-as'} = 0;	# number of queries for routeview source-as requests
$counters{'netblock.source-as'} = 0;	# number of queries for netblock source-as requests
$counters{'netblock.org-id'} = 0;	    # number of queries for netblock org-id requests
$counters{'netblock.net-name'} = 0;		# number of queries for netblock net-name requests
$counters{'netblock.net-handle'} = 0;   # number of queries for netblock net-handle requests
$counters{'registry.source-as'} = 0;	# number of queries for registry source-as requests
$counters{'registry.org-id'} = 0;	    # number of queries for registry org-id requests
$counters{'registry.org-name'} = 0;	    # number of queries for registry org-name requests
$counters{'registry.poc-handle'} = 0;   # number of queries for registry poc-handle requests
$counters{'peers'} = 0;					# number of queries for the peers
$counters{'hosts'} = 0;					# number of queries from unique hosts (regardless of query type)

my $INVALID_INPUT="Sorry, I don't like your input.  You may ask for 'help'";
my $NOT_AUTHORIZED="Sorry, this feature is not currently available.  The server is either too busy, or you are not authorized.";
# enviroment

$ENV{'TZ'} = 'GMT';

#log handle
my $log;
my %opt = ();

# install signal handlers
$SIG{HUP} = \&reload;
$SIG{INT} = \&shutdown;
$SIG{TERM} = \&shutdown;
$SIG{KILL} = \&shutdown;
$SIG{QUIT} = 'IGNORE';    # ignore SIGQUIT
$SIG{__WARN__} =\&handle_warn;
#$SIG{__DIE__} = \&handle_error;

sub new {
   my $self = {};
   my $class = shift;
   return bless _init($self), $class
}

sub _init {
   my $self = shift;

   processArgs();
  # connect to the database
   openConfigFile();
   openDatabase();
   return $self
}

sub processArgs
{
        # change our process name in the PS list
        $0 = "pwhoisd";

        Getopt::Long::Configure('no_ignore_case');
        Getopt::Long::GetOptions(\%opt, 'help|h', 'logfile|l=s', 
					'version|V', 'verbose|v+', 
					'configfile|c=s',
					'port|p=s', 'bind|b=s', 
					'uid|u=n', 'gid|g=n',
					'daemon|d', 'pidfile=s',
					'limit-max-queries|mq=n',
					'no-load',		# testing
					'router-id|r=s'		# controls which router-id to select data from in the database
                ) or die "Can't parse command-line options";
        usage() if $opt{help};


	# config file
	if(!defined($opt{'configfile'})) {
		$opt{'configfile'} = $DEFAULT_CONFIG;
	}

        # set version output to 0
        if(!defined($opt{verbose})) {
                $DEBUG = 'false';
		$opt{verbose} = 0;
        }
	else
	{
		if($opt{verbose} >= 2) {
                        $DEBUG = 'true';
                }
	}	

	if($opt{logfile}) {
	        #  open our log file ... nothing should be written to stdout or stderr
        	# from now on

		if(! -f $opt{logfile}) {
			system("touch ".$opt{logfile});
		}        	
		$log = Log::Dispatch::File->new( name      => 'file1',
                                       min_level => 'debug',
                                       filename  => "$opt{logfile}",
                                       mode      => 'append');
                                       
        }
        else
        {
                  $log = Log::Dispatch::Screen->new( name      => 'screen',
                                                      min_level => 'debug',
                                                      stderr    => 0 );
        }

	if(!defined($opt{'bind'})) {
		$opt{'bind'} = '';
	}

	# set the default port listenn on
	if(!defined($opt{'port'})) {
		$opt{'port'} = $DEFAULT_WHOIS_PORT;
	}

	if(!defined($opt{'uid'})) {
		$opt{'uid'} = $DEFAULT_GID;
	}

	if(!defined($opt{'gid'})) {
		$opt{'gid'} = $DEFAULT_UID;
	}

	# set the limits on maximum queries per host IP/per day
	if(!defined($opt{'limit-max-queries'})) {
		$opt{'limit-max-queries'} = $DEFAULT_MAX_QUERIES;
	}

	if($opt{version}) {
                print getVersion(1) . "\n";
                exit;
        }
	
	if($opt{daemon}) {
                 daemonize();
	}
	$program_start_date = time();
        $log->log( level => 'info', message => "$0 started at ". getDateTimeFormat(time()) ."\n") if $opt{verbose};
}


sub usage
{
        print "usage: $0 [*options*]\n\n",
              "  -h, --help         display this help and exit\n",
              "  -v, --verbose      be verbose about what you do (add more -v's to increase verbosity: above v=2 is considered debug)\n",
              "  -V, --version      output version information and exit\n",
              "  -l, --logfile f    write misc progress output to logfile instead of stdout\n",
	      "  -c, --configfile f   read startup settings from configuration file: default is $DEFAULT_CONFIG\n",
	      "  -d, --daemon       start in the background\n",
	      "  --pidfile	    use alternative PID file location: default is /var/run/pwhoisd.pid\n",
	      "  -p, --port <n>	    port number to listen on: defaults to 43\n",
	      "  --b|bind <ip>      the IP address to bind on: defaults to all interfaces (*)\n",
	      "  -u, --uid          the effective user to run as \n",
	      "  -g, --gid          the effective group to run as \n",
	      "  --limit-max-queries <n>  The maximum number of queries (per IP/per day) default is $DEFAULT_MAX_QUERIES\n",
	      "  -r, --router-id <id>  the router id to use for this server; useful if there is more that one set of data\n",
	      "     in the database and the server should only serve responses from one set of data.\n",
	      "  --no-load          Do not load data -- for testing purposes\n";
        exit;
}

sub daemonize()
{
	# create default options
	if(!defined($opt{pidfile})) {
		$opt{pidfile} = $DEFAULT_PIDFILE;
	}

	# fork and save the process ID
 	defined(my $pid = fork) or die "$0: can't fork: $!";
 	if($pid) {
		# parent
		open PIDFILE, ">".$opt{pidfile}
		or die "$0: can't write to ". $opt{pidfile} .": $!\n";
		print PIDFILE "$pid\n";
		close(PIDFILE);
		exit;
	}
	# child
	POSIX::setsid or die "$0: can't start a new session: $!";
}


# error or warning occurred, bail out
sub handle_warn($)
{
        my $msg = shift;
	if(defined($log)) {
	        $log->log(level=>'warning', message=>"Warning: $msg\n") if $opt{verbose} >= 1;
	}
	else
	{
	        print STDERR "Warning: $msg\n" if $opt{verbose} >= 1;

	}
}

sub handle_error($)
{
        my $msg = shift;
	if(defined($log)) {
	        $log->log(level=>'error', message=>"Error: $msg\n") if $opt{verbose} >= 1;
	}
	else
	{
	        print STDERR "Error: $msg\n" if $opt{verbose} >= 1;

	}
        &shutdown;
}

sub shutdown
{
	if(defined($log)) {
        	$log->log(level=>'info', message=>getCurrentSyslogDateTime() . " $0 shutdown requested.\n") if $opt{verbose};
	}
	# close our database connection
        closeDatabase();
	# close our listening socket
	foreach my $con (keys %connections) {
		$socket->Close($con);
	}
	$socket->Close() if defined($socket);
	undef $socket;
        exit(0);
}

sub reload()
{
	$log->log(level=>"info", message=>getCurrentSyslogDateTime() ." $0 refresh signal received.  Reloading database\n") if $opt{verbose} >= 1;

	# wait for any existing connections to complete?
	# pause communication

	$server_connections_paused=1;
	load_data();
	$server_connections_paused=0;
}


sub openConfigFile()
{
	if(defined($opt{'configfile'})) {
		if(-r $opt{'configfile'}) {
			open(CONFIG, $opt{'configfile'}) or die "Can't open config file: $!";
			while(<CONFIG>)
			{
				
				next if $_ =~ /^\s*#.*$/;  # skip comments
				next if $_ =~ /^\s+$/;	# skip blank lines
				
				my ($name,$value) = ($_ =~ /^\s*([A-z0-9_\-.]+) ?= ?"?([^"\r\n]*)"? ?$/); 
				my $display_value = $value;
				if($name =~ /^.*\.password$/i) {
					$display_value = '<hidden>';
				}
				$log->log(level=>"info", message=>"Found setting $name => $display_value\n") if $opt{verbose} >=1;
				
				$config{$name} = $value;				
			}
			close(CONFIG);
		}
	}
	else
	{
						
	}
	
	# configure based upon configuration settings
	
	my $database = $DEFAULT_DATABASE_TYPE;
	my $dbserver = $DEFAULT_DATABASE_SERVER;
	my $dbuser = $DEFAULT_DATABASE_USER;
	my $dbpasswd = $DEFAULT_DATABASE_PASSWD;
	my $dbname = $DEFAULT_DATABASE_NAME;
	
	if(defined($config{'db.type'})) {
		$database = $config{'db.type'};	
	}
	
	if(defined($config{'db.server'})) {
		$dbserver = $config{'db.server'};	
	}
	
	if(defined($config{'db.name'})) {
		$dbname = $config{'db.name'};	
	}
	
	if(defined($config{'db.user'})) {
		$dbuser = $config{'db.user'};
		$user = $dbuser;	
	}

	if(defined($config{'db.password'})) {
		$dbpasswd = $config{'db.password'};
		$password = $dbpasswd;
	}
	
	# configure the DSN
	
	if($database eq 'postgres' or 
	   $database eq 'pgsql' or 
	   $database eq 'postgresql')
	{
		if($dbserver ne '' and $dbserver ne 'localhost') {
				$dsn = "dbi:Pg:dbname=$dbname;host=$dbserver";
		}
		else {  # use the Unix Domain Socket (for localhost access)
			$dsn = "dbi:Pg:dbname=$dbname";
		}
	}
	elsif($database eq 'mysql')
	{
		$dsn = "dbi:mysql:dbname=$dbname:$dbserver";		
	}
	else
	{
		$log->log(level=>"error", message=>"Invalid database type specified '$database'\n");
		exit(-1);
	}
	
	
	# configure other settings (config-file options override command-line)
	if(defined($config{'pwhoisd.verbose'}))
	{
		if($config{'pwhoisd.verbose'} =~ /\d+/) {
			if($config{'pwhoisd.verbose'} > $opt{verbose}) {	
				$opt{'verbose'} = $config{'pwhoisd.verbose'};
			}
		}
		else
		{
			$log->log(level=>"error", message=>"Invalid configuration option value for 'pwhoisd.verbose'\n");
			exit(-1);
		}
	}
	
	
	if(defined($config{'pwhoisd.logfile'}))
	{
		if($config{'pwhoisd.logfile'} =~ /[A-z0-9\-_\/ \.]+/) {
			$opt{'logfile'} = $config{'pwhoisd.logfile'};

  			if($opt{logfile}) {
                	#  open our log file ... nothing should be written to stdout or stderr
                	# from now on

                		if(! -f $opt{logfile}) {
                        	system("touch ".$opt{logfile});
                		}
						if(defined($log)) {
							$log->log(level=>"info", message=>"Log messages are now being written to log file: ". $opt{logfile} ."\n");
						}
                		$log = Log::Dispatch::File->new(name      => 'file1',
								min_level => 'debug',
								filename  => $opt{logfile},
								mode      => 'append');
			}		
		}
		else
		{
			$log->log(level=>"error", message=>"Invalid configuration option value for 'pwhoisd.logfile'\n");
			exit(-1);
		}
	}
	
	if(defined($config{'pwhoisd.pidfile'}))
	{
		if($config{'pwhoisd.pidfile'} =~ /[A-z0-9\-_\/ \.]+/) {
			$opt{'pidfile'} = $config{'pwhoisd.pidfile'};
		}
		else
		{
			$log->log(level=>"error", message=>"Invalid configuration option value for 'pwhoisd.pidfile'\n");
			exit(-1);
		}
	}
	
	if(defined($config{'pwhoisd.bind'}))
	{
		if($config{'pwhoisd.bind'} =~ /\d{1,3}\.\d{1.3}\.\d{1,3}\.\d{1.3}/) {
			$opt{'bind'} = $config{'pwhoisd.bind'};
		}
		else
		{
			$log->log(level=>"error", message=>"Invalid configuration option value for 'pwhoisd.bind'\n");
			exit(-1);
		}
	}
	
	if(defined($config{'pwhoisd.port'}))
	{
		if($config{'pwhoisd.port'} =~ /\d+/) {
			$opt{'port'} = $config{'pwhoisd.port'};
		}
		else
		{
			$log->log(level=>"error", message=>"Invalid configuration option value for 'pwhoisd.port'\n");
			exit(-1);
		}
	}
	
	if(defined($config{'pwhoisd.uid'}))
	{
		if($config{'pwhoisd.uid'} =~ /\d+/) {
			$opt{'uid'} = $config{'pwhoisd.uid'};
		}
		else
		{
			$log->log(level=>"error", message=>"Invalid configuration option value for 'pwhoisd.uid'\n");
			exit(-1);
		}
	}
	
	if(defined($config{'pwhoisd.gid'}))
	{
		if($config{'pwhoisd.gid'} =~ /\d+/) {
			$opt{'gid'} = $config{'pwhoisd.gid'};
		}
		else
		{
			$log->log(level=>"error", message=>"Invalid configuration option value for 'pwhoisd.gid'\n");
			exit(-1);
		}
	}
	
	if(defined($config{'pwhoisd.default.queries.max'}))
	{
		if($config{'pwhoisd.default.queries.max'} =~ /\d+/) {
			$opt{'limit-max-queries'} = $config{'pwhoisd.default.queries.max'};
		}
		else
		{
			$log->log(level=>"error", message=>"Invalid configuration option value for 'pwhoisd.default.queries.max'\n");
			exit(-1);
		}
	}

	if(defined($config{'pwhoisd.router-id'}))
	{
		$opt{'router-id'} = $config{'pwhoisd.router-id'};
	}
}
# open the database and create prepared statements
sub openDatabase()
{	
	$dbh = DBI->connect($dsn, $user, $password,
                  { RaiseError => 1, AutoCommit => 1 }) 
		or die "Can't open database: $DBI::errstr";

	$log->log(level=>"debug", message=>"Database opened dsn=$dsn\n") if $DEBUG eq 'true'; 

	$select_from_bgp_routes_sth = $dbh->prepare(qq(
				SELECT id,router_id,network,cidr,next_hop,asn,asn_paths,createDate,modifyDate,status,best_route
				FROM bgp_routes 
				WHERE best_route=1 AND status=1 ORDER BY network ASC
		))
		or die "Can't prepare statement: $DBI::errstr";
	
	$select_from_bgp_routes_by_routerid_sth = $dbh->prepare(qq(
				SELECT id,router_id,network,cidr,next_hop,asn,asn_paths,createDate,modifyDate,status,best_route
				FROM bgp_routes 
				WHERE best_route=1 AND status=1 AND router_id=? ORDER BY network ASC
		))
		or die "Can't prepare statement: $DBI::errstr";

	$select_from_bgp_routes_by_prefix_sth = $dbh->prepare(qq(
				SELECT next_hop,asn,asn_paths,createDate,modifyDate,best_route 
				FROM bgp_routes
				WHERE status=1 AND network=? AND cidr=? 
				ORDER BY best_route DESC, next_hop ASC, asn ASC
		))
		or die "Can't prepare statement: $DBI::errstr";

	# select all the routes by prefix which are best-routes only
	$select_from_bgp_routes_by_prefix_bestonly_sth = $dbh->prepare(qq(
				SELECT next_hop,asn,asn_paths,createDate,modifyDate,best_route 
				FROM bgp_routes
				WHERE status=1 AND best_route=1 AND network=? AND cidr=? 
				ORDER BY best_route DESC, next_hop ASC, asn ASC
		))
		or die "Can't prepare statement: $DBI::errstr";


	# select all the routes by prefix only for the one router-id
	$select_from_bgp_routes_by_prefix_by_routerid_sth = $dbh->prepare(qq(
                SELECT next_hop,asn,asn_paths,createDate,modifyDate,best_route 
				FROM bgp_routes
                WHERE status=1 AND network=? AND cidr=? AND router_id=? 
                ORDER BY best_route DESC, next_hop ASC, asn ASC
		))
		or die "Can't prepare statement: $DBI::errstr";

	# select all the routes by prefix only for the one router-id and are best-routes only
	$select_from_bgp_routes_by_prefix_by_routerid_bestonly_sth = $dbh->prepare(qq(
                SELECT next_hop,asn,asn_paths,createDate,modifyDate,best_route 
				FROM bgp_routes
                WHERE status=1 AND best_route=1 AND network=? AND cidr=? AND router_id=? 
                ORDER BY best_route DESC, next_hop ASC, asn ASC
		))
		or die "Can't prepare statement: $DBI::errstr";

	# select all the routes by prefix (without CIDR)
   	$select_from_bgp_routes_by_prefix_nocidr_sth = $dbh->prepare(qq(
                SELECT next_hop,asn,asn_paths,createDate,modifyDate,best_route 
				FROM bgp_routes
				WHERE status=1 AND network=?
				ORDER BY best_route DESC, next_hop ASC, asn ASC
		))
		or die "Can't prepare statement: $DBI::errstr";

	# select all the routes by prefix (without CIDR) and are best-routes only
   	$select_from_bgp_routes_by_prefix_nocidr_bestonly_sth = $dbh->prepare(qq(
                SELECT next_hop,asn,asn_paths,createDate,modifyDate,best_route 
				FROM bgp_routes
				WHERE status=1 AND best_route=1 AND network=?
				ORDER BY best_route DESC, next_hop ASC, asn ASC
		))
		or die "Can't prepare statement: $DBI::errstr";

	# select all the routes by prefix (without CIDR) only for the one router-id
  	$select_from_bgp_routes_by_prefix_nocidr_by_routerid_sth = $dbh->prepare(qq(
                SELECT next_hop,asn,asn_paths,createDate,modifyDate,best_route 
				FROM bgp_routes
                WHERE status=1 AND network=? AND router_id=?
                ORDER BY best_route DESC, next_hop ASC, asn ASC
		))
		or die "Can't prepare statement: $DBI::errstr";

	# select all the routes by prefix (without CIDR) only for the one router-id and are best-routes only
  	$select_from_bgp_routes_by_prefix_nocidr_by_routerid_bestonly_sth = $dbh->prepare(qq(
                SELECT next_hop,asn,asn_paths,createDate,modifyDate,best_route 
				FROM bgp_routes
                WHERE status=1 AND best_route=1 AND network=? AND router_id=?
                ORDER BY best_route DESC, next_hop ASC, asn ASC
		))
		or die "Can't prepare statement: $DBI::errstr";

	# search for all routes by source-as
 	$select_from_bgp_routes_by_source_as_sth = $dbh->prepare(qq(
                SELECT network,cidr,next_hop,asn,asn_paths,createDate,modifyDate,best_route 
				FROM bgp_routes
				WHERE status=1 AND asn=? 
				ORDER BY best_route DESC, next_hop ASC, asn_paths ASC
		))
		or die "Can't prepare statement: $DBI::errstr";

	# search for all routes by source-as that are only marked best-route
	$select_from_bgp_routes_by_source_as_bestonly_sth = $dbh->prepare(qq(
                SELECT network,cidr,next_hop,asn,asn_paths,createDate,modifyDate,best_route 
				FROM bgp_routes
				WHERE status=1 AND best_route=1 AND asn=? 
				ORDER BY best_route DESC, next_hop ASC, asn_paths ASC
		))
		or die "Can't prepare statement: $DBI::errstr";

	# search for all routes by source-as only from the one router-id
	$select_from_bgp_routes_by_source_as_by_routerid_sth = $dbh->prepare(qq(
                SELECT network,cidr,next_hop,asn,asn_paths,createDate,modifyDate,best_route 
				FROM bgp_routes
                WHERE status=1 AND asn=? AND router_id=?
                ORDER BY best_route DESC, next_hop ASC, asn_paths ASC
		))
		or die "Can't prepare statement: $DBI::errstr";

	# search for all routes by source-as only from the one router-id and only best routes
	$select_from_bgp_routes_by_source_as_by_routerid_bestonly_sth = $dbh->prepare(qq(
                SELECT network,cidr,next_hop,asn,asn_paths,createDate,modifyDate,best_route 
				FROM bgp_routes
                WHERE status=1 AND best_route=1 AND asn=? AND router_id=?
                ORDER BY best_route DESC, next_hop ASC, asn_paths ASC
		))
		or die "Can't prepare statement: $DBI::errstr";

	$select_from_pwhois_acl_sth = $dbh->prepare(qq(
				SELECT ip,max_count,status FROM pwhois_acl WHERE status>=0
		)) or die "Can't prepare statement: $DBI::errstr";

	# receive a list of the routers we have received routes from
	# or are announcing "active" best route prefixes from the route view feed
	$select_from_bgp_routes_next_hops_sth = $dbh->prepare(qq(
				SELECT next_hop FROM bgp_routes WHERE best_route=1 AND status=1
				GROUP BY next_hop ORDER BY next_hop ASC
		)) or die "Can't prepare statement: $DBI::errstr"; 

  	# receive a list of the routers we have received routes from
	# or are announcing "active" best route prefixes from the route view feed (only from the one router-id)
	$select_from_bgp_routes_next_hops_by_routerid_sth = $dbh->prepare(qq(
				SELECT next_hop FROM bgp_routes WHERE best_route=1 AND status=1 AND router_id=?
				GROUP BY next_hop ORDER BY next_hop ASC
		)) or die "Can't prepare statement: $DBI::errstr";

	# database version 2
	# TODO: add a check to make sure we don't execute this statement unless database version >= 2
	
	# retrieve the Org-ID associated with the AS we find
	$select_orgname_by_as_sth = $dbh->prepare(qq(
				SELECT organization.orgName FROM organization,asn WHERE organization.org_id=asn.org_id AND asn.asn=?
		)) or die "Can't prepare statement: $DBI::errstr";

	$select_netname_by_route_sth = $dbh->prepare(qq(
				SELECT netName FROM netblock WHERE network=?
		)) or die "Can't prepare statement: $DBI::errstr";

	# search for the netblocks registered searching by the source-as provided
	$select_netblock_by_sourceas_sth = $dbh->prepare(qq(
				SELECT netName,netRange,netType,netblock.registerDate,netblock.updateDate,netblock.techHandle,
					   netblock.org_id,organization.orgName,netblock.createDate,netblock.modifyDate
				FROM   netblock,organization,asn WHERE organization.org_id=asn.org_id AND netblock.org_id=organization.org_id AND asn.asn=?
				ORDER BY network ASC 
		)) or die "Can't prepare statement: $DBI::errstr";

	# search for the netblocks registered searching by the org-id provided
	$select_netblock_by_orgid_sth = $dbh->prepare(qq(
				SELECT netName,netRange,netType,netblock.registerDate,netblock.updateDate,netblock.techHandle,
					   organization.org_id,organization.orgName,asn.asn,netblock.createDate,netblock.modifyDate
				FROM   netblock,organization,asn WHERE netblock.org_id=organization.org_id AND asn.org_id=organization.org_id AND organization.org_id=?
				ORDER BY network ASC 
		)) or die "Can't prepare statement: $DBI::errstr";

	# search for the netblocks registered searching by the net-name provided
	$select_netblock_by_netname_sth = $dbh->prepare(qq(
				SELECT netName,netRange,netType,registerDate,updateDate,techHandle,createDate,modifyDate
				FROM netblock WHERE netName=?
				ORDER BY network ASC 
		)) or die "Can't prepare statement: $DBI::errstr";

    # search for the netblocks registered searching by the net-handle provided
	$select_netblock_by_nethandle_sth = $dbh->prepare(qq(
				SELECT netName,netRange,netType,registerDate,updateDate,techHandle,createDate,modifyDate
				FROM netblock WHERE netHandle=?
				ORDER BY network ASC 
		)) or die "Can't prepare statement: $DBI::errstr";


	# get the registry details for the org-id provided
	$select_registry_details_by_orgid_sth = $dbh->prepare(qq(
				SELECT orgName,canAllocate,street1,street2,street3,street4,street5,street6,
					   city,state,country,postalCode,registerDate,updateDate,comment,referralServer,
			           adminHandle,nocHandle,abuseHandle,techHandle,createDate,modifyDate
				FROM   organization WHERE org_id=?
		)) or die "Can't prepare statement: $DBI::errstr";
	
	# get the registry details for the org-id provided
	$select_registry_details_by_orgname_sth = $dbh->prepare(qq(
				SELECT org_id,orgName,canAllocate,street1,street2,street3,street4,street5,street6,
					   city,state,country,postalCode,registerDate,updateDate,comment,referralServer,
			           adminHandle,nocHandle,abuseHandle,techHandle,createDate,modifyDate
				FROM   organization WHERE orgName ~* ?
		)) or die "Can't prepare statement: $DBI::errstr";
	
	# get the registry details for the org-id provided
	$select_registry_details_by_sourceas_sth = $dbh->prepare(qq(
				SELECT organization.org_id,orgName,canAllocate,street1,street2,street3,street4,street5,street6,
					   city,state,country,postalCode,organization.registerDate,organization.updateDate,organization.comment,referralServer,
			           organization.adminHandle,organization.nocHandle,organization.abuseHandle,organization.techHandle,
					   organization.createDate,organization.modifyDate
				FROM   organization,asn WHERE organization.org_id=asn.org_id AND asn.asn=?
		)) or die "Can't prepare statement: $DBI::errstr";

	# get the registry details for the point of contact by the poc-handle provided
	$select_registry_details_by_pochandle_sth = $dbh->prepare(qq(
				SELECT pocHandle,isRole,firstName,middleName,lastName,roleName,street1,street2,street3,street4,street5,street6,
					   city,state,country,postalCode,registerDate,updateDate,comment,officePhone,mailbox,createDate,modifyDate
				FROM   poc WHERE pocHandle=?
		)) or die "Can't prepare statement: $DBI::errstr";
}

# close down the database connection
sub closeDatabase()
{
	if(defined($log)) {
		$log->log(level=>"debug", message=>"Database closed dsn=$dsn\n") if $DEBUG eq 'true';
	}
	undef $select_from_bgp_routes_sth;
	undef $select_from_bgp_routes_by_routerid_sth;
	undef $select_from_bgp_routes_by_prefix_sth;
	undef $select_from_bgp_routes_by_prefix_bestonly_sth;
	undef $select_from_bgp_routes_by_prefix_by_routerid_sth;
	undef $select_from_bgp_routes_by_prefix_by_routerid_bestonly_sth;
	undef $select_from_bgp_routes_by_prefix_nocidr_sth;
	undef $select_from_bgp_routes_by_prefix_nocidr_bestonly_sth;
	undef $select_from_bgp_routes_by_prefix_nocidr_by_routerid_sth;
	undef $select_from_bgp_routes_by_prefix_nocidr_by_routerid_bestonly_sth;
	undef $select_from_bgp_routes_by_source_as_sth;
	undef $select_from_bgp_routes_by_source_as_bestonly_sth;
	undef $select_from_bgp_routes_by_source_as_by_routerid_sth;
	undef $select_from_bgp_routes_by_source_as_by_routerid_bestonly_sth;
	undef $select_from_pwhois_acl_sth;
	undef $select_from_bgp_routes_next_hops_sth;
	undef $select_from_bgp_routes_next_hops_by_routerid_sth;
	undef $select_orgname_by_as_sth;
	undef $select_netname_by_route_sth;
	undef $select_netblock_by_sourceas_sth;
	undef $select_netblock_by_netname_sth;
	undef $select_netblock_by_nethandle_sth;
	undef $select_netblock_by_orgid_sth;
	undef $select_registry_details_by_orgid_sth;
	undef $select_registry_details_by_orgname_sth;
	undef $select_registry_details_by_sourceas_sth;
	undef $select_registry_details_by_pochandle_sth;
	$dbh->disconnect if defined($dbh);
}

sub getHelp()
{
return "$PROGNAME ($VERSION) $COPYRIGHT\n".
qq{
Help is here: 

DESCRIPTION

       Prefix WhoIs displays the origin-as and other interesting information
related to the most specific prefix currently advertised within the
Internet's global routing table that corresponds to the IP address in
your query.

       The only mandatory parameter is an IP address (optionally            
in CIDR notation, though pwhois assumes a /32 prefix).  You may
provide IP addresses with port numbers, though the port numbers will be 
removed and not returned to you in the result.  You may optionally use 
the "type" operator to change the display format between the default pwhois 
format, RPSL (RFC 2622), and the format used by Cymru (see www.cymru.com).


STANDARD QUERY CONSTRUCTION

       [type=pwhois|cymru|rpsl] <ip_address[/bits]>

BULK QUERIES

Prefix WhoIs supports bulk queries using optional commands.  Only
native and Cymru display types support bulk output.  When submitting a bulk 
query, simply make the term "bulk" or "begin" the first item/line sent in 
your query.  Then, you may optionally set the "type" attribute and then 
enter one IP address per line.  To signify the end of your query, simply 
provide "quit" or "end" as the last line of your query.

EXAMPLES (single query)
				               
       "1.2.3.4"  or  "type=cymru 1.2.3.4"  or  "type=rpsl 1.2.3.4"

EXAMPLES (bulk query)
					               
       begin                   begin
       type=cymru              1.2.3.4:80
       1.2.3.4                 5.6.7.8 
       5.6.7.8/32              ...
       ...                     end
       end

The "netcat" may be used to easily submi bulk queries.  To do so, simply
write your query to a file and concatenate the file contents into
netcat like so:

       \$ netcat <any-pwhois-server> 43 < ./ip_list.txt

Our "WhoB" whois client and our lightweight whois library also support bulk 
queries. Both are available at http://www.pwhois.org/

HELP AND STATUS QUERIES

       The "help" command or "?" displays this help text.

       The "version" command displays the PWHOIS server version
and other details such as the date of the last routing table cache update and 
the number of prefixes in the global table.

        The "peers" command displays the (route server's) peer IP addresses 
from which the PWHOIS server is receiving data.

        These queries are simply:

        [help|version|peers]

SOFTWARE

	You may download this source code and run your own PWHOIS server.  See 
http://www.pwhois.org/ for more details.

DISCLAIMER

	The PWHOIS service is provided for informational purposes only.  We do 
not guarantee its accuracy. By submitting a query, you agree to abide by the 
following terms of use: the compilation, repackaging, dissemination or other 
use of this data is expressly prohibited without our prior written consent. 
You agree not to use electronic processes that are automated to access or 
query this database except as reasonably necessary.  We reserve the right to 
restrict your access to this database in our sole discretion.  We may 
restrict or terminate your access to this database for failure to abide by 
these terms of use.  We reserve the right to modify these terms at any time.

AUTHORS AND THANKS

	The PWHOIS service was created and is maintained by the following
individuals and organizations, and wouldn't be possible without their time, 
energy, and on-going support. Thanks!

   Zachary Kanner, Victor Oppleman, Robb Ballard, Rob Thomas and Team Cymru,
   Rodney Joffe, and Brett Watson.
   
QUESTIONS OR COMMENTS

	Please send questions or comments about this service to:

	pwhois-support\@pwhois.org -- someone will get back to you shortly.
};
}

sub getExtraHelp()
{
return qq{
EXTRA SPECIAL COMMANDS 

	Another interesting query is the "route view" query which displays 
all the active routes in the global routing table cache (at the time of the 
last routing table update) for the prefix specified.

	A query with this feature may look like:

        "routeview prefix=1.2.3.4[/8]" 

	Another form is to search by source-as, showing all the prefixes being
announced by this source.

        "routeview source-as=12345"
		
	Optionally, these commands may be augmented with two type qualifiers, 
best and all, which display only the best routes, or all the routes, respectively.

If the type qualifier is not specified, the default behavior is 'best'.
		
	[type=best|all]
		
	A query with this feature may look like:
		
	"type=all routeview source-as=12345"
		
		or
		
	"type=best routeview prefix=1.2.3.4/24"
	
	Another command option, is to use to the netblock command to search for
registration information for the source-as, net-name, net-handle, or organization you are looking for.

	Queries with this feature may look like:
	
	"netblock source-as=12345"  or  "netblock net-name=JKA-123"  or
			
	"netblock net-handle=NET-1-0-0-0-1"  or  "netblock org-id=ABC-123"
	
	Registry information may be searched for and displayed directly (including contact
information) by searching for either the org-id, org-name, poc-handle, or source-as.

	Queries in this form may look as follows:
	
	"registry org-id=ABC-123"  or  "registry org-name=VOSTROM"  or
	
	"registry poc-handle=JKA-123"  or  "registry source-as=12345"
	
	
	Another command for "debugging" and other monitoring, is the statistics command.

	A command of this form may look like this:
	
	"statistics"
};
}

sub getVersion
{	
	my $cmd_line = shift;
	my $ip = shift;
	my $table_update;
	my $start_date;
	
	if(defined($last_table_update)) {
		$table_update = getDateTimeFormat($last_table_update);
	}
	else
	{
		$table_update = '(pending)';
	}
	
	if(defined($program_start_date)) {
		$start_date = getDateTimeFormat($program_start_date);
	}
	else
	{
		$start_date = '(pending)';
	}
	
	
	if(!$cmd_line) {
		if($opt{'bind'} ne '') {
	                        $ip = $opt{'bind'};
		}
	}
	else
	{
		if($opt{'bind'} ne '') {		
			$ip = $opt{'bind'};
		}
	}
		
return "$PROGNAME ($VERSION) $COPYRIGHT\n\n".
       
       "Server running on $ip:". $opt{'port'} ."\n".
       "Global BGP routing table cache last updated: $table_update\n".
       "Cache contains ". scalar(keys %routes) ." global prefixes from ". scalar(keys %peers) ." peers\n".
       "Server responded to ". $counters{'whois'} . " requests from ". $counters{'hosts'} ." unique IPs since: ". $start_date;
       
}

# get the routeview data from the database searching by prefix 
# (after getting the most specific network from the FIB first)
# based upon the IP address specified
sub getRouteviewPrefixByIP($$)
{
	my ($ip, $search_type) = @_;
	my $cidr;
              
	# find the most specific prefix from the FIB -- use this prefix found
	my ($result, $network, $asn, $asn_paths, $cache_date) = findInNetwork($ip);
	if($result) {
		($ip,$cidr) = ipv4_parse($network);
	}
	else
	{
		return "Route for $ip not found in global routing table";
	}
	return getRouteviewPrefixSearchByCIDR($ip, $cidr, $search_type);
}

sub getRouteviewPrefixByCIDR($$$)
{
	my ($ip,$cidr,$search_type) = @_;
	my $response;
	if(ipv4_is_valid_quaddot($ip .'/'. $cidr))
	{
		# some of the database entries do not have CIDR length, as classful routing is in Global RIB/FIB
		# our FIB has the default prefix length tacked on, but we need to search without the CIDR or we won't
		# find the data in the database -- this shouldn't matter as it is an exact network match search.
		
		my $sth;		
			
		if(defined($opt{'router-id'}))
		{
			if($search_type eq 'best')
			{
				$log->log(level=>"debug", message=>"Searching for routes by prefix=". $ip ."/". $cidr ." router-id=". $opt{'router-id'} . " and only best-routes\n") if opt{'verbose'} >= 4;
				$sth = $select_from_bgp_routes_by_prefix_nocidr_by_routerid_bestonly_sth;
				$sth->execute(ipv4_quaddot_to_decimal($ip), ipv4_quaddot_to_decimal($opt{'router-id'}));
				#$sth = $select_from_bgp_routes_by_prefix_by_routerid_bestonly_sth;
				#$sth->execute(ipv4_quaddot_to_decimal($ip), $cidr, ipv4_quaddot_to_decimal($opt{'router-id'}));
			}
			else
			{
				$log->log(level=>"debug", message=>"Searching for routes by prefix=". $ip ."/". $cidr ." router-id=". $opt{'router-id'} . " and all routes\n") if opt{'verbose'} >= 4;
				$sth = $select_from_bgp_routes_by_prefix_nocidr_by_routerid_sth;
				$sth->execute(ipv4_quaddot_to_decimal($ip), ipv4_quaddot_to_decimal($opt{'router-id'}));
				#$sth = $select_from_bgp_routes_by_prefix_by_routerid_sth;
				#$sth->execute(ipv4_quaddot_to_decimal($ip), $cidr, ipv4_quaddot_to_decimal($opt{'router-id'}));
			}
		}
		else
		{
			if($search_type eq 'best')
			{
				$log->log(level=>"debug", message=>"Searching for routes by prefix=". $ip ."/". $cidr ." and only best-routes\n") if $opt{'verbose'} >= 4;
				$sth =  $select_from_bgp_routes_by_prefix_nocidr_bestonly_sth;
				$sth->execute(ipv4_quaddot_to_decimal($ip));
				#$sth =  $select_from_bgp_routes_by_prefix_bestonly_sth;
				#$sth->execute(ipv4_quaddot_to_decimal($ip), $cidr);
			}
			else
			{
				$log->log(level=>"debug", message=>"Searching for routes by prefix=". $ip ."/". $cidr ." and all routes\n") if $opt{'verbose'} >= 4;
				$sth =  $select_from_bgp_routes_by_prefix_nocidr_sth;
				$sth->execute(ipv4_quaddot_to_decimal($ip));
				#$sth =  $select_from_bgp_routes_by_prefix_sth;
				#$sth->execute(ipv4_quaddot_to_decimal($ip), $cidr);
			}
		}
								
		my $count=0;
		while(my($next_hop, $asn, $asn_paths, $createDate, $modifyDate, $best_route) = $sth->fetchrow_array())
		{
			my $line;
				
			if($count == 0)
			{
				$response = "\n".
						    "Origin-AS: $asn\n".
						    "   Prefix: $ip/$cidr\n\n".
					
						    "    Create-Date             | Modify-Date              | Next-Hop        | AS-Path\n";
			}
				
			if($best_route)
			{
				$line = sprintf("*> %+24s | %+24s | %+15s | %-s\n", 
							    getDateTimeFormat($createDate), 
							    getDateTimeFormat($modifyDate), 
							    ipv4_decimal_to_quaddot($next_hop) , $asn_paths);
			}
			else
			{
				$line = sprintf("*  %+24s | %+24s | %+15s | %-s\n", 
							    getDateTimeFormat($createDate), 
							    getDateTimeFormat($modifyDate), 
							    ipv4_decimal_to_quaddot($next_hop) , $asn_paths);
				
			}
			$response .= $line;
			$count++;
		}

		$sth->finish();

		if($response eq '')
		{
			$response = "No prefixes found in routeview database for prefix=$ip/$cidr";
		}
	}
	else
	{
		$log->log(level=>"debug", message=>"Invalid ip address specified: $ip\n") if $DEBUG eq 'true';
		$response=$INVALID_INPUT; 
	}
	return $response;
}

# get the routes being announced by this source-as
sub getRouteviewBySourceAS($$)
{
	my ($source_as, $search_type) = @_;
	my $response;
	my $sth;
	
	if(defined($opt{'router-id'}))
	{
		if($search_type eq 'best')
		{
			$log->log(level=>"debug", message=>"Searching for routes by source-as=". $source_as ." router-id=". $opt{'router-id'} 
												." and only best-routes\n") if $opt{'verbose'} >= 4;
												
			$sth = $select_from_bgp_routes_by_source_as_by_routerid_bestonly_sth;
			$sth->execute($source_as, ipv4_quaddot_to_decimal($opt{'router-id'}));
		}
		else
		{
			$log->log(level=>"debug", message=>"Searching for routes by source-as=". $source_as ." router-id=". $opt{'router-id'} 
												." and all routes\n") if $opt{'verbose'} >= 4;
												
			$sth = $select_from_bgp_routes_by_source_as_by_routerid_sth;
			$sth->execute($source_as, ipv4_quaddot_to_decimal($opt{'router-id'}));
		}
	}
	else
	{
		if($search_type eq 'best')
		{
			$log->log(level=>"debug", message=>"Searching for routes by source-as=". $source_as ." and only best-routes\n") if $opt{'verbose'} >= 4;
			$sth = $select_from_bgp_routes_by_source_as_bestonly_sth;
			$sth->execute($source_as);
		}
		else
		{
			$log->log(level=>"debug", message=>"Searching for routes by source-as=". $source_as ." and all routes\n") if $opt{'verbose'} >= 4;
			$sth = $select_from_bgp_routes_by_source_as_sth;
			$sth->execute($source_as);
		}
	}

	my $count=0;
	while(my($prefix, $network_cidr, $next_hop, $asn, $asn_paths, $createDate, $modifyDate, $best_route) = $sth->fetchrow_array())
	{
		my $line;
		my $network;

		if(defined($network_cidr))
		{
			$network = ipv4_decimal_to_quaddot($prefix) .'/'. $network_cidr;
		}
		else
		{
			$network = ipv4_decimal_to_quaddot($prefix);
		}

		if($count == 0)
		{
			$response = "\n".
					    "Origin-AS: $asn\n\n".

					    "    Prefix            | Create-Date              | Modify-Date              | Next-Hop        | AS-Path\n";
		}

		if($best_route)
		{
			$line = sprintf("*> %+18s | %+24s | %+24s | %+15s | %-s\n",
							$network,	
							getDateTimeFormat($createDate),
							getDateTimeFormat($modifyDate),
							ipv4_decimal_to_quaddot($next_hop) , $asn_paths);
		}
		else
		{
			$line = sprintf("*  %+18s | %+24s | %+24s | %+15s | %-s\n",
							$network,
							getDateTimeFormat($createDate),
							getDateTimeFormat($modifyDate),
							ipv4_decimal_to_quaddot($next_hop) , $asn_paths);
		}
		$response .= $line;
		$count++;
	}
	$sth->finish();

	if($response eq '')
	{
		$response = "No prefixes found in routeview database for source-as=$source_as";
	}
	return $response;
}

# get the networks registered to this source-as
sub getNetblockBySourceAS($$)
{
	my ($source_as, $search_type) = @_;
	my $response;
	my $sth;
	
	$log->log(level=>"debug", message=>"Searching for netblocks by source-as=". $source_as ."\n") if $opt{'verbose'} >= 4;
												
	$select_netblock_by_sourceas_sth->execute($source_as);
	
	my $count=0;
	while(my($netName,$netRange,$netType,$registerDate,$updateDate,$techHandle,$orgId,$orgName,$createDate, $modifyDate) = 
			$select_netblock_by_sourceas_sth->fetchrow_array())
	{
		my $line;
		my $netTypeName = 'unknown';
		if($netType == 1) {
			$netTypeName = 'assignment';
		}
		elsif($netType == 2) {
			$netTypeName = 'reassignment';
		}
		elsif($netType == 3) {
			$netTypeName = 'rir' ;
		}
		elsif($netType == 4) {
			$netTypeName = 'allocation';
		}
		
		if($count == 0)
		{
			$response = "\n".
					    "Origin-AS: $source_as\n".
						"Org-ID: $orgId\n".
						"Org-Name: $orgName\n\n".

					    "    Net-Range                              | Net-Name             | Net-Type      | Register-Date | Update-Date | Create-Date              | Modify-Date              \n";
		}

		$line = sprintf("*> %+39s | %+20s | %+14s | %+13s | %+11s | %+24s | %+24s\n",
							$netRange,	
							$netName,
							$netTypeName,
							$registerDate,
							$updateDate,
							getDateTimeFormat($createDate),
							getDateTimeFormat($modifyDate));
		$response .= $line;
		$count++;
	}
	$select_netblock_by_sourceas_sth->finish();

	if($response eq '')
	{
		$response = "No netblocks found in registry database for source-as=$source_as";
	}
	return $response;
}

# get the networks registered to this net-name
sub getNetblockByNetName($$)
{
	my ($net_name, $search_type) = @_;
	my $response;
	my $sth;
	
	$log->log(level=>"debug", message=>"Searching for netblocks by net-name=". $net_name ."\n") if $opt{'verbose'} >= 4;
												
	$select_netblock_by_netname_sth->execute($net_name);
	
	my $count=0;
	while(my($netName,$netRange,$netType,$registerDate,$updateDate,$techHandle,$createDate,$modifyDate) = $select_netblock_by_netname_sth->fetchrow_array())
	{
		my $line;
		my $netTypeName = 'unknown';
		if($netType == 1) {
			$netTypeName = 'assignment';
		}
		elsif($netType == 2) {
			$netTypeName = 'reassignment';
		}
		elsif($netType == 3) {
			$netTypeName = 'rir' ;
		}
		elsif($netType == 4) {
			$netTypeName = 'allocation';
		}
		
		if($count == 0)
		{
			$response = "\n".

					    "    Net-Range                              | Net-Name             | Net-Type       | Register-Date | Update-Date | Create-Date              | Modify-Date              \n";
		}

		$line = sprintf("*> %+39s | %+20s | %+14s | %+13s | %+11s | %+24s | %+24s\n",
							$netRange,	
							$netName,
							$netTypeName,
							$registerDate,
							$updateDate,
							getDateTimeFormat($createDate),
							getDateTimeFormat($modifyDate));
		$response .= $line;
		$count++;
	}
	$select_netblock_by_netname_sth->finish();

	if($response eq '')
	{
		$response = "No netblocks found in registry database for net-name=$net_name";
	}
	return $response;
}

# get the networks registered to this net-handle
sub getNetblockByNetHandle($$)
{
	my ($net_handle, $search_type) = @_;
	my $response;
	my $sth;
	
	$log->log(level=>"debug", message=>"Searching for netblocks by net-handle=". $net_handle ."\n") if $opt{'verbose'} >= 4;
												
	$select_netblock_by_nethandle_sth->execute($net_handle);
	
	my $count=0;
	while(my($netName,$netRange,$netType,$registerDate,$updateDate,$techHandle,$createDate,$modifyDate) = $select_netblock_by_nethandle_sth->fetchrow_array())
	{
		my $line;
		my $netTypeName = 'unknown';
		if($netType == 1) {
			$netTypeName = 'assignment';
		}
		elsif($netType == 2) {
			$netTypeName = 'reassignment';
		}
		elsif($netType == 3) {
			$netTypeName = 'rir' ;
		}
		elsif($netType == 4) {
			$netTypeName = 'allocation';
		}
		
		if($count == 0)
		{
			$response = "\n".

					    "    Net-Range                              | Net-Name             | Net-Type       | Register-Date | Update-Date | Create-Date              | Modify-Date              \n";
		}

		$line = sprintf("*> %+39s | %+20s | %+14s | %+13s | %+11s | %+24s | %+24s\n",
							$netRange,	
							$netName,
							$netTypeName,
							$registerDate,
							$updateDate,
							getDateTimeFormat($createDate),
							getDateTimeFormat($modifyDate));
		$response .= $line;
		$count++;
	}
	$select_netblock_by_nethandle_sth->finish();

	if($response eq '')
	{
		$response = "No netblocks found in registry database for net-handle=$net_handle";
	}
	return $response;
}

# get the networks registered to this source-as
sub getNetblockByOrgID($$)
{
	my ($org_id, $search_type) = @_;
	my $response;
	my $sth;
	
	$log->log(level=>"debug", message=>"Searching for netblocks by org-id=". $org_id ."\n") if $opt{'verbose'} >= 4;
												
	$select_netblock_by_orgid_sth->execute($org_id);
	
	my $count=0;
	while(my($netName,$netRange,$netType,$registerDate,$updateDate,$techHandle,$orgId,$orgName,$asn,$createDate,$modifyDate) = 
			$select_netblock_by_orgid_sth->fetchrow_array())
	{
		my $line;
		my $netTypeName = 'unknown';
		if($netType == 1) {
			$netTypeName = 'assignment';
		}
		elsif($netType == 2) {
			$netTypeName = 'reassignment';
		}
		elsif($netType == 3) {
			$netTypeName = 'rir' ;
		}
		elsif($netType == 4) {
			$netTypeName = 'allocation';
		}
		
		if($count == 0)
		{
			$response = "\n".
					    "Origin-AS: $asn\n".
						"Org-ID: $org_id\n".
						"Org-Name: $orgName\n\n".

					    "    Net-Range                              | Net-Name             | Net-Type       | Register-Date | Update-Date | Create-Date              | Modify-Date              \n";
		}

		$line = sprintf("*> %+39s | %+20s | %+14s | %+13s | %+11s | %+24s | %+24s\n",
							$netRange,	
							$netName,
							$netTypeName,
							$registerDate,
							$updateDate,
							getDateTimeFormat($createDate),
							getDateTimeFormat($modifyDate));
		$response .= $line;
		$count++;
	}
	$select_netblock_by_orgid_sth->finish();

	if($response eq '')
	{
		$response = "No netblocks found in registry database for org-id=$org_id";
	}
	return $response;
}

# get the registry details for the Org specified
sub getRegistryDetailsForOrgID($$)
{
	my ($org_id, $search_type) = @_;
	my $response;
	my $sth;
	
	$log->log(level=>"debug", message=>"Searching for registry details by org-id=". $org_id ."\n") if $opt{'verbose'} >= 4;
												
	$select_registry_details_by_orgid_sth->execute($org_id);
	
	my $count=0;
	while(my($orgName,$canAllocate,$street1,$street2,$street3,$street4,$street5,$street6,
	         $city,$state,$country,$postalCode,$registerDate,$updateDate,$comment,$referralServer,
			 $adminHandle,$nocHandle,$abuseHandle,$techHandle,$createDate,$modifyDate) = 
			$select_registry_details_by_orgid_sth->fetchrow_array())
	{
		my $line = "Org-ID: $org_id\n".
		           "Org-Name: $orgName\n".
			       "Can-Allocate: $canAllocate\n";
		   $line .= "Street-1: $street1\n" if defined($street1);
		   $line .= "Street-2: $street2\n" if defined($street2);
		   $line .= "Street-3: $street3\n" if defined($street3);
		   $line .= "Street-4: $street4\n" if defined($street4);
		   $line .= "Street-5: $street5\n" if defined($street5);
		   $line .= "Street-6: $street6\n" if defined($street6);
		   $line .= "City: $city\n" if defined($city);
		   $line .= "State: $state\n" if defined($state);
		   $line .= "Postal-Code: $postalCode\n" if defined($postalCode);
		   $line .= "Country: $country\n" if defined($country);
		   $line .= "Register-Date: $registerDate\n" if defined($registerDate);
		   $line .= "Update-Date: $updateDate\n" if defined($updateDate);
		   $line .= "Create-Date: ". getDateTimeFormat($createDate) ."\n" if defined($createDate);
		   $line .= "Modify-Date: ". getDateTimeFormat($modifyDate) ."\n" if defined($modifyDate);
		   $line .= "Admin-Handle: $adminHandle\n" if defined($adminHandle);
		   $line .= "NOC-Handle: $nocHandle\n" if defined($nocHandle);
		   $line .= "Abuse-Handle: $abuseHandle\n" if defined($abuseHandle);
		   $line .= "Tech-Handle: $techHandle\n" if defined($techHandle);
		   $line .= "Referral-Server: $referralServer\n" if defined($referralServer);
		   $line .= "Comment: $comment\n" if defined($comment);
		$response .= $line;
		$count ++;
	}
	$select_registry_details_by_orgid_sth->finish();

	if($response eq '')
	{
		$response = "No organization found in registry database for org-id=$org_id";
	}
	return $response;
}

# get the registry details for the Org name specified
sub getRegistryDetailsForOrgName($$)
{
	my ($org_name, $search_type) = @_;
	my $response;
	my $sth;
	
	$log->log(level=>"debug", message=>"Searching for registry details by org-name=". $org_name ."\n") if $opt{'verbose'} >= 4;
												
	$select_registry_details_by_orgname_sth->execute($org_name);
	
	my $count=0;
	while(my($org_id,$orgName,$canAllocate,$street1,$street2,$street3,$street4,$street5,$street6,
	         $city,$state,$country,$postalCode,$registerDate,$updateDate,$comment,$referralServer,
			 $adminHandle,$nocHandle,$abuseHandle,$techHandle,$createDate,$modifyDate) = 
			$select_registry_details_by_orgname_sth->fetchrow_array())
	{
		my $line = "Org-ID: $org_id\n".
		           "Org-Name: $orgName\n".
			       "Can-Allocate: $canAllocate\n";
		   $line .= "Street-1: $street1\n" if defined($street1);
		   $line .= "Street-2: $street2\n" if defined($street2);
		   $line .= "Street-3: $street3\n" if defined($street3);
		   $line .= "Street-4: $street4\n" if defined($street4);
		   $line .= "Street-5: $street5\n" if defined($street5);
		   $line .= "Street-6: $street6\n" if defined($street6);
		   $line .= "City: $city\n" if defined($city);
		   $line .= "State: $state\n" if defined($state);
		   $line .= "Postal-Code: $postalCode\n" if defined($postalCode);
		   $line .= "Country: $country\n" if defined($country);
		   $line .= "Register-Date: $registerDate\n" if defined($registerDate);
		   $line .= "Update-Date: $updateDate\n" if defined($updateDate);
		   $line .= "Create-Date: ". getDateTimeFormat($createDate) ."\n" if defined($createDate);
		   $line .= "Modify-Date: ". getDateTimeFormat($modifyDate) ."\n" if defined($modifyDate);
		   $line .= "Admin-Handle: $adminHandle\n" if defined($adminHandle);
		   $line .= "NOC-Handle: $nocHandle\n" if defined($nocHandle);
		   $line .= "Abuse-Handle: $abuseHandle\n" if defined($abuseHandle);
		   $line .= "Tech-Handle: $techHandle\n" if defined($techHandle);
		   $line .= "Referral-Server: $referralServer\n" if defined($referralServer);
		   $line .= "Comment: $comment\n" if defined($comment);
		$response .= $line;
		$count ++;
	}
	$select_registry_details_by_orgname_sth->finish();

	if($response eq '')
	{
		$response = "No organization found in registry database for org-name=$org_name";
	}
	return $response;
}

# get the registry details for the Org specified
sub getRegistryDetailsForSourceAS($$)
{
	my ($source_as, $search_type) = @_;
	my $response;
	my $sth;
	
	$log->log(level=>"debug", message=>"Searching for registry details by source-as=". $source_as ."\n") if $opt{'verbose'} >= 4;
												
	$select_registry_details_by_sourceas_sth->execute($source_as);
	
	my $count=0;
	while(my($orgId, $orgName,$canAllocate,$street1,$street2,$street3,$street4,$street5,$street6,
	         $city,$state,$country,$postalCode,$registerDate,$updateDate,$comment,$referralServer,
			 $adminHandle,$nocHandle,$abuseHandle,$techHandle,$createDate,$modifyDate) = 
			$select_registry_details_by_sourceas_sth->fetchrow_array())
	{
		my $line = "Org-ID: $orgId\n".
		          "Org-Name: $orgName\n".
			      "Can-Allocate: $canAllocate\n"; 
	       $line .= "Street-1: $street1\n" if defined($street1);
		   $line .= "Street-2: $street2\n" if defined($street2);
		   $line .= "Street-3: $street3\n" if defined($street3);
		   $line .= "Street-4: $street4\n" if defined($street4);
		   $line .= "Street-5: $street5\n" if defined($street5);
		   $line .= "Street-6: $street6\n" if defined($street6);
		   $line .= "City: $city\n" if defined($city);
		   $line .= "State: $state\n" if defined($state);
		   $line .= "Postal-Code: $postalCode\n" if defined($postalCode);
		   $line .= "Country: $country\n" if defined($country);
		   $line .= "Register-Date: $registerDate\n" if defined($registerDate);
		   $line .= "Update-Date: $updateDate\n" if defined($updateDate);
		   $line .= "Create-Date: ". getDateTimeFormat($createDate) ."\n" if defined($createDate);
		   $line .= "Modify-Date: ". getDateTimeFormat($modifyDate) ."\n" if defined($modifyDate);
		   $line .= "Admin-Handle: $adminHandle\n" if defined($adminHandle);
		   $line .= "NOC-Handle: $nocHandle\n" if defined($nocHandle);
		   $line .= "Abuse-Handle: $abuseHandle\n" if defined($abuseHandle);
		   $line .= "Tech-Handle: $techHandle\n" if defined($techHandle);
		   $line .= "Referral-Server: $referralServer\n" if defined($referralServer);
		   $line .= "Comment: $comment\n" if defined($comment);
		$response .= $line;
		$count ++;
	}
	$select_registry_details_by_sourceas_sth->finish();

	if($response eq '')
	{
		$response = "No organization found in registry database for source-as=$source_as";
	}
	return $response;
}

# get the registry details for the Org specified
sub getRegistryDetailsForPOC($$)
{
	my ($poc_handle, $search_type) = @_;
	my $response;
	my $sth;
	
	$log->log(level=>"debug", message=>"Searching for registry details by poc-handle=". $poc_handle ."\n") if $opt{'verbose'} >= 4;
												
	$select_registry_details_by_pochandle_sth->execute($poc_handle);
	
	my $count=0;
	while(my($pocHandle,$isRole,$firstName,$middleName,$lastName,$roleName,$street1,$street2,$street3,$street4,$street5,$street6,
	         $city,$state,$country,$postalCode,$registerDate,$updateDate,$comment,$officePhone,$mailbox,$createDate,$modifyDate) = 
			$select_registry_details_by_pochandle_sth->fetchrow_array())
	{
		my $line ="POC-Handle: $pocHandle\n".
		       "Is-Role: $isRole\n" ;
			   
		if($isRole == 1) {
			$line .= "Role-Name: $roleName\n" if defined($roleName);
		} else {
			$line .= "First-Name: $firstName\n" if defined($firstName);
			$line .= "Middle-Name: $middleName\n" if defined($middleName);
			$line .= "Last-Name: $lastName\n" if defined($lastName);
		}
		
		   $line .= "Street-1: $street1\n" if defined($street1);
		   $line .= "Street-2: $street2\n" if defined($street2);
		   $line .= "Street-3: $street3\n" if defined($street3);
		   $line .= "Street-4: $street4\n" if defined($street4);
		   $line .= "Street-5: $street5\n" if defined($street5);
		   $line .= "Street-6: $street6\n" if defined($street6);
		   $line .= "City: $city\n" if defined($city);
		   $line .= "State: $state\n" if defined($state);
		   $line .= "Postal-Code: $postalCode\n" if defined($postalCode);
		   $line .= "Country: $country\n" if defined($country);
		   $line .= "Register-Date: $registerDate\n" if defined($registerDate);
		   $line .= "Update-Date: $updateDate\n" if defined($updateDate);
		   $line .= "Create-Date: ". getDateTimeFormat($createDate) ."\n" if defined($createDate);
		   $line .= "Modify-Date: ". getDateTimeFormat($modifyDate) ."\n" if defined($modifyDate);
		   $line .= "Comment: $comment\n" if defined($comment);

		   $line .= "Office-Phone: $officePhone\n" if defined($officePhone);
		   $line .= "Mailbox: $mailbox\n" if defined($mailbox);
		
		$response .= $line;
		$count ++;
	}
	$select_registry_details_by_pochandle_sth->finish();

	if($response eq '')
	{
		$response = "No point of contact found in registry database for poc-handle=$poc_handle";
	}
	return $response;
}

# get the list of peer routers
sub getPeers()
{
	my $response = "Peers:\n";
	foreach my $peer (sort keys %peers) {
		$response .= "$peer\n";
	}
	return $response;
}

# get the list of peer routers
sub getStatistics($)
{
	my $type = shift;
	my $response;
	if($type eq 'summary' or $type eq 'all')
	{
	   $response = "Server Statistics:\n".
				   "                  Unique IPs: ". $counters{'hosts'} ."\n".
				   "               Whois Queries: ". $counters{'whois'} ."\n".
				   "    Routeview Prefix Queries: ". $counters{'routeview.prefix'} ."\n".
				   " Routeview Source-AS Queries: ". $counters{'routeview.source-as'} ."\n".
				   "  Netblock Source-AS Queries: ". $counters{'netblock.source-as'} ."\n".
				   "     Netblock Org-ID Queries: ". $counters{'netblock.org-id'} ."\n".
				   "   Netblock Net-Name Queries: ". $counters{'netblock.net-name'} ."\n".
				   " Netblock Net-Handle Queries: ". $counters{'netblock.net-handle'} ."\n".
				   "     Registry Org-ID Queries: ". $counters{'registry.org-id'} ."\n".
				   "   Registry Org-Name Queries: ". $counters{'registry.org-name'} ."\n".
				   "  Registry Source-AS Queries: ". $counters{'registry.source-as'} ."\n".				   
				   "        Registry POC Queries: ". $counters{'registry.poc-handle'} ."\n".
				   "               Peers Queries: ". $counters{'peers'} ."\n\n";
	}
	
	if($type eq 'all')
	{			 
	  $response .= "Cache/ACL Statistics:\n".
				   "     Unique IPs: ". scalar(keys %requests) ."\n\n".
				   "   IP              | Count      | Limit      | First                    | Last\n";
	
		foreach my $ip (sort keys %requests)
		{
			my $line;
			if($requests{$ip}{'acl'} >= 1) {
				$line = sprintf("+> %+15s | %+10s | %+10s | %+24s | %+24s\n",
								   $ip,	
								   $requests{$ip}{'count'},
								   $requests{$ip}{'limit'},
								   getDateTimeFormat($requests{$ip}{'firstQuery'}),
								   getDateTimeFormat($requests{$ip}{'lastQuery'})
								   );
				$response .= $line;
			}
			else
			{
				$line = sprintf("-> %+15s | %+10s | %+10s | %+24s | %+24s\n",
								   $ip,	
								   $requests{$ip}{'count'},
								   $requests{$ip}{'limit'},
								   getDateTimeFormat($requests{$ip}{'firstQuery'}),
								   getDateTimeFormat($requests{$ip}{'lastQuery'})
								   );
				$response .= $line;
			}
		}
	}
				
	return $response;
}


# the general request handler loop to parse out and determine what type of request it is
sub do_query
{
	my $req = shift;
	my $client = shift;
	my $client_ip = $socket->PeerAddr($client);
	my $display_type = 'pwhois';
	my $search_type = 'best';
	my $host_ip;
	my $response;
	my $extra;
	my $application='unknown';
	if($connections{$client}{'bulk'}) {
		$application = $connections{$client}{'application'};
	}

	my $requestDate = time();

	$log->log(level=>"debug", message=>"Request received from $client_ip with query='$req'\n") if $opt{'verbose'} >= 4;

	return "Error: server is reloading internal datasets or it is too busy for your request.  Please try again in a few minutes."
		if $server_connections_paused;


	# check the request to make sure it is well formed

	if($req =~ /^\s?help|\?\s?$/i) {
		return getHelp();
	}
	if($req =~ /^\s?extra-help\s?$/i) {
		return getHelp() . getExtraHelp();
	}
	if($req =~ /^\s?version\s?$/i) {
		return getVersion(0, $socket->LocalAddr($client));
	}
	if($req =~ /^\s?statistics\s?$/i) {
		return getStatistics('summary');
	}
	if($req =~ /^\s?type=all statistics\s?$/i) {
		return getStatistics('all');
	}
	
	if($req =~ /^\s?(begin)|(bulk)$/i) {
		$connections{$client}{'bulk'} = 1;
		$connections{$client}{'bulk_end'} = 0;
		$connections{$client}{'bulk_count'} = 0;
		$log->log(level=>"debug", message=>"bulk mode selected for client: $client_ip waiting for more data\n")		
			if $opt{'verbose'} >= 2;

		return '';
	}
	if($req =~ /^\s?(end)|(quit)$/i) {
		$connections{$client}{'bulk_end'} = 1;
		$log->log(level=>"debug", message=>"end of bulk mode found from client: $client_ip ... closing connection.\n")
			if $opt{'verbose'} >= 2;
		return;
	}
	
	# match app="blah blah" or app=blah
	if($req =~ /^\s?app=[A-z0-9\-.]+|\"[A-z0-9\-. ]+\"\s?/i)
	{
		my ($app,$rest) = ($req =~ /^\s?app=([A-z0-9\-.]+|\"[A-z0-9\-. ]+\")\s?(.*)\s?/i);
		if($app ne '') {
			$app =~ s/\"//g;
			$application = $app;
		}
		
		if($connections{$client}{'bulk'}) {
			$connections{$client}{'application'} = $application;
		}
		
		$log->log(level=>"debug", message=>"setting the application to \'$application\' rest=\'$rest\' ... \n") if $opt{'verbose'} >= 2;
		$req = $rest;
		
		# no more data on this line -- wait for more data -- because we are in bulk mode
		if($connections{$client}{'bulk'} and $req eq '') {
			return;
		}
		else {
			# there has to be more data -- if not it will be caught as an error below
		}
	}

	# match type=XXX on separate line (in bulk mode only)
	if($req =~ /^\s?type=((pwhois)|(cymru)|(rpsl))\s?$/i)
	{
		my $type = $1;
		if($connections{$client}{'bulk'})
		{
		
			if(!defined($connections{$client}{'displayType'}))
			{
				$connections{$client}{'displayType'} = $type;
				$display_type = 'cymru';
				$log->log(level=>"debug", message=>"setting the display type to ". $connections{$client}{'displayType'} ." for bulk session\n") 
						if $opt{'verbose'} >= 2; 
			}
			return;
		}
		else
		{
			return $INVALID_INPUT; 
		}

	}
	# match type=XXX not on a separate line but on the same line as the query
	if($req =~ /^\s?type=(pwhois|cymru|rpsl)\s?(.*)/i)
	{	
		$display_type = $1;
		my $rest = $2;
		$log->log(level=>"debug", message=>"setting the display type to \'$display_type\' rest=\'$rest\' ... \n") if $opt{'verbose'} >= 2; 
		$req = $rest;
	}
	# match type=XXX not on a separate line but on the same line as the query (search-type)
	if($req =~ /^\s?type=(all|best)\s?(.*)/i)
	{	
		$search_type = $1;
		my $rest = $2;
		$log->log(level=>"debug", message=>"setting the search type to $search_type\n") if $opt{'verbose'} >= 2; 
		$req = $rest;
	}

	# perform queries, only after checking limits
	
	my $advanced = 0;  # flag indicates that user is allowed to send advanced queries
	
	# check to make sure the client IP hasn't exceeded their request limit
	if(defined($requests{$client_ip}))
	{
		# check to see if the user has been restricted entirely
		if(defined($requests{$client_ip}{'acl'})) {
			if($requests{$client_ip}{'acl'} == 0) {
				$log->log(level=>"debug", message=>"Request not allowed from $client_ip - ACCESS DENIED\n") if $opt{'verbose'} >= 1;
				return "Error: Unable to access server at this time - limit exceeded or access is denied.";
			}
			elsif($requests{$client_ip}{'acl'} == 1) {
				$advanced = 0;
			}
			elsif($requests{$client_ip}{'acl'} >= 2) {
				$advanced = 1;
			}
		}
		else {
			# user isn't defined -- do nothing (user has public level access)
		}
		
		my $count = $requests{$client_ip}{'count'};
		my $limit = $requests{$client_ip}{'limit'};
		# if the source IP has exceeded their query limit in the past 24 hours, bail out and
		# don't let them continue.  If the limit is 0, then they can submit unlimited queries
		if($count >= $limit and $requests{$client_ip}{'lastQuery'} > $requestDate - 1*24*3600 and $limit > 0)
		{
			$log->log(level=>"info", message=>"Request not processed -- limit exceeded by $client_ip\n") if $opt{'verbose'} >= 1;
			return "Error: Unable to perform lookup; Query limit exceeded.";	
		}
		elsif($count >= $limit and $requests{$client_ip}{'lastQuery'}  <= $requestDate - 1*24*3600 and $limit > 0)
		{
			# update the count so the user can start over
			$requests{$client_ip}{'count'} == 0;	
			$requests{$client_ip}{'lastQuery'} = $requestDate;	        
		}
		else
		{
			if($count == 0) {
				$counters{'hosts'}++;
			}
			
			$requests{$client_ip}{'count'}++;
			$requests{$client_ip}{'lastQuery'} = $requestDate;
			if(!defined($requests{$client_ip}{'firstQuery'})){
				$requests{$client_ip}{'firstQuery'} = $requestDate;
			}
		}
	}
	else 
	{
		$counters{'hosts'}++;		# total count of unique client requests
		$requests{$client_ip}{'count'}=1;
		$requests{$client_ip}{'lastQuery'} = $requestDate; 
		$requests{$client_ip}{'firstQuery'} = $requestDate;
		$requests{$client_ip}{'limit'} = $opt{'limit-max-queries'};
	}

  	# search for all prefixes (as stored in our database snapshot) after first finding most specific from FIB
	# and return all the routers, Source-AS, Next-Hops, and AS-Paths (no CIDR specified, just the IP)
	if($req =~ /^\s?routeview prefix=\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3}\s?$/i)
	{
		if($advanced == 0) {
			return $NOT_AUTHORIZED;
		}
		
		my($ip)=($req =~ /^\s?routeview prefix=(\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3})\s?$/i);
		$log->log(level=>"info", message=> getCurrentSyslogDateTime() ." Performing routeview prefix query for $ip src=$client_ip app=\"$application\" count=".
										   $requests{$client_ip}{'count'} ." limit=". $requests{$client_ip}{'limit'} ."\n") if $opt{verbose} >= 1;

		$counters{'routeview.prefix'}++;
		
		# pass the ip only (the CIDR will be found from the FIB)
		return getRouteviewPrefixByIP($ip, $search_type);
	}
			
	# search for all prefixes (as stored in our database snapshot) -- by a specific prefix
	# and return all the routers, Source-AS, Next-Hops, and AS-Paths
	if($req =~ /^\s?routeview prefix=\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3}\/\d{1,2}\s?$/i)
	{
		if($advanced == 0) {
			return $NOT_AUTHORIZED;
		}
		
		my($ip, $cidr)=($req =~ /^\s?routeview prefix=(\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3})\/(\d{1,2})\s?$/i);
		$log->log(level=>"info", message=> getCurrentSyslogDateTime() ." Performing routeview prefix db query for $ip/$cidr src=$client_ip app=\"$application\" count=".
										   $requests{$client_ip}{'count'} ." limit=". $requests{$client_ip}{'limit'} ."\n") if $opt{verbose} >= 1;
		
		$counters{'routeview.prefix'}++;
		
		
		# pass the ip and cidr prefix		
		return getRouteviewPrefixByCIDR($ip, $cidr, $search_type);
	}
	
	# search for all prefixes (as stored in our database snapshot)
	# and return all the routers, Source-AS, Next-Hops, and AS-Paths
	if($req =~ /^\s?routeview source-as=\d{1,7}\s?$/i)
	{
		if($advanced == 0) {
			return $NOT_AUTHORIZED;
		}
		
		my($source_as)=($req =~ /^\s?routeview source-as=(\d{1,7})\s?$/i);
		$log->log(level=>"info", message=> getCurrentSyslogDateTime() ." Performing routeview source-as db query for $source_as src=$client_ip app=\"$application\" count=".
		                                   $requests{$client_ip}{'count'} ." limit=". $requests{$client_ip}{'limit'} ."\n") if $opt{verbose} >= 1;
		
		$counters{'routeview.source-as'}++;
		
		# pass the source-as 
		return getRouteviewBySourceAS($source_as, $search_type);
	}
	
	# search for all registered prefixes (as stored in our database snapshot)
	# and return all the details
	if($req =~ /^\s?netblock source-as=\d{1,7}\s?$/i)
	{
		if($advanced == 0) {
			return $NOT_AUTHORIZED;
		}
		
		my($source_as)=($req =~ /^\s?netblock source-as=(\d{1,7})\s?$/i);
		$log->log(level=>"info", message=> getCurrentSyslogDateTime() ." Performing netblock source-as db query for $source_as src=$client_ip app=\"$application\" count=".
		                                   $requests{$client_ip}{'count'} ." limit=". $requests{$client_ip}{'limit'} ."\n") if $opt{verbose} >= 1;
		
		$counters{'netblock.source-as'}++;
		
		# pass the source-as 
		return getNetblockBySourceAS($source_as, $search_type);
	}
	# search for all registered prefixes (as stored in our database snapshot)
	# and return all the details
	if($req =~ /^\s?netblock org-id=[A-z0-9\-]{1,20}\s?$/i)
	{
		if($advanced == 0) {
			return $NOT_AUTHORIZED;
		}
		
		my($org_id)=($req =~ /^\s?netblock org-id=([A-z0-9\-]{1,20})\s?$/i);
		$log->log(level=>"info", message=> getCurrentSyslogDateTime() ." Performing netblock org-id db query for $org_id src=$client_ip app=\"$application\" count=".
		                                   $requests{$client_ip}{'count'} ." limit=". $requests{$client_ip}{'limit'} ."\n") if $opt{verbose} >= 1;
		
		$counters{'netblock.org-id'}++;
		
		# pass the source-as 
		return getNetblockByOrgID($org_id, $search_type);
	}
	# search for all registered prefixes (as stored in our database snapshot)
	# and return all the details
	if($req =~ /^\s?netblock net-name=[A-z0-9\-]{1,128}\s?$/i)
	{
		if($advanced == 0) {
			return $NOT_AUTHORIZED;
		}
		
		my($net_name)=($req =~ /^\s?netblock net-name=([A-z0-9\-]{1,128})\s?$/i);
		$log->log(level=>"info", message=> getCurrentSyslogDateTime() ." Performing netblock net-name db query for $net_name src=$client_ip app=\"$application\" count=".
		                                   $requests{$client_ip}{'count'} ." limit=". $requests{$client_ip}{'limit'} ."\n") if $opt{verbose} >= 1;
		
		$counters{'netblock.net-name'}++;
		
		# pass the source-as 
		return getNetblockByNetName($net_name, $search_type);
	}

	# search for all registered prefixes (as stored in our database snapshot)
	# and return all the details
	if($req =~ /^\s?netblock net-handle=[A-z0-9\-]{1,128}\s?$/i)
	{
		if($advanced == 0) {
			return $NOT_AUTHORIZED;
		}
		
		my($net_handle)=($req =~ /^\s?netblock net-handle=([A-z0-9\-]{1,128})\s?$/i);
		$log->log(level=>"info", message=> getCurrentSyslogDateTime() ." Performing netblock net-handle db query for $net_handle src=$client_ip app=\"$application\" count=".
		                                   $requests{$client_ip}{'count'} ." limit=". $requests{$client_ip}{'limit'} ."\n") if $opt{verbose} >= 1;
		
		$counters{'netblock.net-handle'}++;
		
		# pass the source-as 
		return getNetblockByNetHandle($net_handle, $search_type);
	}

	# search for the registry information associated with the org-id specified
	if($req =~ /^\s?registry org-id=[A-z0-9\-]{1,20}\s?$/i)
	{
		if($advanced == 0) {
			return $NOT_AUTHORIZED;
		}
		
		my($org_id)=($req =~ /^\s?registry org-id=([A-z0-9\-]{1,20})\s?$/i);
		$log->log(level=>"info", message=> getCurrentSyslogDateTime() ." Performing registry org-id db query for $org_id src=$client_ip app=\"$application\" count=".
		                                   $requests{$client_ip}{'count'} ." limit=". $requests{$client_ip}{'limit'} ."\n") if $opt{verbose} >= 1;
		
		$counters{'registry.org-id'}++;
		
		return getRegistryDetailsForOrgID($org_id, $search_type);
	}
	
	# search for the registry information associated with the org-id specified
	if($req =~ /^\s?registry org-name=[A-z0-9\- ]+|\"[A-z0-9\- ]+\"\s?$/i)
	{
		if($advanced == 0) {
			return $NOT_AUTHORIZED;
		}
		
		my($org_name)=($req =~ /^\s?registry org-name=([A-z0-9\- ]+|\"[A-z0-9\- ]+\")\s?$/i);
		$log->log(level=>"info", message=> getCurrentSyslogDateTime() ." Performing registry org-name db query for $org_name src=$client_ip app=\"$application\" count=".
		                                   $requests{$client_ip}{'count'} ." limit=". $requests{$client_ip}{'limit'} ."\n") if $opt{verbose} >= 1;
		
		# if($req =~ /^\s?app=[A-z0-9\-.]+|\"[A-z0-9\-. ]+\"\s?/i)
		
		$counters{'registry.org-name'}++;
		
		return getRegistryDetailsForOrgName($org_name, $search_type);
	}
	# search for the registry information associated (for Org information) with the source-as specified
	if($req =~ /^\s?registry source-as=\d{1,7}\s?$/i)
	{
		if($advanced == 0) {
			return $NOT_AUTHORIZED;
		}
		
		my($source_as)=($req =~ /^\s?registry source-as=(\d{1,7})\s?$/i);
		$log->log(level=>"info", message=> getCurrentSyslogDateTime() ." Performing registry source-as db query for $source_as src=$client_ip app=\"$application\" count=".
		                                   $requests{$client_ip}{'count'} ." limit=". $requests{$client_ip}{'limit'} ."\n") if $opt{verbose} >= 1;
		
		$counters{'registry.source-as'}++;
		
		return getRegistryDetailsForSourceAS($source_as, $search_type);
	}
	# search for the registry information associated (for contact information) with the poc-handle specified
	if($req =~ /^\s?registry poc-handle=[A-z0-9\-]{1,20}\s?$/i)
	{
		if($advanced == 0) {
			return $NOT_AUTHORIZED;
		}
		
		my($poc_handle)=($req =~ /^\s?registry poc-handle=([A-z0-9\-]{1,20})\s?$/i);
		$log->log(level=>"info", message=> getCurrentSyslogDateTime() ." Performing registry poc-handle db query for $poc_handle src=$client_ip app=\"$application\" count=".
		                                   $requests{$client_ip}{'count'} ." limit=". $requests{$client_ip}{'limit'} ."\n") if $opt{verbose} >= 1;
		
		$counters{'registry.poc-handle'}++;
		
		# pass the source-as 
		return getRegistryDetailsForPOC($poc_handle, $search_type);
	}



	# get a list of the peer routers
	if($req =~ /^\s?peers/i)
	{
		$log->log(level=>"info", message=> getCurrentSyslogDateTime() ." Performing peers request src=$client_ip app=\"$application\" count=".
		                                   $requests{$client_ip}{'count'} ." limit=". $requests{$client_ip}{'limit'} ."\n") if $opt{verbose} >= 1;

		$counters{'peers'}++;
		
		return getPeers();
	}

	# regular prefix whois queries (no extra fields or parameters)
	
	# IP notation	
	if($req =~ /^\s?\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3}\s?[A-z0-9\.\- ]{0,15}$/)
	{
		my($ip);
		($ip,$extra)=($req =~ /^\s?(\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3})\s?([A-z0-9\.\- ]{0,15})$/);
		if(ipv4_is_valid_quaddot($ip)) {
			$host_ip = $ip;
		}
		else
		{
		        $log->log(level=>"debug", message=>"Invalid ip address specified: $ip\n") if $DEBUG eq 'true';
			return $INVALID_INPUT; 
		}
		
	} # prefix notation
	elsif($req =~ /^\s?\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3}\/\d{1,2}\s?[A-z0-9\.\- ]{0,15}$/)
	{
		my($ip,$cidr);
		($ip,$cidr,$extra)=($req =~ /^\s?(\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3})\/(\d{1,2})\s?([A-z0-9\.\- ]{0,15})$/);
			
		if(ipv4_is_valid_quaddot($ip)) {			
			$host_ip = $ip;
		}
		else
		{
			$log->log(level=>"debug", message=>"Invalid ip address specified: $ip\n") if $DEBUG eq 'true';
			return $INVALID_INPUT;
		}
	} # ip/port notation	
	elsif($req =~ /^\s?\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3}:\d{1,6}\s?[A-z0-9\.\- ]{0,15}$/i)
	{
		my($ip,$port);
		($ip,$port,$extra)=($req =~ /^\s?(\d{0,3}\.\d{0,3}\.\d{0,3}\.\d{0,3}):(\d{1,6})\s?([A-z0-9\.\- ]{0,15})$/i);
			
		if(ipv4_is_valid_quaddot($ip)) {			
			$host_ip = $ip;
		}
		else
		{
			$log->log(level=>"debug", message=>"Invalid ip address specified: $ip\n") if $DEBUG eq 'true';
			return $INVALID_INPUT;
		}
	}
	else
	{
	        $log->log(level=>"debug", message=>"Invalid request specified: '$req'\n") if $DEBUG eq 'true';
		return $INVALID_INPUT; 
	}

	$log->log(level=>"info", message=> getCurrentSyslogDateTime() ." Performing whois query for $host_ip src=$client_ip app=\"$application\" count=". 
				$requests{$client_ip}{'count'} ." limit=". $requests{$client_ip}{'limit'} ."\n") if $opt{verbose} >= 1;

	# we are now going to process the query -- count this as a hit regardless of the response
	$counters{'whois'}++;
	
	# take the IP address provided and find the network address that matches
	# The most specific.  Then return the ASN for that netblock based upon
	# The BGP routing table (FIB) that is cached in memory.

	my ($result, $network, $asn, $asn_paths, $orgName, $netName, $cache_date) = findInNetwork($host_ip);
	if($result) {
	
		if($display_type eq 'cymru' or ($connections{$client}{'bulk'} and $connections{$client}{'displayType'} eq 'cymru'))
		{
			if($connections{$client}{'bulk'} and $connections{$client}{'bulk_count'} == 0) {
				$response .= "Bulk mode; one IP per line. [". getCymruDateFormat(time()) ."]\n";
			}
			
			if($extra ne '') {

				# if($connections{$client}{'bulk'} == 0 or ($connections{$client}{'bulk'} and $connections{$client}{'bulk_count'} == 0)) 
				# {
				#	$response = "ASN     | IP              | Info            | Name\n"; 
				# }
			
				# ASN     | IP               | Name
				# 3356    | 4.2.2.1          | LEVEL3 Level 3 Communications

				if($netName eq '') {
					$netName = 'NULL';
				}
					$response .= sprintf("%-7s | %-15s | %-15s | %-s", $asn, $host_ip, $extra, $netName);
			}
			else {
			
				#  Don't print out the header for cymru format (bulk)
				# if($connections{$client}{'bulk'} == 0 or ($connections{$client}{'bulk'} and $connections{$client}{'bulk_count'} == 0)) 
				# {
				#	$response = "ASN     | IP              | Name\n"; 
				# }

				$response .= sprintf("%-7s | %-15s | %-s", $asn, $host_ip, $netName);			
			}

			if($connections{$client}{'bulk'}) {
				$connections{$client}{'bulk_count'}++;
			}
		}
		elsif($display_type eq 'rpsl' and !$connections{$client}{'bulk'})
		{
			# Origin: AS<asn> AS12266
			# Route: <cidr_prefix> 4.0.0.0/8
			# Date: <date of update in rpsl>
			# Source: PWHOIS Server x.x.x.x:port at <date of update>

			$response = "Origin: AS$asn\n" .
				    "Route: $network\n" .
				    "Date: ". getRpslDateFormat($cache_date) ."\n" .
					"Org-Name: ". $orgName ."\n" .
					"Net-Name: ". $netName ."\n" .
				    "Source: PWHOIS Server ". $socket->LocalAddr($client) .":". $opt{port} ." at ". getRpslDateFormat($cache_date);		
					
			if($extra ne '') {
				$response .= "Info: $extra\n";
			}
		}
		# pwhois format
		elsif($display_type eq 'pwhois' or ($connections{$client}{'bulk'} and $connections{$client}{'displayType'} eq 'pwhois'))
		{		
			$response = "IP: $host_ip\n" .
				    "Origin-AS: $asn\n" .
				    "Prefix: $network\n" .
				    "AS-Path: $asn_paths\n" .
					"Org-Name: $orgName\n" .
					"Net-Name: $netName\n" .
				    "Cache-Date: $cache_date\n";
					
			if($extra ne '') {
				$response .= "Info: $extra";
			}
		
			if($connections{$client}{'bulk'} and $connections{$client}{'bulk_end'} == 0) {
				$response .= "\n";
			}
		}
		else
		{
			return $INVALID_INPUT; 
		}
	}
	else # not found
	{	
		if($connections{$client}{'bulk'})
		{
			if($display_type eq 'cymru' or ($connections{$client}{'bulk'} and $connections{$client}{'displayType'} eq 'cymru'))
			{
			
				
				# ASN     | IP               | Name
				# 3356    | 4.2.2.1          | LEVEL3 Level 3 Communications

				if($extra ne '') {
				
					if($connections{$client}{'bulk_count'} == 0) {
						$response = "ASN     | IP               | Info                | Name\n"; 
					}
					my $line = sprintf("%+7s | %+15s | %+15s | %-s", 0, "NULL", $extra, "NULL");
					$response .= $line;
				}
				else {
					if($connections{$client}{'bulk_count'} == 0) {
						$response = "ASN | IP              | Name\n"; 
					}
					my $line = sprintf("%+7s | %+15s | %-s", 0, "NULL", "NULL");
					$response .= $line;

				}
				$connections{$client}{'bulk_count'}++;
	
			}
			elsif($display_type eq 'rpsl')
			{
				return "No matching data found";
			}
			elsif($display_type eq 'pwhois' or ($connections{$client}{'bulk'} and $connections{$client}{'displayType'} eq 'pwhois'))
			{
				$response = "IP: $host_ip\n" .
					    "Origin-AS: NULL\n" .
					    "Prefix: NULL\n" .
					    "AS-Path: NULL\n" .
						"Org-Name: NULL\n" .
						"Net-Name: NULL\n" .
					    "Cache-Date: NULL\n";
						
				if($extra ne '') {
					$response .= "Info: $extra\n";
				}
		
				if($connections{$client}{'bulk_end'} == 0) {
					$response .= "\n";
				}
				
			}
		}
		else
		{				
			$response = "That IP address doesn't appear in the global routing table as of ". getDateTimeFormat($last_table_update);
		}
	}
	return $response;
}

sub load_data()
{
	my $cacheDate = time();
	
	# clear any cached data (if any)	
		
	%routes = ();
	undef $pt; $pt = new Net::Patricia;

	my $routes_found=0;

	my $sth;

	if(defined($opt{'router-id'})) {
		$sth = $select_from_bgp_routes_by_routerid_sth;
		$sth->execute(ipv4_quaddot_to_decimal($opt{'router-id'}));
	}
	else
	{
		$sth = $select_from_bgp_routes_sth;
		$sth->execute();
	}
	
	# get the data from the database (currently about 150K rows)
	while(my($id,$router_id, $network,$cidr,$nextHop,$asn,$asn_paths,$createDate,$modifyDate,$status,$best_route) =
		$sth->fetchrow_array()) {

		# polulate the internal cache table with data from the database
		# add the values to the hash
		my $network_quaddot = ipv4_decimal_to_quaddot($network);
		my $route;
		if(defined($cidr)) {
			$route = $network_quaddot .'/'. $cidr;
		}
		else
		{
			# if the route doesn't have a CIDR prefix, get the default prefix for classful routing
			$route = ipv4_parse($network_quaddot .'/'. 
					ipv4_msk2cidr(ipv4_dflt_netmask($network_quaddot)));
		}
		
		if(defined($routes{$route})) {
			$log->log(level=>"error", message=>"Warning: Route $route is already in FIB with id=". 
						$routes{$route}{'id'} ." ... $id is replacing it. Last one wins!!\n") if $opt{'verbose'} >= 1;
		}
		
		$routes{$route}{'class'} = 'bgp';
		$routes{$route}{'route'} = $route;
		$routes{$route}{'status'} = $status;
		$routes{$route}{'id'} = $id;
		$routes{$route}{'router_id'} = $router_id;
		$routes{$route}{'asn'} = $asn;
		$routes{$route}{'path'} = $asn_paths;
		$routes{$route}{'createDate'} = $createDate;
		$routes{$route}{'modifyDate'} = $modifyDate;
		$routes{$route}{'cacheDate'} = $cacheDate;
		
		if($config{'registry.database'} == 1) {
		
			# get the orgName
			$select_orgname_by_as_sth->execute($asn);
			while(my($orgName) = $select_orgname_by_as_sth->fetchrow_array()) {
				$routes{$route}{'orgName'} = $orgName;
			} 
			$select_orgname_by_as_sth->finish();
				
			# get the netName
			$select_netname_by_route_sth->execute(ipv4_quaddot_to_decimal($route));
			while(my($netName) = $select_netname_by_route_sth->fetchrow_array()) {
				$routes{$route}{'netName'} = $netName;
			}
			$select_netname_by_route_sth->finish();
			
		}
		
		# update the table update date from one of the records
		$last_table_update = $modifyDate if $routes_found == 0;
		
		$pt->add_string($route, $route);
		
		$routes_found++;
		$log->log(level=>"debug", message=>"Loading (best) network route: $route with asn=$asn id=$id.\n") if $opt{'verbose'} >= 10;
	}

	$sth->finish();
        $log->log(level=>"info", message=> getCurrentSyslogDateTime() ." BGP routing table cache loaded with ". scalar(keys %routes) . " routes.\n") if $opt{'verbose'} >= 1;

	%requests = ();
	# load our pwhois ACL data
	$select_from_pwhois_acl_sth->execute();
	while(my($ipdec, $max_count, $acl) = $select_from_pwhois_acl_sth->fetchrow_array()) {
		  my $ip = ipv4_decimal_to_quaddot($ipdec);
		  $requests{$ip}{'limit'} = $max_count;
		  $requests{$ip}{'lastQuery'} = 0;
		  $requests{$ip}{'firstQuery'} = 0;
		  $requests{$ip}{'count'} = 0;
		  $requests{$ip}{'acl'} = $acl;
		  
		  $log->log(level=>"info", message=> getCurrentSyslogDateTime() ." Setting ACL for $ip limit=$max_count acl=$acl\n") if $opt{'verbose'} >= 1;

	}
	$select_from_pwhois_acl_sth->finish();

	%peers = ();

	if(defined($opt{'router-id'})) {
		$sth = $select_from_bgp_routes_next_hops_by_routerid_sth;
		$sth->execute(ipv4_quaddot_to_decimal($opt{'router-id'}));
	}
	else
	{
		$sth = $select_from_bgp_routes_next_hops_sth;
		$sth->execute();
	}

	while(my($next_hop) = $sth->fetchrow_array()) 
	{
		my $ip = ipv4_decimal_to_quaddot($next_hop);
		$peers{$ip} = 1;
	}
	$sth->finish();
		
        $log->log(level=>"info", message=> getCurrentSyslogDateTime() ." Loaded peer information ... ". scalar(keys %peers) ." peers in feed(s).\n") if $opt{'verbose'} >= 1;
}

sub startServer()
{
	$socket = Net::Socket::NonBlock::Nest->new(SelectT  => 0.1,
        						SilenceT => 0,
							debug    => $^W,
							BuffSize => 32766,
							# BuffSize => 127044,
							# BuffSize => 10240,
							) or die "Error creating sockets nest: $@\n";
	
	$socket->Listen(LocalAddr => $opt{'bind'},
			LocalPort => $opt{'port'},
			Proto     => 'tcp',
			Accept    => \&clientConnect,
			SilenceT  => 0,
			ClientsST => 10,
			Listen    => 10,
			MaxClients => 10000
			)
			or die "Could not listen on port ". $opt{port} .": $@\n";
	
	my $localaddr = "*" if $opt{'bind'} eq '';
	$localaddr = $opt{'bind'} if $opt{'bind'} ne '';
	$log->log(level=>"info", message=>getCurrentSyslogDateTime() ." Whois server listening on pwhois://$localaddr:". $opt{'port'} ."\n") if $opt{verbose} >= 1;


	# drop previledges
	POSIX::setuid($opt{'uid'});
	POSIX::setgid($opt{'gid'});

	while($socket->IO())
	{	      
		 # $log->log(level=>"debug", message=>"Selecting on socket\n") if $opt{'verbose'} >= 10;
		foreach my $ClnSock (sort keys %connections)
		{
			my $req = undef;
			my $client_ip = $socket->PeerAddr($ClnSock);
			$log->log(level=>"debug", message=>"Receiving data from $client_ip\n") if $opt{'verbose'} >= 9;
			while(($req = $socket->Gets($ClnSock)) and length($req))
			{
				$log->log(level=>"debug", message=>"Data received from $client_ip: $req\n") if $opt{'verbose'} >= 9;
				if(defined($req)) {
					my($response) = do_query($req, $ClnSock);
					if($response ne '') {	
						$socket->Puts($ClnSock, $response . "\n");
						$socket->IO();
					}
					if($connections{$ClnSock}{'bulk'})
					{
						if($connections{$ClnSock}{'bulk_end'})
						{
							$socket->Close($ClnSock);
							delete($connections{$ClnSock});
						}
						else
						{					
							# wait for additional commands
							$log->log(level=>"debug", message=>"Waiting for additional commands from: $client_ip\n") if $opt{'verbose'} >= 9;
						}
					}
					else
					{
						$log->log(level=>"debug", message=>"Connection closed: $client_ip\n") if $opt{'verbose'} >= 9;
						$socket->Close($ClnSock);
						delete($connections{$ClnSock});
					}
				}
				else
				{
					$log->log(level=>"debug", message=>"Connection closed by foreign host: $client_ip\n") if $opt{'verbose'} >= 9;
					$socket->Close($ClnSock);
					delete($connections{$ClnSock});
				}
			}
			if(!defined($req)) {
				$socket->Close($ClnSock);
				delete($connections{$ClnSock});
			}
		}
	}
	$socket->Close();
}

sub clientConnect 
{
        my $client = shift;
	if(defined($client)) {
		my $client_ip = $socket->PeerAddr($client);
		$log->log(level=>"debug", message=>"Got connection from: $client_ip\n") if $opt{'verbose'} >= 9;
		#$connections{$client}{'socket'} = \$client;
		$connections{$client}{'bulk'} = 0;
		$connections{$client}{'bulk_end'} = 0;
		$connections{$client}{'application'} = 'unknown';
		return 1;
	}
	else
	{
		return 0;
	}
}

sub getDateTimeFormat($) {
	return Time::Format::time_format('Mon dd yyyy hh:mm{in}:ss tz', shift);
}

sub getRpslDateFormat($) {
	return Time::Format::time_format('yyyymmdd', shift);
}

sub getSyslogDateTime($) {
	return Time::Format::time_format('Mon ?d yyyy hh:mm{in}:ss', shift);
}

sub getCymruDateFormat($) {
	return Time::Format::time_format('yyyy-mm-dd hh:mm{in}:ss tz', shift);
}

sub getCurrentSyslogDateTime() {
	return getSyslogDateTime(time());
}



sub DESTROY {
   my $self = shift;
   # clean up lock and temp files
   #$self->SUPER::DESTROY
}

## check the routing table to see if the
# ip address is within one of the networks.  If so
# set the community name (class) and network route
# address that matches.
sub findInNetwork
{
	my $dest = shift;
		
	if($dest eq '')  {
		warn "No ip address specified.\n";
		return (0, '', '');
	}
	
	die "Ip address can not be null\n"  if !defined($dest);

	#	$log->log(level=>'debug', message=>"Attempting to find '$dest' in RIB.\n")
	#	if $opt{verbose} >= 9 and $DEBUG eq 'true';
  
	# parse the dest. address to determine netmask -- default to /32 if non-provided
	my ($ip,$cidr) = ipv4_parse($dest);

	my $node = $pt->match_string($dest);
	if(defined($node)) {
		my $network = $node;                
		my $asn;
		my $path;
		my $cache_date;
		my $orgName;
		my $netName;
		my ($network_ip, $network_cidr) =  ipv4_parse($network);
		$network_cidr =  ipv4_msk2cidr( ipv4_dflt_netmask($network_ip)) if !defined($network_cidr);

		$log->log(level=>'debug', message=>"comparing $ip is in network $network ($network_ip/$network_cidr) ... \n") if $opt{verbose} >= 4 and $DEBUG eq 'true';
		if(ipv4_in_network($network_ip, $network_cidr, $ip, $cidr))
		{
			if($routes{$network}{'class'} eq 'bgp') {

				$path = $routes{$network}{'path'};
                       		$asn = $routes{$network}{'asn'};
				$cache_date = $routes{$network}{'cacheDate'};
				$orgName = $routes{$network}{'orgName'};
				if($orgName eq '') {
					$orgName = 'NULL';
				}
				$netName = $routes{$network}{'netName'};
				if($netName eq '') {
					$netName = 'NULL';
				}
			}                        
			else
			{
				$log->log(level=>'error', message=>" woah ... unknown routing table entry found for $network\n")
					if $opt{verbose} and $DEBUG eq 'true';
			}

			$log->log(level=>'debug', message=>" found $ip in network $network with most specific asn $asn\n")
                		if $opt{verbose} >= 7 and $DEBUG eq 'true';
			return (1, $network, $asn, $path, $orgName, $netName, $cache_date);
		}
	}
	$log->log(level=>'debug', message=>"$ip was not found in the routing table.\n") if $opt{verbose} >= 7 and $DEBUG eq 'true';
	return (0,'','','','', '', '');
}

sub ipv4_is_valid_quaddot($)
{
	my $ip = shift;

	if($ip =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/)
	{
		my (@octets) = split /\./, $ip;
		foreach (@octets) {
			return 0 if $_  < 0 or $_ > 255;
		}
	}
	elsif($ip =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/\d{1,2}$/)
	{
		my ($ip_addr, $cidr) = split /\//, $ip;
	 	my (@octets) = split /\./, $ip_addr;
	        foreach (@octets) {
			return 0 if $_  < 0 or $_ > 255;
		}

		if($cidr < 0 or $cidr > 32) {
			return 0;	
		}
	}
	else
	{
		return 0;
	}
	# valid quaddot
	return 1;
}


sub contains 
{
	my $value = shift;
	my @array = @_;

        foreach my $av (@array) {
#		$log->log(level=>"debug", message=>"Comparing value $value eq $av\n") if $DEBUG eq 'true';
		return 1 if $value == $av;
	}
	return 0;
}


#  Some of these ipv4 functions are from the IPv4Addr module.  I had to modify
# some of the to work appropriately for our use here.
#
#    Author: Francis J. Lacoste <francis@Contre.COM>
#
#    Copyright (C) 1999 Francis J. Lacoste, iNsu Innovations Inc.
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms as perl itself.

my $ip_rgx = "\\d+\\.\\d+\\.\\d+\\.\\d+";

# Given an IPv4 address in host, ip/netmask or cidr format
# returns a ip / cidr pair.
sub ipv4_parse($;$) {
  my ($ip,$msk);
  # Called with 2 args, assume first is IP address
  if ( defined $_[1] ) {
    $ip = $_[0];
    $msk= $_[1];
  } else {
    ($ip)  = $_[0] =~ /($ip_rgx)/o;
    ($msk) = $_[0] =~ m!/(.+)!o;
  }

  # Remove white spaces
  $msk =~ s/\s//g if defined $msk;

  # Check Netmask to see if it is a CIDR or Network
  if (defined $msk ) {
    if ($msk =~ /^\d{1,2}$/) {
      # Check cidr
      warn ": invalid cidr: ". $msk ."\n"
        if $msk < 0 or $msk > 32;
    } elsif ($msk =~ /^$ip_rgx$/o ) {
      $msk = ipv4_msk2cidr($msk);
    } else {
      warn ": invalid netmask specification: ". $msk ."\n";
    }
  } else {
    # Host
    return $ip;
  }
  wantarray ? ($ip,$msk) : "$ip/$msk";
}

sub ipv4_dflt_netmask($) {
  my ($ip) = ipv4_parse($_[0]);

  my ($b1) = split /\./, $ip;

  return "255.0.0.0"	if $b1 <= 127;
  return "255.255.0.0"	if $b1 <= 191;
  return "255.255.255.0";
}

# Transform a netmask in a CIDR mask length
sub ipv4_msk2cidr($) {
  my $msk = $_[0];
  my @bytes = split /\./, $msk;
  my $cidr = 0;
  for (@bytes) {
    my $bits = unpack( "B*", pack( "C", $_ ) );
    $cidr +=  $bits =~ tr /1/1/;
  }
  return $cidr;
}

# Transform a CIDR mask length in a netmask
sub ipv4_cidr2msk($) {
  my $cidr = shift;
  my $bits = "1" x $cidr . "0" x (32 - $cidr);
  return join ".", (unpack 'CCCC', pack("B*", $bits ));
}

# Return the network address of
# an IPv4 address
sub ipv4_network($;$) {
  my ($ip,$cidr) = ipv4_parse( $_[0], $_[1] );

  # If only an host is given, use the default netmask
  unless ($cidr) {
    $cidr = ipv4_msk2cidr( ipv4_dflt_netmask($ip) );
  }
  my $u32 = unpack "N", pack "CCCC", split /\./, $ip;
  my $bits = "1" x $cidr . "0" x (32 - $cidr );

  my $msk = unpack "N", pack "B*", $bits;

  my $net = join ".", unpack "CCCC", pack "N", $u32 & $msk;

  wantarray ? ( $net, $cidr) : "$net/$cidr";
}

sub ipv4_in_network($$;$$) {
  my ($ip1,$cidr1,$ip2,$cidr2);
  if ( @_ >= 3) {
  	($ip1,$cidr1) = ipv4_parse( $_[0], $_[1] );
	($ip2,$cidr2) = ipv4_parse( $_[2], $_[3] );
  } else {
	($ip1,$cidr1) = ipv4_parse( $_[0]);
	($ip2,$cidr2) = ipv4_parse( $_[1]);
  }

  # Check for magic addresses.
  return 0 if $ip1 eq "255.255.255.255" or $ip1 eq "0.0.0.0";
  return 0 if $ip2 eq "255.255.255.255" or $ip2 eq "0.0.0.0";
			  
  # Case where first argument is really a host
  return $ip1 eq $ip2 unless ($cidr1);
  # Case where second argument is an host
  if ( not defined $cidr2) {
	return ipv4_network( $ip1, $cidr1) eq ipv4_network( $ip2, $cidr1 );
  } elsif ( $cidr2 > $cidr1 ) {
  	# Netmask 2 is more specific than netmask 1
 	 return ipv4_network( $ip1, $cidr2) eq ipv4_network( $ip2, $cidr2);
  } else {
	# Netmask 1 is more specific than netmask 2
	return ipv4_network( $ip1, $cidr1) eq ipv4_network( $ip2, $cidr2);
  }
}

# convert quaddot to decimal integer
sub ipv4_quaddot_to_decimal($) {
	my $ip = shift;
	warn "invalid ip address provided" if !defined($ip);
	return -1 if !defined($ip);
	warn "invalid ip address provided: $ip" if(! $ip =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/);
	return "" if(! $ip =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/);
       
	return unpack "N", pack "CCCC", split /\./, $ip;
}

# convert decimal integer to quaddot
sub ipv4_decimal_to_quaddot($) {
	my $decimal = shift;
	warn "invalid decimal value provided" if !defined($decimal);
	return "" if !defined($decimal);
	
	warn "invalid decimal value provided: $decimal" if($decimal < 0 or $decimal > 4294967295);
	return "" if($decimal < 0 or $decimal > 4294967295);
        return join ".", unpack "CCCC", pack "N", $decimal;
}
 
my $obj = new Pwhoisd(); 
$obj->load_data() if !defined($opt{'no-load'});
$obj->startServer();

1;
