-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathdns_flood_collector.pl
executable file
·157 lines (120 loc) · 3.55 KB
/
dns_flood_collector.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/perl
use strict;
use threads;
use threads::shared;
use Sys::Syslog;
use Data::Dumper;
use Getopt::Long;
use POSIX;
use IO::Socket::Multicast;
use JSON;
# Native Maxmind library - http://www.maxmind.com/download/geoip/api/perl/
# requires: http://www.maxmind.com/app/c
use Geo::IP;
# set these to the same port and multicast (or unicast) address as the detector
use constant GROUP => '226.1.1.2';
use constant PORT => '2000';
my %ipc_source :shared;
my %ipc_customer :shared;
my $time_to_die :shared = 0;
my $debug;
my $foreground=0;
# determines how often you want to aggregage and write-out stats dumps
my $interval = 60;
# you can get the binary format GeoLiteCity.dat from Maxmind
# http://www.maxmind.com/app/geolitecity
my $gi = Geo::IP->open("/usr/local/GeoLiteCity.dat",GEOIP_MEMORY_CACHE | GEOIP_CHECK_CACHE);
# adjust this to the path where you want to keep the
sub PATH {'/tmp/'}
$|=1;
GetOptions(
"debug" => \$debug,
"foreground" => \$foreground,
"interval=s" => \$interval,
);
main();
exit();
sub main() {
# daemonize unless running in foreground
unless ($foreground){
daemonize();
}
# prepare data acquisition thread
threads->new(\&get_data);
while (! $time_to_die ) {
# record time started to help evenly space runs
my $start_run = time();
my $next_run = $start_run + $interval;
# de-serialize latest copy of source address structure
# execute this in a isolated scope so that lock goes out of scope
{
my $source_distance;
# lock data structure to prevent other thread from updating it
lock(%ipc_source);
# open coordinates file for graph generation
open(CRDS, ">".PATH."/coords.txt.tmp");
# calculate great circle distance between each source IP and local POP
foreach my $key (keys %ipc_source) {
eval {
my $r = $gi->record_by_addr($key);
# write raw entry to coordinates file
print CRDS $key.",".$ipc_source{$key}.",".$r->latitude.",".$r->longitude."\n";
};
if ($@) {
print CRDS $key.",".$ipc_source{$key}.",0,0\n";
}
}
# close coordinate file
close CRDS;
system("mv ".PATH."/coords.txt.tmp ".PATH."/coords.txt");
# clean out structure for next sample period
%ipc_source = ();
}
# sleep to make the interval
while((my $time_left = ($next_run - time())) > 0) {
sleep($time_left);
}
}
threads->join();
return;
}
# fetch data from UDP multicast
sub get_data() {
# set up our multicast listener
# note: this will receive unicast fine too
my $sock = IO::Socket::Multicast->new(LocalPort=>PORT,ReuseAddr=>1);
$sock->mcast_add(GROUP) || die "Couldn't set group: $!\n";
while ( ! $time_to_die ) {
my $data;
next unless $sock->recv($data,1500);
# decode JSON
eval {
my $obj = decode_json $data;
print Dumper $obj;
foreach my $ip (keys %{$obj->{data}}) {
my $count = $obj->{data}->{$ip};
lock(%ipc_source);
$ipc_source{$ip}+=$count;
}
};
}
# done!
threads->exit();
}
# daemonize application
sub daemonize {
chdir '/' or die "Can't chdir to /: $!";
open STDIN, '/dev/null' or die "Can't read /dev/null: $!";
open STDOUT, '>/dev/null';
# fork and exit parent
my $pid = fork();
exit if $pid;
die "Couldn't fork: $!" unless defined ($pid);
POSIX::setsid() || die ("$0 can't start a new session: $!");
open STDERR, '>&STDOUT' or die "Can't dup stdout: $!";
# signal handlers
$SIG{KILL} = \&handler;
}
sub handler {
$time_to_die = 1;
}