Net-Connection-Sniffer
view release on metacpan or search on metacpan
);
use Proc::PidUtil qw(
if_run_exit
is_running
get_script_name
make_pidfile
zap_pidfile
);
use Net::DNS::Codes qw(
T_PTR
T_SOA
C_IN
BITS_QUERY
RD
NS_PACKETSZ
HFIXEDSZ
QUERY
NOERROR
NXDOMAIN
SERVFAIL
);
use Net::DNS::ToolKit qw(
get16
getIPv4
gethead
newhead
get_ns
);
use Net::DNS::ToolKit::RR;
#use Net::DNS::ToolKit::Debug qw(
# print_head
# print_buf
#);
use Sys::Sig qw(TERM KILL USR1);
bootstrap Net::Connection::Sniffer $VERSION;
# File GLOBAL variables
# define LIBNET_ETH_H 0x0e /* Ethernet header: 14 bytes */
my $ETH_H = 14;
# define LIBNET_IPV4_H 0x14 /* IPv4 header: 20 bytes */
my $IPV4_H = 20;
#define LIBNET_IPV6_H 0x28 /* IPv6 header: 40 bytes */
#my $IPV6_H = 40;
## define LIBNET_TCP_H 0x14 /* TCP header: 20 bytes */
#my $TCP_H = 20;
#my $addr_off = $ETH_H + 12; # src address
#my $frag_off = $ETH_H + 6;
my $minlen = $ETH_H + $IPV4_H + 4; # need port numbers at a minimum
my $snaplen = $minlen;
my $oneday = 86400; # WARNING, set in XS also
my $unique = $$ -1; # dns sequence number
my($match,$nomatch,$payoff);
my $af_inet6 = eval { AF_INET6() };
$af_inet6 = 0 if $@;
#################################################
##### global vars reset by HUP
my ($purge,$nxpurge);
##### global vars used in 'collect', 'dump_stats', elsewhere
#### WARNING, these are set in the XS portion also
my ($now, $start, $rate, $bw);
sub set_gvars {
$now = time;
$start = $now;
$rate = 0; # global
$bw = 0; # global
p2xs_gvars($now, $start, $rate, $bw); # reload xs vars from the module, clear 'hup', 'ra', 'ba'
}
##### global vars used in 'collect' or 'daemon'only
my ($dto);
sub set_globals() {
set_gvars();
$purge = \&setpurge; # starting purge routine
# $ra = 0E0; # intermediate rate accumulator
# $ba = 0E0; # intermediate bw accumulator
$nxpurge = 0;
$dto = $now + 15;
}
set_globals;
#my %subref;
#foreach(sort keys %Net::Connection::Sniffer::) {
# my $subref = \&{"Net::Connection::Sniffer::$_"};
# $Net::Connection::Sniffer::{$_} =~ /[^:]+$/;
# $subref{$subref} = $&;
#}
my %dispatch = (
&LISTEN_MSG => \&do_listen,
&DNS_NEEDED => \&dns_send,
&DUMP_REQUEST => \&dump_stats,
&DNS_RECEIVE => \&dns_rcv,
&PURGE => \&timer,
);
my $dnslookup = [];
my $stats = {
# naddr => {
# B => 5678, # bytes accumulated
# C => 1234, # counts
# E => 124444, # count epoch
# N => ['hostname',], # hostname(s) for this IP
# R => 2345, # rate
# S => 123456, # count start time
# T => 123455, # TTL timeout of PTR record
# W => 7890. # bandWidth
# },
};
# UDP dump port
if ($port = $c->{port}) {
bad_config("invalid port number '$c->{port}'")
unless $port =~ /\d/ && $port !~ /\D/;
if ($iaddr = $c->{host}) {
if ($iaddr eq 'INADDR_LOOPBACK') {
$iaddr = '127.0.0.1';
}
elsif ($iaddr eq 'INADDR_ANY') {
$iaddr = '0.0.0.0';
}
} else {
$c->{host} = $iaddr = '127.0.0.1';
}
bad_config("bad dump host '$c->{host}'")
unless defined ($iaddr = inet_aton($iaddr));
if ($c->{allowed} && @{$c->{allowed}}) {
foreach (0..$#{$c->{allowed}}) {
my $con = inet_aton($c->{allowed}->[$_]);
bad_config("invalid 'allowed' host or IP '". $c->{allowed}->[$_] ."'")
unless $con;
push @allowed, $con;
}
} else {
push @allowed, inet_aton('127.0.0.1');
}
}
}
sub clean_child() {
my $pid = fork;
if ($pid) {
waitpid($pid,0);
exit 0;
}
chdir '/'; # allow root dismount
open STDIN, '/dev/null' or die "Can't dup STDIN to /dev/null: $!";
open STDOUT, '>/dev/null' or die "Can't dup STDOUT to /dev/null: $!";
#open(STDOUT,">&STDERR");
#select STDERR;
#$| = 1;
#select STDOUT;
#$| = 1;
exit 0 if $pid = fork; # double fork to release instantiating terminal
}
#
# input: ipv4 or ipv6 network address
# returns: interface,
# need promiscious [t/f]
sub get_if($) {
my $naddr = ipanyto6(shift);
my %net;
my @ifs = interfaces Net::Interface ();
IF:
foreach my $if (@ifs) {
my @addrs = $if->address(AF_INET());
my @netms = $if->netmask(AF_INET());
if ($af_inet6) {
push @addrs, $if->address($af_inet6);
push @netms, $if->netmask($af_inet6);
}
foreach (0..$#addrs) {
my $iddr = ipanyto6($addrs[$_]);
my $name = $if->name();
if ($naddr eq $iddr) {
$match = $name;
last IF;
}
my $mask = maskanyto6($netms[$_]);
my $net = $iddr & $mask;
my $bcst = $iddr | ~$mask;
$name = $1 if $name =~ /(.+)\:/; # for linux, fix aliases
$net{$name} = $iddr
if sub128($naddr,$net) && sub128($bcst,$naddr); # within CIDR
}
}
@_ = sort keys %net;
my $pmsc = @_;
if (!$match && $pmsc) {
$match = shift @_;
}
return (wantarray)
? ($match,$pmsc)
: $match;
}
# ETHERNET HEADER
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Ethernet destination address (first 32 bits) |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Ethernet dest (last 16 bits) |Ethernet source (first 16 bits)|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Ethernet source address (last 32 bits) |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Type code | |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | IP header, then TCP header, then your data |
# | |
# ...
# | |
# | end of your data |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Ethernet Checksum |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# IPV4 HEADER
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# |Version| IHL |Type of Service| Total Length |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Identification |Flags| Fragment Offset |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Time to Live | Protocol | Header Checksum |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Source Address |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# | Destination Address |
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
# TCP HEADER
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
( run in 0.592 second using v1.01-cache-2.11-cpan-39bf76dae61 )