mirror of
https://github.com/adulau/pdns-toolkit.git
synced 2024-12-22 08:46:00 +00:00
Initial passive DNS server commit at CanSecWest
This commit is contained in:
parent
52da990aad
commit
9be511dc94
15 changed files with 26273 additions and 0 deletions
6
pdns-server/bin/pdns-dnscap2feeder.sh
Normal file
6
pdns-server/bin/pdns-dnscap2feeder.sh
Normal file
|
@ -0,0 +1,6 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# A simple feeder using dnscap input
|
||||
#
|
||||
|
||||
./dnscap -T -s r -ieth1 -g 2>&1 >/dev/null | perl pdns-feeder.pl
|
147
pdns-server/bin/pdns-feeder.pl
Normal file
147
pdns-server/bin/pdns-feeder.pl
Normal file
|
@ -0,0 +1,147 @@
|
|||
#
|
||||
# Minimal and Scalable Passive DNS - Redis feeder
|
||||
#
|
||||
# Read dnscap output to feed a Redis database
|
||||
#
|
||||
#
|
||||
# Copyright (C) 2010-2012 Alexandre Dulaunoy
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Date::Manip;
|
||||
use Redis;
|
||||
use Scalar::Util;
|
||||
use IO::Handle;
|
||||
|
||||
$| = 1;
|
||||
|
||||
my $block = 0;
|
||||
my $date;
|
||||
my %counter = (
|
||||
"processed" => 0,
|
||||
"redisrst" => 0
|
||||
);
|
||||
my @RRType = ( "A", "AAAA", "CNAME", "NS" );
|
||||
my %RRTypevalue = (
|
||||
"A" => 1,
|
||||
"AAAA" => 28,
|
||||
"CNAME" => 5,
|
||||
"NS" => 2,
|
||||
"MX" => 15,
|
||||
"SRV" => 33
|
||||
);
|
||||
|
||||
my $parsedate = new Date::Manip::Date;
|
||||
|
||||
my $r = Redis->new( server => '127.0.0.1:6379', encoding => undef );
|
||||
|
||||
my $stdin = new IO::Handle;
|
||||
$stdin->fdopen( fileno(STDIN), "r" );
|
||||
|
||||
while ( defined( $_ = $stdin->getline ) ) {
|
||||
|
||||
if ( $counter{'redisrst'} == 1 ) {
|
||||
$r->quit;
|
||||
undef($r);
|
||||
print "Reset\n";
|
||||
$r = Redis->new( server => '127.0.0.1:6379', encoding => undef );
|
||||
$counter{'redisrst'} = 0;
|
||||
}
|
||||
if (m/^\[/) {
|
||||
$block = 1;
|
||||
{
|
||||
my @s = split;
|
||||
$date = $s[1];
|
||||
}
|
||||
}
|
||||
|
||||
if ( !(m/^\[/) ) {
|
||||
$_ =~ s/\cI//;
|
||||
|
||||
# discarding - [1.2.3.4].53 [149.13.33.69].5234
|
||||
if (m/^\[/) { next; }
|
||||
|
||||
# split - 21 ns10.ovh.net,IN,AAAA,172800,2001:41d0:1:1981::1 \
|
||||
if (m/^\d* /) {
|
||||
$_ =~ s/^\d* //;
|
||||
}
|
||||
|
||||
# discarding - dns QUERY,NOERROR,26278,qr|rd|ra \
|
||||
if (m/^dns /) { next; }
|
||||
|
||||
# decode line
|
||||
ProcessRequest( $date, $_ );
|
||||
$counter{'processed'}++;
|
||||
}
|
||||
if ( ( $counter{'processed'} % 1000 ) == 0 ) {
|
||||
$counter{'redisrst'} = 1;
|
||||
print "Processed:"
|
||||
. $counter{'processed'}
|
||||
. " Redist RST:"
|
||||
. $counter{'redisrst'} . "\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub ProcessRequest {
|
||||
my $epoch = shift;
|
||||
my $line = shift;
|
||||
chomp($line);
|
||||
|
||||
#my $err = $parsedate->parse($date);
|
||||
#my $epoch = $parsedate->printf('%s');
|
||||
|
||||
my @l = split( /,/, $line );
|
||||
if ( $l[2] ~~ @RRType ) {
|
||||
my @rdatas = split( / /, $l[4] );
|
||||
|
||||
#$l[4] =~ s/\s\\.*$//g;
|
||||
#$l[4] =~ s/(.*)\s/$1/g;
|
||||
print $l[0] . " " . $l[2] . " " . "$rdatas[0]\n";
|
||||
my @rdatap = split( / /, $l[4] );
|
||||
RedisUpdate( $l[0], $l[2], $rdatap[0], $epoch );
|
||||
undef(@rdatap);
|
||||
}
|
||||
}
|
||||
|
||||
sub RedisUpdate {
|
||||
my $name = lc(shift);
|
||||
my $recordtype = shift;
|
||||
my $rdata = lc(shift);
|
||||
my $timestamp = shift;
|
||||
|
||||
my $recordtypevalue = $RRTypevalue{$recordtype};
|
||||
my $pdns_r = "r:" . $name . ":" . $recordtypevalue;
|
||||
my $pdns_v = "v:" . $rdata;
|
||||
my $pdns_s = "s:" . $name . ":" . $rdata;
|
||||
my $pdns_l = "l:" . $name . ":" . $rdata;
|
||||
my $pdns_o = "o:" . $name . ":" . $rdata;
|
||||
|
||||
my $ret = $r->sadd( $pdns_r, $rdata );
|
||||
$ret = $r->sadd( $pdns_v, $name );
|
||||
|
||||
# set first seen value
|
||||
if ( !( $r->exists($pdns_s) ) ) {
|
||||
$ret = $r->set( $pdns_s, $timestamp );
|
||||
}
|
||||
|
||||
# set last seen value
|
||||
$ret = $r->set( $pdns_l, $timestamp );
|
||||
|
||||
# increment the occurence value
|
||||
$ret = $r->incr($pdns_o);
|
||||
}
|
85
pdns-server/bin/pdns-ranking.py
Normal file
85
pdns-server/bin/pdns-ranking.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
#
|
||||
# Minimal and Scalable Passive DNS - ranking records
|
||||
#
|
||||
# Copyright (C) 2012 Alexandre Dulaunoy
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Sample input (pdns-ranking.py) read line with the Passive DNS format:
|
||||
# s:truecsi.org:ns3.afraid.org. = 1327737190
|
||||
#
|
||||
# and output the following:
|
||||
# truecsi.org
|
||||
# [(1.0003765060241001, 'truecsi.org')]
|
||||
# truecsi.org,1.00037650602
|
||||
#
|
||||
# and update the DB 6 database
|
||||
# redis> select 6
|
||||
# OK
|
||||
# redis> KEYS "*"
|
||||
# 1) "truecsi.org"
|
||||
# redis> GET "truecsi.org"
|
||||
# "1.00037650602"
|
||||
# redis>
|
||||
|
||||
|
||||
import sys
|
||||
import re
|
||||
import redis
|
||||
|
||||
path = "../lib/DomainClassifier/DomainClassifier/"
|
||||
sys.path.append(path)
|
||||
|
||||
import domainclassifier
|
||||
|
||||
|
||||
def domexist(ldomain = None):
|
||||
print ldomain
|
||||
r = redis.StrictRedis(host='localhost', port=6379, db=6)
|
||||
|
||||
if ldomain is not None:
|
||||
if r.exists(ldomain):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def domstore(ldomain = None, rank = 1.0):
|
||||
r = redis.StrictRedis(host='localhost', port=6379, db=6)
|
||||
return r.set(ldomain,rank)
|
||||
|
||||
for line in sys.stdin:
|
||||
domain = line.split(':')[1].lower()
|
||||
if not (re.search("(spamhaus.org|arpa)$", domain)):
|
||||
if not domexist(ldomain=domain):
|
||||
c = domainclassifier.Extract(domain)
|
||||
c.domain()
|
||||
c.validdomain()
|
||||
r = c.rankdomain()
|
||||
if r:
|
||||
r.sort(reverse=True)
|
||||
ranking = r[0][0]
|
||||
print r
|
||||
if ranking is not None:
|
||||
print domain+","+str(ranking)
|
||||
domstore(ldomain=domain, rank=ranking)
|
||||
else:
|
||||
print domain+","+str(1)
|
||||
domstore(ldomain=domain)
|
||||
else:
|
||||
print domain+","+str(1)
|
||||
domstore(ldomain=domain)
|
||||
else:
|
||||
print domain+" already cached"
|
114
pdns-server/bin/query.pl
Normal file
114
pdns-server/bin/query.pl
Normal file
|
@ -0,0 +1,114 @@
|
|||
#
|
||||
# Minimal and Scalable Passive DNS - Query tool
|
||||
#
|
||||
# Copyright (C) 2010-2012 Alexandre Dulaunoy
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
use POSIX qw(strftime);
|
||||
use Redis;
|
||||
use Date::Manip;
|
||||
|
||||
# will lookup the following RR types
|
||||
my %RRTypevalue = (
|
||||
"A" => 1,
|
||||
"AAAA" => 28,
|
||||
"CNAME" => 5,
|
||||
"NS" => 2
|
||||
|
||||
);
|
||||
|
||||
my $r = Redis->new( server => '127.0.0.1:6379', encoding => undef );
|
||||
|
||||
#sample feeder input
|
||||
#1298823157.764221 "SET" "l:www.l.google.com:72.14.204.103" "1298819557"
|
||||
#1298823157.764353 "INCR" "o:www.l.google.com:72.14.204.103"
|
||||
#1298823157.765310 "SADD" "r:www.l.google.com:1" "72.14.204.104"
|
||||
#1298823157.765439 "SADD" "v:72.14.204.104" "www.l.google.com"
|
||||
#1298823157.765567 "EXISTS" "s:www.l.google.com:72.14.204.104"
|
||||
#1298823157.765696 "SET" "l:www.l.google.com:72.14.204.104" "1298819557"
|
||||
#1298823157.765823 "INCR" "o:www.l.google.com:72.14.204.104"
|
||||
#1298823157.766776 "SADD" "r:www.l.google.com:1" "72.14.204.147"
|
||||
#1298823157.766901 "SADD" "v:72.14.204.147" "www.l.google.com"
|
||||
#1298823157.767086 "EXISTS" "s:www.l.google.com:72.14.204.147"
|
||||
#1298823157.767207 "SET" "l:www.l.google.com:72.14.204.147" "1298819557"
|
||||
#1298823157.767333 "INCR" "o:www.l.google.com:72.14.204.147"
|
||||
|
||||
while ( my ( $typename, $typevalue ) = each(%RRTypevalue) ) {
|
||||
|
||||
my @x = Lookup( $ARGV[0], $typevalue );
|
||||
|
||||
if ( defined(@x) ) {
|
||||
foreach $p (@x) {
|
||||
print $p. "("
|
||||
. $typename . ")"
|
||||
. " first seen: "
|
||||
. NiceDate( FirstSeen( $ARGV[0], $p ) )
|
||||
. " last seen: "
|
||||
. NiceDate( LastSeen( $ARGV[0], $p ) )
|
||||
. " Hits: "
|
||||
. OccTuple( $ARGV[0], $p ) . "\n";
|
||||
print " behind: " . join( ",", LookupIP($p) ) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sub NiceDate {
|
||||
my $epochvalue = shift;
|
||||
|
||||
return strftime( "%Y%m%d-%H:%M:%S", localtime($epochvalue) );
|
||||
|
||||
}
|
||||
|
||||
sub Lookup {
|
||||
my $name = shift;
|
||||
my $type = shift;
|
||||
|
||||
# default A record
|
||||
if ( !( defined($type) ) ) { $type = 1; }
|
||||
my $key = "r:$name:$type";
|
||||
return $r->smembers($key);
|
||||
}
|
||||
|
||||
sub LookupIP {
|
||||
my $name = shift;
|
||||
my $key = "v:$name";
|
||||
return $r->smembers($key);
|
||||
}
|
||||
|
||||
sub FirstSeen {
|
||||
my $name = shift;
|
||||
my $rdata = shift;
|
||||
|
||||
my $key = "s:$name:$rdata";
|
||||
return $r->get($key);
|
||||
}
|
||||
|
||||
sub LastSeen {
|
||||
my $name = shift;
|
||||
my $rdata = shift;
|
||||
|
||||
my $key = "l:$name:$rdata";
|
||||
return $r->get($key);
|
||||
}
|
||||
|
||||
sub OccTuple {
|
||||
my $name = shift;
|
||||
my $rdata = shift;
|
||||
|
||||
my $key = "o:$name:$rdata";
|
||||
return $r->get($key);
|
||||
}
|
79
pdns-server/doc/RR-Type.txt
Normal file
79
pdns-server/doc/RR-Type.txt
Normal file
|
@ -0,0 +1,79 @@
|
|||
A 1 a host address [RFC1035]
|
||||
NS 2 an authoritative name server [RFC1035]
|
||||
MD 3 a mail destination (Obsolete - use MX) [RFC1035]
|
||||
MF 4 a mail forwarder (Obsolete - use MX) [RFC1035]
|
||||
CNAME 5 the canonical name for an alias [RFC1035]
|
||||
SOA 6 marks the start of a zone of authority [RFC1035]
|
||||
MB 7 a mailbox domain name (EXPERIMENTAL) [RFC1035]
|
||||
MG 8 a mail group member (EXPERIMENTAL) [RFC1035]
|
||||
MR 9 a mail rename domain name (EXPERIMENTAL) [RFC1035]
|
||||
NULL 10 a null RR (EXPERIMENTAL) [RFC1035]
|
||||
WKS 11 a well known service description [RFC1035]
|
||||
PTR 12 a domain name pointer [RFC1035]
|
||||
HINFO 13 host information [RFC1035]
|
||||
MINFO 14 mailbox or mail list information [RFC1035]
|
||||
MX 15 mail exchange [RFC1035]
|
||||
TXT 16 text strings [RFC1035]
|
||||
RP 17 for Responsible Person [RFC1183]
|
||||
AFSDB 18 for AFS Data Base location [RFC1183][RFC5864]
|
||||
X25 19 for X.25 PSDN address [RFC1183]
|
||||
ISDN 20 for ISDN address [RFC1183]
|
||||
RT 21 for Route Through [RFC1183]
|
||||
NSAP 22 for NSAP address, NSAP style A record [RFC1706]
|
||||
NSAP-PTR 23 for domain name pointer, NSAP style [RFC1348]
|
||||
SIG 24 for security signature [RFC4034][RFC3755][RFC2535]
|
||||
KEY 25 for security key [RFC4034][RFC3755][RFC2535]
|
||||
PX 26 X.400 mail mapping information [RFC2163]
|
||||
GPOS 27 Geographical Position [RFC1712]
|
||||
AAAA 28 IP6 Address [RFC3596]
|
||||
LOC 29 Location Information [RFC1876]
|
||||
NXT 30 Next Domain - OBSOLETE [RFC3755][RFC2535]
|
||||
EID 31 Endpoint Identifier [Patton]
|
||||
NIMLOC 32 Nimrod Locator [Patton]
|
||||
SRV 33 Server Selection [RFC2782]
|
||||
ATMA 34 ATM Address [ATMDOC]
|
||||
NAPTR 35 Naming Authority Pointer [RFC2915][RFC2168][RFC3403]
|
||||
KX 36 Key Exchanger [RFC2230]
|
||||
CERT 37 CERT [RFC4398]
|
||||
A6 38 A6 (Experimental) [RFC3226][RFC2874]
|
||||
DNAME 39 DNAME [RFC2672]
|
||||
SINK 40 SINK [Eastlake]
|
||||
OPT 41 OPT [RFC2671]
|
||||
APL 42 APL [RFC3123]
|
||||
DS 43 Delegation Signer [RFC4034][RFC3658]
|
||||
SSHFP 44 SSH Key Fingerprint [RFC4255]
|
||||
IPSECKEY 45 IPSECKEY [RFC4025]
|
||||
RRSIG 46 RRSIG [RFC4034][RFC3755]
|
||||
NSEC 47 NSEC [RFC4034][RFC3755]
|
||||
DNSKEY 48 DNSKEY [RFC4034][RFC3755]
|
||||
DHCID 49 DHCID [RFC4701]
|
||||
NSEC3 50 NSEC3 [RFC5155]
|
||||
NSEC3PARAM 51 NSEC3PARAM [RFC5155]
|
||||
Unassigned 52-54
|
||||
HIP 55 Host Identity Protocol [RFC5205]
|
||||
NINFO 56 NINFO [Reid]
|
||||
RKEY 57 RKEY [Reid]
|
||||
TALINK 58 Trust Anchor LINK [Wijngaards]
|
||||
Unassigned 59-98
|
||||
SPF 99 [RFC4408]
|
||||
UINFO 100 [IANA-Reserved]
|
||||
UID 101 [IANA-Reserved]
|
||||
GID 102 [IANA-Reserved]
|
||||
UNSPEC 103 [IANA-Reserved]
|
||||
Unassigned 104-248
|
||||
TKEY 249 Transaction Key [RFC2930]
|
||||
TSIG 250 Transaction Signature [RFC2845]
|
||||
IXFR 251 incremental transfer [RFC1995]
|
||||
AXFR 252 transfer of an entire zone [RFC1035][RFC5936]
|
||||
MAILB 253 mailbox-related RRs (MB, MG or MR) [RFC1035]
|
||||
MAILA 254 mail agent RRs (Obsolete - see MX) [RFC1035]
|
||||
* 255 A request for all records [RFC1035]
|
||||
URI 256 URI [Faltstrom]
|
||||
Unassigned 257-32767
|
||||
TA 32768 DNSSEC Trust Authorities [Weiler] 2005-12-13
|
||||
DLV 32769 DNSSEC Lookaside Validation [RFC4431]
|
||||
Unassigned 32770-65279
|
||||
Private use 65280-65534
|
||||
Reserved 65535
|
||||
|
||||
|
97
pdns-server/doc/datastore-format.txt
Normal file
97
pdns-server/doc/datastore-format.txt
Normal file
|
@ -0,0 +1,97 @@
|
|||
This is a description of the various data-structure used
|
||||
in the Passive DNS toolkit.
|
||||
|
||||
= Redis Database number =
|
||||
|
||||
- (0) default passive dns
|
||||
- (6) ranking of hostname
|
||||
|
||||
= format for redis db 0 =
|
||||
|
||||
KEY, VALUE
|
||||
|
||||
s:<tuple> <epoch timestamp>
|
||||
l:<tuple> <epoch timestamp>
|
||||
o:<tuple> <integer value>
|
||||
|
||||
KEY, {SET}
|
||||
|
||||
r:<record>:<rr-type-value> {<answer>}
|
||||
v:<answer> {<record>}
|
||||
|
||||
# "SET" "l:www.l.google.com:72.14.204.103" "1298819557"
|
||||
#1298823157.764353 "INCR" "o:www.l.google.com:72.14.204.103"
|
||||
#1298823157.765310 "SADD" "r:www.l.google.com:1" "72.14.204.104"
|
||||
#1298823157.765439 "SADD" "v:72.14.204.104" "www.l.google.com"
|
||||
#1298823157.765567 "EXISTS" "s:www.l.google.com:72.14.204.104"
|
||||
#1298823157.765696 "SET" "l:www.l.google.com:72.14.204.104" "1298819557"
|
||||
#1298823157.765823 "INCR" "o:www.l.google.com:72.14.204.104"
|
||||
#1298823157.766776 "SADD" "r:www.l.google.com:1" "72.14.204.147"
|
||||
#1298823157.766901 "SADD" "v:72.14.204.147" "www.l.google.com"
|
||||
#1298823157.767086 "EXISTS" "s:www.l.google.com:72.14.204.147"
|
||||
#1298823157.767207 "SET" "l:www.l.google.com:72.14.204.147" "1298819557"
|
||||
#1298823157.767333 "INCR" "o:www.l.google.com:72.14.204.147"
|
||||
|
||||
|
||||
== sample db 0 ==
|
||||
|
||||
Here is the classical update process for a feeder:
|
||||
|
||||
Set or update the last-seen.
|
||||
|
||||
#1298823157.764221 "SET" "l:www.l.google.com:72.14.204.103" "1298819557"
|
||||
|
||||
Update the occurence of the answer seen.
|
||||
|
||||
#1298823157.764353 "INCR" "o:www.l.google.com:72.14.204.103"
|
||||
|
||||
Add to a set "record:rr-type" the seen answer.
|
||||
|
||||
#1298823157.765310 "SADD" "r:www.l.google.com:1" "72.14.204.104"
|
||||
|
||||
Add the reverse for fast lookup.
|
||||
|
||||
#1298823157.765439 "SADD" "v:72.14.204.104" "www.l.google.com"
|
||||
|
||||
If there is no existing first-seen for the tuple,
|
||||
|
||||
#1298823157.765567 "EXISTS" "s:www.l.google.com:72.14.204.104"
|
||||
|
||||
A first-seen is set:
|
||||
|
||||
#1298823157.765696 "SET" "l:www.l.google.com:72.14.204.104" "1298819557"
|
||||
|
||||
and so on...
|
||||
|
||||
#1298823157.765823 "INCR" "o:www.l.google.com:72.14.204.104"
|
||||
#1298823157.766776 "SADD" "r:www.l.google.com:1" "72.14.204.147"
|
||||
#1298823157.766901 "SADD" "v:72.14.204.147" "www.l.google.com"
|
||||
#1298823157.767086 "EXISTS" "s:www.l.google.com:72.14.204.147"
|
||||
#1298823157.767207 "SET" "l:www.l.google.com:72.14.204.147" "1298819557"
|
||||
#1298823157.767333 "INCR" "o:www.l.google.com:72.14.204.147"
|
||||
|
||||
= format for redis db 6 =
|
||||
|
||||
This db index hold a ranking value per hostname.
|
||||
|
||||
KEY, VALUE
|
||||
|
||||
hostname, float double precision
|
||||
|
||||
This can be seen as a cache when an expiration can be set
|
||||
by key to limit the lifetime of a ranking. You can create
|
||||
a persistent ranking for setting up some "golden" domains.
|
||||
|
||||
== sample db 6 ==
|
||||
|
||||
1) "truecsi.org"
|
||||
redis> GET "truecsi.org"
|
||||
"1.00037650602"
|
||||
redis>
|
||||
|
||||
= contact =
|
||||
|
||||
Update, extension, or new data-structure more than welcome.
|
||||
|
||||
https://www.github.com/adulau/
|
||||
|
3
pdns-server/lib/install.sh
Normal file
3
pdns-server/lib/install.sh
Normal file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
git clone git://github.com/adulau/DomainClassifier.git
|
171
pdns-server/web/PDNSq.pm
Normal file
171
pdns-server/web/PDNSq.pm
Normal file
|
@ -0,0 +1,171 @@
|
|||
#
|
||||
# Minimal and Scalable Passive DNS - Query tool
|
||||
#
|
||||
# Copyright (C) 2010-2012 Alexandre Dulaunoy
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package PDNS::Query;
|
||||
|
||||
use base 'Exporter';
|
||||
|
||||
use POSIX qw(strftime);
|
||||
use Redis;
|
||||
use Date::Manip;
|
||||
use Time::Format;
|
||||
|
||||
my %RRTypevalue = (
|
||||
"A" => 1,
|
||||
"NS" => 2,
|
||||
"AAAA" => 28,
|
||||
"CNAME" => 5
|
||||
);
|
||||
|
||||
my $r = Redis->new( server => '127.0.0.1:6379', encoding => undef );
|
||||
|
||||
my $burl = "/l/";
|
||||
|
||||
sub Query {
|
||||
|
||||
my $query = shift;
|
||||
my $ret = "";
|
||||
$ret .= "<a id=\"v_toggle\" href=\"\#\">[+-]</a>";
|
||||
$ret .= "<div id=\"vertical_slide\">";
|
||||
|
||||
while ( my ( $typename, $typevalue ) = each(%RRTypevalue) ) {
|
||||
|
||||
my @x = Lookup( $query, $typevalue );
|
||||
if ( defined(@x) ) {
|
||||
foreach $p (@x) {
|
||||
if ( $typename =~ m/CNAME/ ) {
|
||||
$ret .= MakeLink($p) . "(" . $typename . ")";
|
||||
}
|
||||
else {
|
||||
$ret .= $p . "(" . $typename . ")";
|
||||
}
|
||||
$ret .=
|
||||
" first seen: "
|
||||
. NiceDate( FirstSeen( $query, $p ) )
|
||||
. " last seen: "
|
||||
. NiceDate( LastSeen( $query, $p ) )
|
||||
. " Hits: "
|
||||
. OccTuple( $query, $p ) . "\n";
|
||||
|
||||
#$ret .= " behind: " . join( "" , LookupIP($p) ) . "\n";
|
||||
$ret .= "<br />";
|
||||
foreach $n ( LookupIP($p) ) {
|
||||
$ret .= MakeLink($n);
|
||||
}
|
||||
$ret .= "<br /> <br />";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
$ret .= "</div>";
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub QueryJSON {
|
||||
my $query = shift;
|
||||
my $ret = <<JSHEAD;
|
||||
var event_data =
|
||||
{
|
||||
"dateTimeFormat": "iso8601",
|
||||
"events":[
|
||||
JSHEAD
|
||||
while ( my ( $typename, $typevalue ) = each(%RRTypevalue) ) {
|
||||
my @x = Lookup( $query, $typevalue );
|
||||
|
||||
if ( defined(@x) ) {
|
||||
foreach $p (@x) {
|
||||
$ret .= "\{\"start\": \""
|
||||
. NiceDateISO( FirstSeen( $query, $p ) ) . "\",\n";
|
||||
$ret .= "\"end\": \""
|
||||
. NiceDateISO( LastSeen( $query, $p ) ) . "\",\n";
|
||||
chomp($p);
|
||||
$ret .= "\"title\": \"" . $p . "(" . $typename . ")\",\n";
|
||||
$ret .= "\"description\": \"" . $p . "(" . $typename . ")\",\n";
|
||||
$ret .= "\"instant\": \"false\",\n";
|
||||
$ret .= "\},\n";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
chop($ret);
|
||||
chop($ret);
|
||||
return $ret . "] };\n";
|
||||
}
|
||||
|
||||
sub MakeLink {
|
||||
my $value = shift;
|
||||
my $ret = "<a href=\"" . $burl . "" . $value . "\">$value</a> ";
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub NiceDate {
|
||||
my $epochvalue = shift;
|
||||
|
||||
return strftime( "%Y%m%d-%H:%M:%S", localtime($epochvalue) );
|
||||
|
||||
}
|
||||
|
||||
sub NiceDateISO {
|
||||
my $epochvalue = shift;
|
||||
my $format = "yyyy-mm{on}-ddThh:mm:ss+00:00";
|
||||
return time_format( $format, $epochvalue );
|
||||
}
|
||||
|
||||
sub Lookup {
|
||||
my $name = shift;
|
||||
my $type = shift;
|
||||
|
||||
# default A record
|
||||
if ( !( defined($type) ) ) { $type = 1; }
|
||||
my $key = "r:$name:$type";
|
||||
return $r->smembers($key);
|
||||
}
|
||||
|
||||
sub LookupIP {
|
||||
my $name = shift;
|
||||
my $key = "v:$name";
|
||||
return $r->smembers($key);
|
||||
}
|
||||
|
||||
sub FirstSeen {
|
||||
my $name = shift;
|
||||
my $rdata = shift;
|
||||
|
||||
my $key = "s:$name:$rdata";
|
||||
return $r->get($key);
|
||||
}
|
||||
|
||||
sub LastSeen {
|
||||
my $name = shift;
|
||||
my $rdata = shift;
|
||||
|
||||
my $key = "l:$name:$rdata";
|
||||
return $r->get($key);
|
||||
}
|
||||
|
||||
sub OccTuple {
|
||||
my $name = shift;
|
||||
my $rdata = shift;
|
||||
|
||||
my $key = "o:$name:$rdata";
|
||||
return -1;
|
||||
|
||||
#return $r->get($key);
|
||||
}
|
||||
|
||||
1;
|
214
pdns-server/web/pdns-web.pl
Executable file
214
pdns-server/web/pdns-web.pl
Executable file
|
@ -0,0 +1,214 @@
|
|||
#!/bin/env perl
|
||||
#
|
||||
# Minimal and Scalable Passive DNS - Web interface
|
||||
#
|
||||
# Copyright (C) 2010-2012 Alexandre Dulaunoy
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
use Mojolicious::Lite;
|
||||
use Mojolicious::Static;
|
||||
use PDNSq;
|
||||
|
||||
# Documentation browser under "/perldoc" (this plugin requires Perl 5.10)
|
||||
plugin 'pod_renderer';
|
||||
|
||||
get '/l/(.query)' => sub {
|
||||
my $self = shift;
|
||||
my $q = $self->param('query');
|
||||
my $ret = PDNS::Query::Query($q);
|
||||
my $json = PDNS::Query::QueryJSON($q);
|
||||
$self->render(template => 'index', q => $ret, query => $q, jq => $json);
|
||||
};
|
||||
|
||||
post '/l' => sub {
|
||||
my $self = shift;
|
||||
my $q = $self->param('q');
|
||||
my $ret = PDNS::Query::Query($q);
|
||||
my $json = PDNS::Query::QueryJSON($q);
|
||||
$self->render(template => 'index', q => $ret, query => $q, jq => $json);
|
||||
};
|
||||
|
||||
get '/js/(.query)' => sub {
|
||||
my $self = shift;
|
||||
my $q = $self->param('query');
|
||||
$self->render_static('static/'.$q);
|
||||
};
|
||||
|
||||
get '/__history__.html' => sub {
|
||||
my $self = shift;
|
||||
$self->render_static('static/__history__.html');
|
||||
};
|
||||
|
||||
get '/' => sub {
|
||||
my $self = shift;
|
||||
$self->render(template => 'main');
|
||||
};
|
||||
|
||||
|
||||
app->secret('You have to change it');
|
||||
app->start;
|
||||
__DATA__
|
||||
|
||||
@@ main.html.ep
|
||||
% layout 'default';
|
||||
% title 'Passive DNS';
|
||||
<h1>Passive DNS interface</h1>
|
||||
<form name="lookup" action="/l/"i method=POST>
|
||||
<input type="text" name="q" />
|
||||
<input type="submit" value="lookup"/>
|
||||
</form>
|
||||
|
||||
@@ index.html.ep
|
||||
% layout 'default';
|
||||
% title 'PDNS Lookup';
|
||||
<h1>Passive DNS lookup result for <a href="/l/<%= $query %>"><%= $query %></a></h1>
|
||||
<a href="#visual">(go to visual timeline)</a><br /><br />
|
||||
<%== $q %>
|
||||
<a name="visual">Visual timeline</a>
|
||||
<div id="tl" class="timeline-default" style="height: 400px; border: 1px solid #aaa;"></div>
|
||||
|
||||
|
||||
<noscript>
|
||||
This page uses Javascript to show you a Timeline. Please enable Javascript in your browser to see the full page. Thank you.
|
||||
</noscript>
|
||||
<script>
|
||||
|
||||
var tl;
|
||||
//in php you can get this 1so8601 date using date("c",$you_date_variable);
|
||||
var startProj = SimileAjax.DateTime.parseIso8601DateTime("2011-03-01T00:00:00");
|
||||
var endProj = SimileAjax.DateTime.parseIso8601DateTime("2011-12-30T00:00:00");
|
||||
|
||||
<%== $jq %>
|
||||
|
||||
function onLoad() {
|
||||
var tl_el = document.getElementById("tl");
|
||||
var eventSource = new Timeline.DefaultEventSource();
|
||||
var theme = Timeline.ClassicTheme.create();
|
||||
theme.autoWidth = true; // Set the Timeline's "width" automatically.
|
||||
theme.autoWidthMargin=10;
|
||||
theme.event.bubble.width = 220;
|
||||
theme.event.bubble.height = 120;
|
||||
|
||||
theme.ether.backgroundColors = ["#E6E6E6","#F7F7F7"];
|
||||
|
||||
theme.timeline_start = startProj;
|
||||
theme.timeline_stop = endProj;
|
||||
|
||||
theme.event.track.height = "20";
|
||||
theme.event.tape.height = 10; // px
|
||||
theme.event.track.height = theme.event.tape.height + 6;
|
||||
|
||||
var d = SimileAjax.DateTime.parseIso8601DateTime("2011-03-01T00:00:00");
|
||||
var bandInfos = [
|
||||
|
||||
Timeline.createBandInfo({
|
||||
layout: 'original',// original, overview, detailed
|
||||
eventSource: eventSource,
|
||||
date: d,
|
||||
width: 350,
|
||||
intervalUnit: Timeline.DateTime.DAY,
|
||||
intervalPixels: 100,
|
||||
//trackHeight: 10,
|
||||
theme :theme
|
||||
|
||||
}),
|
||||
Timeline.createBandInfo({
|
||||
layout: 'overview',
|
||||
date: d,
|
||||
trackHeight: 0.5,
|
||||
trackGap: 0.2,
|
||||
eventSource: eventSource,
|
||||
width: 50,
|
||||
intervalUnit: Timeline.DateTime.MONTH,
|
||||
// showEventText: false,
|
||||
intervalPixels: 200,
|
||||
theme :theme
|
||||
})
|
||||
|
||||
];
|
||||
|
||||
bandInfos[1].highlight = true;
|
||||
bandInfos[1].syncWith = 0;
|
||||
|
||||
|
||||
|
||||
bandInfos[1].decorators = [
|
||||
new Timeline.SpanHighlightDecorator({
|
||||
// startDate: startProj,
|
||||
// endDate: endProj,
|
||||
inFront: false,
|
||||
color: "#FFC080",
|
||||
opacity: 30,
|
||||
startLabel: "Begin",
|
||||
endLabel: "End",
|
||||
theme: theme
|
||||
})
|
||||
];
|
||||
|
||||
|
||||
tl = Timeline.create(tl_el, bandInfos, Timeline.HORIZONTAL);
|
||||
// show loading message
|
||||
tl.showLoadingMessage();
|
||||
|
||||
eventSource.loadJSON(event_data, document.location.href);
|
||||
|
||||
// dismiss loading message
|
||||
tl.hideLoadingMessage();
|
||||
|
||||
// setup highlight filters
|
||||
//setupFilterHighlightControls(document.getElementById("controls"), tl, [0,1], theme);
|
||||
|
||||
}
|
||||
//function centerProjStart() {
|
||||
// tl.getBand(1).setCenterVisibleDate(startProj);
|
||||
//}
|
||||
|
||||
//function centerProjEnd() {
|
||||
// tl.getBand(1).setCenterVisibleDate(endProj);
|
||||
// }
|
||||
|
||||
var resizeTimerID = null;
|
||||
function onResize() {
|
||||
if(resizeTimerID == null) {
|
||||
resizeTimerID = window.setTimeout(function() {
|
||||
resizeTimerID = null;
|
||||
tl.layout();
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ layouts/default.html.ep
|
||||
<!doctype html><html>
|
||||
<head><title><%= title %></title>
|
||||
<link rel="stylesheet" type="text/css" href="http://www.circl.lu/css/styles.css">
|
||||
<link rel="stylesheet" type="text/css" href="/js/pdns.css">
|
||||
|
||||
<link rel="shortcut icon" href="/favicon.ico" />
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
|
||||
<script src="http://static.simile.mit.edu/timeline/api-2.3.0/timeline-api.js?bundle=true" type="text/javascript"></script>
|
||||
<script src="/js/mootools-core-1.3.1.js" type="text/javascript"></script>
|
||||
<script src="/js/mootools-more-1.3.1.1.js" type="text/javascript"></script>
|
||||
<script src="/js/moo-pdns.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body onload="onLoad();" onresize="onResize();">
|
||||
<div class="header" id="header">Passive DNS toolkit
|
||||
</div>
|
||||
<div class="body" id="body">
|
||||
<%= content =%></body>
|
||||
</div>
|
||||
</html>
|
5
pdns-server/web/pdns-web.sh
Normal file
5
pdns-server/web/pdns-web.sh
Normal file
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Will listen on port 3000
|
||||
perl pdns-web.pl daemon --reload
|
||||
|
17
pdns-server/web/public/static/moo-pdns.js
Normal file
17
pdns-server/web/public/static/moo-pdns.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
window.addEvent('domready', function() {
|
||||
|
||||
var status = {
|
||||
'true': 'open',
|
||||
'false': 'close'
|
||||
};
|
||||
|
||||
// -- vertical
|
||||
|
||||
var myVerticalSlide = new Fx.Slide('vertical_slide');
|
||||
|
||||
document.id('v_toggle').addEvent('click', function(event){
|
||||
event.stop();
|
||||
myVerticalSlide.toggle();
|
||||
});
|
||||
|
||||
});
|
5928
pdns-server/web/public/static/mootools-core-1.3.1-full-compat.js
vendored
Normal file
5928
pdns-server/web/public/static/mootools-core-1.3.1-full-compat.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
5928
pdns-server/web/public/static/mootools-core-1.3.1.js
vendored
Normal file
5928
pdns-server/web/public/static/mootools-core-1.3.1.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
13463
pdns-server/web/public/static/mootools-more-1.3.1.1.js
Normal file
13463
pdns-server/web/public/static/mootools-more-1.3.1.1.js
Normal file
File diff suppressed because it is too large
Load diff
16
pdns-server/web/public/static/pdns.css
Normal file
16
pdns-server/web/public/static/pdns.css
Normal file
|
@ -0,0 +1,16 @@
|
|||
h3.section {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
#vertical_slide, #horizontal_slide {
|
||||
background: #D0C8C8;
|
||||
color: #8A7575;
|
||||
padding: 10px;
|
||||
border: 5px solid #F3F1F1;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.marginbottom {
|
||||
/* Since the Fx.Slide element resets margins, we set a margin on the above element */
|
||||
margin-bottom: 10px;
|
||||
}
|
Loading…
Reference in a new issue