File: dns_flood_collector.pl

package info (click to toggle)
dns-flood-detector 1.20-6
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, sid
  • size: 216 kB
  • sloc: ansic: 737; perl: 102; sh: 75; makefile: 50
file content (157 lines) | stat: -rwxr-xr-x 3,639 bytes parent folder | download | duplicates (6)
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;
}