Net-Frame-Dump-1.18000755001750001750 013652762052 13565 5ustar00gomorgomor000000000000Net-Frame-Dump-1.18/Build.PL000444001750001750 111713652762052 15216 0ustar00gomorgomor000000000000# # $Id: Build.PL,v bf7e6e9a3b7d 2014/11/30 11:23:39 gomor $ # use strict; use warnings; use Module::Build; my $builder = Module::Build->new( module_name => 'Net::Frame::Dump', license => 'artistic', dist_author => 'GomoR ', dist_version_from => 'lib/Net/Frame/Dump.pm', requires => { 'perl' => '5.6.1', 'Class::Gomor' => '1.00', 'Net::Pcap' => '0.12', 'Net::Frame' => 0, 'Time::HiRes' => 0, 'IO::Select' => 0, }, configure_requires => { 'Module::Build' => 0, }, ); $builder->create_build_script; Net-Frame-Dump-1.18/Changes000444001750001750 716013652762052 15221 0ustar00gomorgomor000000000000Revision history for Perl extension Net::Frame::Dump. 1.18 Fri 1 May 10:43:36 CEST 2020 - new: Dump: filterCodeOptimize setting to enable pcap filter optimizer (requirement for long pcap filters, for instance). Off by default. - update: copyright notice 1.17 Sun 17 Jun 13:10:12 CEST 2018 - bugfix: Dump::Online*: fixed a memleak due to the lack of freeing the pcap filter in stop() method in N:F:Dump::Online and N:F:Dump::Online2. 1.16 Thu 17 May 13:57:30 CEST 2018 - bugfix: RT#125315, typos in pod and code. 1.15 Tue 1 May 09:16:32 CEST 2018 - BUGFIX: Dump::Online2: yes, again. Since some newer version of libpcap and changes in pcap_dispatch(), we have to call can_read() on the selectable file descriptor and call pcap_dispatch() only when there is something to read. Which sounds logical, after all. - update: copyright notice 1.14 Tue Dec 9 19:12:25 CET 2014 - BUGFIX: Dump::Online2: do not eat 100% and do not block for timeoutOnNext seconds when it is not REALLY needed. We had to remove the setnonblock option and to use pcap_dispatch i/o pcap_next_ex. This should be the final bugfix... There was also an infinite loop condition. - new: Dump::Online2: maxRunTime() option to stop looping on next() forever when we do not want. Default to loop forever. 1.13 Sun Dec 2 15:44:14 CET 2012 - BUGFIX: Dump::Online2: in non-blocking mode, we must handle our internal received frame ring buffer. Many packets were missed because of that. 1.12 Tue Nov 13 20:15:19 CET 2012 - UPDATE: Dump::Online2: now uses IO::Select to avoid eating 100% CPU - update: mailing list link 1.11 Sun Nov 11 15:23:04 CET 2012 - bugfix: Dump::Online2 pod - new: Dump::Online2: support for capture only mode - new: examples for Dump::Online2 1.10 Wed Sep 12 20:15:09 CEST 2012 - bugfix: Online: use SUPER::next() instead of next() - update: error messages scheme changed - update: copyright notice 1.09 Mon Mar 28 19:33:24 CEST 2011 - new: added support for ERF data link type - new: added support to append data to a pcap file in Writer mode - bugfix: allows to sniff on an interface without IP address 1.08 Sun Feb 20 17:51:58 CET 2011 - new: Net::Frame::Dump::Online2: non-blocking live capture => If I had seen that sooner ... 1.07 Thu Jan 13 11:17:12 CET 2011 - bugfix: stop() returns if in isSon() - bugfix: stop() when _clean() - update: _killTcpdump(), only KILL now - update: you can send a SIGHUP to print pcap stats - update: SIGINT & SIGTERM call _clean in parent process - update: printStats() only print stats - update: copyright notice 1.06 Wed Jun 17 00:11:39 CEST 2009 - bugfix: isSon() and isFather() from Net::Frame::Dump::Online now truly return isSon() and isFather() ^^ 1.05 Sun May 31 16:43:22 CEST 2009 - new: NF_DUMP_LAYER_80211 link layer support - new: NF_DUMP_LAYER_80211_RADIOTAP link layer support - update: replaced croak()/carp() messages with warn() - update: copyright notice 1.04 Sun Apr 20 12:20:04 CEST 2008 - bugfix: do not return() when no IPv4 address is assigned in live capture - update: mailing list URL, see README 1.03 Sat Apr 19 18:51:49 CEST 2008 - new: added snaplen attribute in Net::Frame::Dump::Online 1.02 - bugfix: license string (should be artistic, not Artistic) 1.01 Sat Feb 16 18:02:37 CET 2008 - bugfix: no pcap_lookupnet() in setFilter() - added NF_DUMP_LAYER_80211RADIO link layer support 1.00 Sun Dec 17 17:53:51 CET 2006 - first public release Net-Frame-Dump-1.18/LICENSE000444001750001750 26513652762052 14712 0ustar00gomorgomor000000000000LICENSE This program is free software. You can redistribute it and/or modify it under the following terms: - the Perl Artistic License (in the file LICENSE.Artistic), Net-Frame-Dump-1.18/LICENSE.Artistic000444001750001750 1373413652762052 16540 0ustar00gomorgomor000000000000 The "Artistic License" Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder as specified below. "Copyright Holder" is whoever is named in the copyright or copyrights for the package. "You" is you, if you're thinking about copying or distributing this Package. "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as uunet.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) give non-standard executables non-standard names, and clearly document the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. You may embed this Package's interpreter within an executable of yours (by linking); this shall be construed as a mere form of aggregation, provided that the complete Standard Version of the interpreter is so embedded. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whoever generated them, and may be sold commercially, and may be aggregated with this Package. If such scripts or library files are aggregated with this Package via the so-called "undump" or "unexec" methods of producing a binary executable image, then distribution of such an image shall neither be construed as a distribution of this Package nor shall it fall under the restrictions of Paragraphs 3 and 4, provided that you do not represent such an executable image as a Standard Version of this Package. 7. C subroutines (or comparably compiled subroutines in other languages) supplied by you and linked into this Package in order to emulate subroutines and variables of the language defined by this Package shall not be considered part of this Package, but are the equivalent of input as in Paragraph 6, provided these subroutines do not change the language in any way that would cause it to fail the regression tests for the language. 8. Aggregation of this Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package. 9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End Net-Frame-Dump-1.18/MANIFEST000444001750001750 75413652762052 15041 0ustar00gomorgomor000000000000Build.PL Changes LICENSE LICENSE.Artistic MANIFEST This list of files Makefile.PL README examples/dump-offline.pl examples/dump-online-1-frame.pl examples/dump-online-onrecv.pl examples/dump-online.pl examples/dump-online2.pl examples/dump-online2-saveonly.pl examples/dump-writer.pl lib/Net/Frame/Dump.pm lib/Net/Frame/Dump/Offline.pm lib/Net/Frame/Dump/Online.pm lib/Net/Frame/Dump/Online2.pm lib/Net/Frame/Dump/Writer.pm t/01-use.t t/02-pod-coverage.t t/03-test-pod.t META.yml META.json Net-Frame-Dump-1.18/META.json000444001750001750 276213652762052 15352 0ustar00gomorgomor000000000000{ "abstract" : "base-class for a tcpdump like implementation", "author" : [ "GomoR " ], "dynamic_config" : 1, "generated_by" : "Module::Build version 0.4224", "license" : [ "artistic_1" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Net-Frame-Dump", "prereqs" : { "configure" : { "requires" : { "Module::Build" : "0" } }, "runtime" : { "requires" : { "Class::Gomor" : "1.00", "IO::Select" : "0", "Net::Frame" : "0", "Net::Pcap" : "0.12", "Time::HiRes" : "0", "perl" : "v5.6.1" } } }, "provides" : { "Net::Frame::Dump" : { "file" : "lib/Net/Frame/Dump.pm", "version" : "1.18" }, "Net::Frame::Dump::Offline" : { "file" : "lib/Net/Frame/Dump/Offline.pm" }, "Net::Frame::Dump::Online" : { "file" : "lib/Net/Frame/Dump/Online.pm" }, "Net::Frame::Dump::Online2" : { "file" : "lib/Net/Frame/Dump/Online2.pm" }, "Net::Frame::Dump::Writer" : { "file" : "lib/Net/Frame/Dump/Writer.pm" } }, "release_status" : "stable", "resources" : { "license" : [ "http://www.perlfoundation.org/artistic_license_1_0" ] }, "version" : "1.18", "x_serialization_backend" : "JSON::PP version 2.27400_02" } Net-Frame-Dump-1.18/META.yml000444001750001750 200013652762052 15163 0ustar00gomorgomor000000000000--- abstract: 'base-class for a tcpdump like implementation' author: - 'GomoR ' build_requires: {} configure_requires: Module::Build: '0' dynamic_config: 1 generated_by: 'Module::Build version 0.4224, CPAN::Meta::Converter version 2.150010' license: artistic meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Net-Frame-Dump provides: Net::Frame::Dump: file: lib/Net/Frame/Dump.pm version: '1.18' Net::Frame::Dump::Offline: file: lib/Net/Frame/Dump/Offline.pm Net::Frame::Dump::Online: file: lib/Net/Frame/Dump/Online.pm Net::Frame::Dump::Online2: file: lib/Net/Frame/Dump/Online2.pm Net::Frame::Dump::Writer: file: lib/Net/Frame/Dump/Writer.pm requires: Class::Gomor: '1.00' IO::Select: '0' Net::Frame: '0' Net::Pcap: '0.12' Time::HiRes: '0' perl: v5.6.1 resources: license: http://www.perlfoundation.org/artistic_license_1_0 version: '1.18' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' Net-Frame-Dump-1.18/Makefile.PL000444001750001750 103313652762052 15671 0ustar00gomorgomor000000000000# # $Id: Makefile.PL,v 62e64fd640ce 2012/11/13 19:16:33 gomor $ # use ExtUtils::MakeMaker; require v5.6.1; WriteMakefile( NAME => 'Net::Frame::Dump', VERSION_FROM => 'lib/Net/Frame/Dump.pm', LICENSE => 'artistic', ABSTRACT_FROM => 'lib/Net/Frame/Dump.pm', AUTHOR => 'GomoR ', PREREQ_PM => { Class::Gomor => '1.00', Net::Pcap => '0.12', Net::Frame => 0, Time::HiRes => 0, IO::Select => 0, }, MIN_PERL_VERSION => '5.6.1', ); Net-Frame-Dump-1.18/README000444001750001750 122313652762052 14600 0ustar00gomorgomor000000000000Net::Frame::Dump ================ INSTALLATION To install this module type the following: perl Makefile.PL make make test make install DEPENDENCIES This module requires these other modules and libraries: Perl v5.6.1 Class::Gomor Net::Frame Net::Pcap Time::HiRes IO::Select GETTING HELP A mailing list is available for all questions concerning Net::Frame::*: https://www.secure-side.com/lists/mailman/listinfo/netframe COPYRIGHT AND LICENSE You may distribute this module under the terms of the Artistic license. See LICENSE.Artistic file in the source distribution archive. Copyright (c) 2006-2018, Patrice Auffret Net-Frame-Dump-1.18/examples000755001750001750 013652762052 15403 5ustar00gomorgomor000000000000Net-Frame-Dump-1.18/examples/dump-offline.pl000555001750001750 111613652762052 20464 0ustar00gomorgomor000000000000#!/usr/bin/perl use strict; use warnings; my $oDump; my $file = shift || die("Specify file\n"); use Net::Frame::Dump::Offline; use Net::Frame::Simple; $oDump = Net::Frame::Dump::Offline->new(file => $file); $oDump->start; my $count = 0; while (my $h = $oDump->next) { my $f = Net::Frame::Simple->new( raw => $h->{raw}, firstLayer => $h->{firstLayer}, timestamp => $h->{timestamp}, ); my $len = length($h->{raw}); print 'o Frame number: '.$count++." (length: $len)\n"; print $f->print."\n"; } END { $oDump && $oDump->isRunning && $oDump->stop } Net-Frame-Dump-1.18/examples/dump-online-1-frame.pl000555001750001750 127313652762052 21560 0ustar00gomorgomor000000000000#!/usr/bin/perl use strict; use warnings; my $oDump; my $dev = shift || die("Specify network interface to use\n"); use Net::Frame::Dump::Online; use Net::Frame::Simple; use Class::Gomor qw($Debug); $Debug = 3; $oDump = Net::Frame::Dump::Online->new(dev => $dev); $oDump->start; my $count = 0; while (1) { if (my $h = $oDump->next) { my $f = Net::Frame::Simple->new( raw => $h->{raw}, firstLayer => $h->{firstLayer}, timestamp => $h->{timestamp}, ); my $len = length($h->{raw}); print 'o Frame number: '.$count++." (length: $len)\n"; print $f->print."\n"; last; } } END { $oDump && $oDump->isRunning && $oDump->stop } Net-Frame-Dump-1.18/examples/dump-online-onrecv.pl000555001750001750 103613652762052 21621 0ustar00gomorgomor000000000000#!/usr/bin/perl use strict; use warnings; my $oDump; my $dev = shift || die("Specify network interface to use\n"); use Net::Frame::Dump::Online; use Net::Frame::Simple; sub callOnRecv { my ($h, $data) = @_; print "Data: $data\n"; my $oSimple = Net::Frame::Simple->newFromDump($h); print $oSimple->print."\n"; } $oDump = Net::Frame::Dump::Online->new( dev => $dev, onRecv => \&callOnRecv, onRecvCount => 1, onRecvData => 'test', ); $oDump->start; END { $oDump && $oDump->isRunning && $oDump->stop } Net-Frame-Dump-1.18/examples/dump-online.pl000555001750001750 117313652762052 20331 0ustar00gomorgomor000000000000#!/usr/bin/perl use strict; use warnings; my $oDump; my $dev = shift || die("Specify network interface to use\n"); use Net::Frame::Dump::Online; use Net::Frame::Simple; use Class::Gomor qw($Debug); $Debug = 3; $oDump = Net::Frame::Dump::Online->new(dev => $dev); $oDump->start; my $count = 0; while (1) { if (my $h = $oDump->next) { my $f = Net::Frame::Simple->new( raw => $h->{raw}, firstLayer => $h->{firstLayer}, timestamp => $h->{timestamp}, ); my $len = length($h->{raw}); print 'o Frame number: '.$count++." (length: $len)\n"; print $f->print."\n"; } } Net-Frame-Dump-1.18/examples/dump-online2-saveonly.pl000555001750001750 57213652762052 22233 0ustar00gomorgomor000000000000#!/usr/bin/perl use strict; use warnings; my $oDump; my $dev = shift || die("Specify network interface to use\n"); use Net::Frame::Dump::Online2; use Net::Frame::Simple; use Class::Gomor qw($Debug); $Debug = 3; $oDump = Net::Frame::Dump::Online2->new( dev => $dev, file => 'save-example-online2.pcap', overwrite => 1, ); $oDump->start; <>; $oDump->stop; Net-Frame-Dump-1.18/examples/dump-online2.pl000555001750001750 136013652762052 20411 0ustar00gomorgomor000000000000#!/usr/bin/perl use strict; use warnings; my $dev = shift || die("Specify network interface to use\n"); my $filter = shift; my $oDump; use Net::Frame::Dump::Online2; use Net::Frame::Simple; use Class::Gomor qw($Debug); $Debug = 3; $oDump = Net::Frame::Dump::Online2->new( dev => $dev, filter => $filter ? $filter : '', ); $oDump->start; while (1) { if (my $f = $oDump->next) { my $raw = $f->{raw}; my $firstLayerType = $f->{firstLayer}; my $timestamp = $f->{timestamp}; print "Received at: $timestamp\n"; my $frame = Net::Frame::Simple->newFromDump($f); print $frame->print."\n"; } if ($oDump->timeout) { print "Timeout occured, end of capture\n"; last; } } Net-Frame-Dump-1.18/examples/dump-writer.pl000555001750001750 101113652762052 20350 0ustar00gomorgomor000000000000#!/usr/bin/perl use strict; use warnings; use Net::Frame::Layer::IPv4; use Net::Frame::Dump::Writer; use Net::Frame::Simple; my $oDump = Net::Frame::Dump::Writer->new( file => 'new-file.pcap', overwrite => 1, ); $oDump->start; for (0..255) { my $ip = Net::Frame::Layer::IPv4->new( length => 1480, protocol => $_, ); $ip->pack; my $raw = pack('H*', 'f'x1000); $oDump->write({ timestamp => '10.10', raw => $ip->raw.$raw }); } END { $oDump && $oDump->isRunning && $oDump->stop } Net-Frame-Dump-1.18/lib000755001750001750 013652762052 14333 5ustar00gomorgomor000000000000Net-Frame-Dump-1.18/lib/Net000755001750001750 013652762052 15061 5ustar00gomorgomor000000000000Net-Frame-Dump-1.18/lib/Net/Frame000755001750001750 013652762052 16113 5ustar00gomorgomor000000000000Net-Frame-Dump-1.18/lib/Net/Frame/Dump.pm000444001750001750 1263613652762052 17543 0ustar00gomorgomor000000000000# # $Id: Dump.pm,v a93481b098ec 2020/05/01 08:55:56 gomor $ # package Net::Frame::Dump; use strict; use warnings; our $VERSION = '1.18'; use base qw(Class::Gomor::Array Exporter); our %EXPORT_TAGS = ( consts => [qw( NF_DUMP_LAYER_NULL NF_DUMP_LAYER_ETH NF_DUMP_LAYER_RAW NF_DUMP_LAYER_SLL NF_DUMP_LAYER_PPP NF_DUMP_LAYER_80211_RADIOTAP NF_DUMP_LAYER_80211 NF_DUMP_LAYER_ERF )], ); our @EXPORT_OK = ( @{$EXPORT_TAGS{consts}}, ); use constant NF_DUMP_LAYER_NULL => 0; use constant NF_DUMP_LAYER_ETH => 1; use constant NF_DUMP_LAYER_PPP => 9; use constant NF_DUMP_LAYER_RAW => 12; use constant NF_DUMP_LAYER_80211 => 105; use constant NF_DUMP_LAYER_SLL => 113; use constant NF_DUMP_LAYER_80211_RADIOTAP => 127; use constant NF_DUMP_LAYER_ERF => 175; our @AS = qw( file filter filterCodeOptimizer overwrite firstLayer isRunning keepTimestamp _framesStored _pcapd _dumper ); our @AA = qw( frames ); __PACKAGE__->cgBuildIndices; __PACKAGE__->cgBuildAccessorsScalar(\@AS); __PACKAGE__->cgBuildAccessorsArray(\@AA); use Net::Pcap; use Time::HiRes qw(gettimeofday); use Net::Frame::Layer qw(:consts :subs); sub new { my $self = shift->SUPER::new( filter => '', overwrite => 0, isRunning => 0, keepTimestamp => 0, filterCodeOptimizer => 0, frames => [], _framesStored => {}, @_, ); if (!defined($self->file)) { my $int = getRandom32bitsInt(); $self->file("netframe-tmp-$$.$int.pcap"); } return $self; } sub flush { my $self = shift; $self->frames([]); $self->_framesStored({}); } sub _addStore { my $self = shift; my ($oSimple) = @_; # If parameter, we store it if ($oSimple) { my $key = $oSimple->getKey; push @{$self->_framesStored->{$key}}, $oSimple; # If it is ICMPv4, we store a second time if (exists $oSimple->ref->{ICMPv4}) { push @{$self->_framesStored->{$oSimple->ref->{ICMPv4}->getKey}}, $oSimple; } } # We return the hash ref return $self->_framesStored; } sub store { my $self = shift; my ($oSimple) = @_; $self->_addStore($oSimple); my @frames = $self->frames; push @frames, $oSimple; return $self->frames(\@frames); } sub _getTimestamp { my $self = shift; my ($hdr) = @_; $hdr->{tv_sec}.'.'.sprintf("%06d", $hdr->{tv_usec}); } sub _setTimestamp { my $self = shift; my @time = Time::HiRes::gettimeofday(); $time[0].'.'.sprintf("%06d", $time[1]); } my $mapLinks = { NF_DUMP_LAYER_NULL() => 'NULL', NF_DUMP_LAYER_ETH() => 'ETH', NF_DUMP_LAYER_RAW() => 'RAW', NF_DUMP_LAYER_SLL() => 'SLL', NF_DUMP_LAYER_PPP() => 'PPP', NF_DUMP_LAYER_80211() => '80211', NF_DUMP_LAYER_80211_RADIOTAP() => '80211::Radiotap', NF_DUMP_LAYER_ERF() => 'ERF', }; sub getFirstLayer { my $self = shift; my $link = Net::Pcap::datalink($self->_pcapd); $self->firstLayer($mapLinks->{$link} || NF_LAYER_UNKNOWN); } sub next { my $self = shift; my %hdr; if (my $raw = Net::Pcap::next($self->_pcapd, \%hdr)) { my $ts = $self->keepTimestamp ? $self->_getTimestamp(\%hdr) : $self->_setTimestamp; return { firstLayer => $self->firstLayer, timestamp => $ts, raw => $raw, }; } return; } sub nextEx { my $self = shift; my %hdr; my $raw; my $r = Net::Pcap::next_ex($self->_pcapd, \%hdr, \$raw); if ($r > 0) { my $ts = $self->keepTimestamp ? $self->_getTimestamp(\%hdr) : $self->_setTimestamp; return { firstLayer => $self->firstLayer, timestamp => $ts, raw => $raw, }; } return $r; } sub getFramesFor { my $self = shift; my ($oSimple) = @_; my $results; my $key = $oSimple->getKeyReverse; push @$results, @{$self->_framesStored->{$key}} if exists $self->_framesStored->{$key}; # Add also ICMPv4 if (exists $self->_framesStored->{ICMPv4}) { push @$results, @{$self->_framesStored->{ICMPv4}}; } return $results ? @$results : (); } 1; __END__ =head1 NAME Net::Frame::Dump - base-class for a tcpdump like implementation =head1 DESCRIPTION B is the base class for all dump modules. With them, you can open a device for live capture, for offline analysis, or for creating a pcap file. See B, B, B for specific usage. =head1 METHODS =over 4 =item B (%h) Base-class object constructor. =back =head1 CONSTANTS Load them: use Net::Frame::Dump qw(:consts); =over 4 =item B =item B =item B =item B =item B =item B =item B Various supported link layers. =back =head1 SEE ALSO L, L, L =head1 AUTHOR Patrice EGomoRE Auffret =head1 COPYRIGHT AND LICENSE Copyright (c) 2006-2020, Patrice EGomoRE Auffret You may distribute this module under the terms of the Artistic license. See LICENSE.Artistic file in the source distribution archive. =cut Net-Frame-Dump-1.18/lib/Net/Frame/Dump000755001750001750 013652762052 17020 5ustar00gomorgomor000000000000Net-Frame-Dump-1.18/lib/Net/Frame/Dump/Offline.pm000444001750001750 1120013652762052 21107 0ustar00gomorgomor000000000000# # $Id: Offline.pm,v a93481b098ec 2020/05/01 08:55:56 gomor $ # package Net::Frame::Dump::Offline; use strict; use warnings; use base qw(Net::Frame::Dump); __PACKAGE__->cgBuildIndices; use Net::Frame::Dump qw(:consts); use Net::Pcap; use Time::HiRes qw(gettimeofday); sub _openFile { my $self = shift; my $err; my $pcapd = Net::Pcap::open_offline($self->file, \$err); if (!defined($pcapd)) { print("[-] ".__PACKAGE__.": Net::Pcap::open_offline: ". "@{[$self->file]}: $err\n"); return; } $self->_pcapd($pcapd); return $self->getFirstLayer; } sub _setFilter { my $self = shift; my $str = $self->filter; if (!defined($str)) { return; } my $filter; Net::Pcap::compile($self->_pcapd, \$filter, $str, $self->filterCodeOptimizer, 0); if (!defined($filter)) { print("[-] ".__PACKAGE__.": Net::Pcap::compile: error\n"); return; } Net::Pcap::setfilter($self->_pcapd, $filter); } sub start { my $self = shift; $self->isRunning(1); if (! -f $self->file) { print("[-] ".__PACKAGE__.": File does not exists: ".$self->file."\n"); return; } $self->_openFile; $self->_setFilter; return 1; } sub stop { my $self = shift; if (!$self->isRunning) { return; } Net::Pcap::close($self->_pcapd); $self->_pcapd(undef); $self->isRunning(0); return 1; } 1; __END__ =head1 NAME Net::Frame::Dump::Offline - tcpdump like implementation, offline mode =head1 SYNOPSIS use Net::Frame::Dump::Offline; # # Simple offline anaysis # my $oDump = Net::Frame::Dump::Offline->new(file => $file); $oDump->start; my $count = 0; while (my $h = $oDump->next) { my $f = Net::Frame::Simple->new( raw => $h->{raw}, firstLayer => $h->{firstLayer}, timestamp => $h->{timestamp}, ); my $len = length($h->{raw}); print 'o Frame number: '.$count++." (length: $len)\n"; print $f->print."\n"; } $oDump->stop; # # Default parameters on creation # my $oDumpDefault = Net::Frame::Dump::Offline->new( file => "netframe-tmp-$$.$int.pcap", filter => '', isRunning => 0, keepTimestamp => 0, ); =head1 DESCRIPTION This module implements a tcpdump-like program, for offline analysis. =head1 ATTRIBUTES The following are inherited attributes: =over 4 =item B Name of the .pcap file to read. =item B Pcap filter to use. Default to no filter. =item B Stores information about the first layer type contained on read frame. This attribute is filled only after a call to B method. =item B Returns true if a call to start has been done, false otherwise or if a call to stop has been done. =item B Sometimes, when frames are captured and saved to a .pcap file, timestamps sucks. That is, you send a frame, and receive the reply, but your request appear to have been sent after the reply. So, to correct that, you can use B own timestamping system. The default is 0. Set it manually to 1 if you need original .pcap frames timestamps. =back =head1 METHODS =over 4 =item B =item B (hash) Object constructor. You can pass attributes that will overwrite default ones. See B for default values. =item B When you want to start reading frames from the file, call this method. =item B When you want to stop reading frames from the file, call this method. =item B Returns the next captured frame; undef if no more frames are awaiting. =item B (B object) This method will store internally, sorted, the B object passed as a single parameter. B methods, implemented in various B objects will be used to efficiently retrieve (via B method) frames. Basically, it is used to make B method (from B) to retrieve quickly the reply frame for a request frame. =item B This will return an array of possible reply frames for the specified B object. For example, reply frames for a UDP probe will be all the frames which have the same source port and destination port as the request. =item B Will flush stored frames, the one which have been stored via B method. =back =head1 SEE ALSO L =head1 AUTHOR Patrice EGomoRE Auffret =head1 COPYRIGHT AND LICENSE Copyright (c) 2006-2020, Patrice EGomoRE Auffret You may distribute this module under the terms of the Artistic license. See LICENSE.Artistic file in the source distribution archive. =cut Net-Frame-Dump-1.18/lib/Net/Frame/Dump/Online.pm000444001750001750 3614213652762052 20765 0ustar00gomorgomor000000000000# # $Id: Online.pm,v a93481b098ec 2020/05/01 08:55:56 gomor $ # package Net::Frame::Dump::Online; use strict; use warnings; use base qw(Net::Frame::Dump); our @AS = qw( dev timeoutOnNext timeout promisc snaplen unlinkOnStop onRecv onRecvCount onRecvData _pid _sName _sDataAwaiting _firstTime _son _fcode ); __PACKAGE__->cgBuildIndices; __PACKAGE__->cgBuildAccessorsScalar(\@AS); BEGIN { my $osname = { cygwin => [ \&_checkWin32, ], MSWin32 => [ \&_checkWin32, ], }; *_check = $osname->{$^O}->[0] || \&_checkOther; } use Net::Frame::Dump qw(:consts); use Net::Pcap; use Time::HiRes qw(gettimeofday); use Storable qw(lock_store lock_retrieve); use Net::Frame::Layer qw(:subs); sub _checkWin32 { return 1; } sub _checkOther { if ($>) { die("[-]: Net::Frame::Dump::Online: Must be EUID 0 (or equivalent) ". "to open a device for live capture\n"); } return 1; } sub new { my $self = shift->SUPER::new( timeoutOnNext => 3, timeout => 0, promisc => 0, snaplen => 1514, unlinkOnStop => 1, onRecvCount => -1, _sDataAwaiting => 0, @_, ); if (!defined($self->_sName)) { my $int = getRandom32bitsInt(); $self->_sName("netframe-tmp-$$.$int.storable"); } $SIG{INT} = sub { return unless $self->isFather; $self->_clean; }; $SIG{TERM} = sub { return unless $self->isFather; $self->_clean; }; if (!defined($self->dev)) { print("[-] ".__PACKAGE__.": You MUST pass `dev' attribute\n"); return; } return $self; } sub _sStore { lock_store(\$_[1], $_[0]->_sName) or do { print("[-] ".__PACKAGE__.": lock_store: @{[$_[0]->_sName]}: $!\n"); return; }; return 1; } sub _sRetrieve { ${lock_retrieve(shift->_sName)}; } sub _sWaitFile { my $self = shift; my $startTime = gettimeofday(); my $thisTime = $startTime; while (! -f $self->_sName) { if ($thisTime - $startTime > 10) { print("[-] ".__PACKAGE__.": too long for file creation: ". $self->_sName."\n"); return; } $thisTime = gettimeofday(); } return 1; } sub _sWaitFileSize { my $self = shift; $self->_sWaitFile; my $startTime = gettimeofday(); my $thisTime = $startTime; while (! ((stat($self->_sName))[7] > 0)) { if ($thisTime - $startTime > 10) { $self->_clean; print("[-] ".__PACKAGE__.": too long for file creation2: ". $self->_sName."\n"); return; } $thisTime = gettimeofday(); } return 1; } sub _startOnRecv { my $self = shift; my $err; my $pd = Net::Pcap::open_live( $self->dev, $self->snaplen, $self->promisc, 1000, \$err, ); unless ($pd) { print("[-] ".__PACKAGE__.": open_live: $err\n"); return; } $self->_pcapd($pd); my $net = 0; my $mask = 0; Net::Pcap::lookupnet($self->dev, \$net, \$mask, \$err); if ($err) { print("[!] ".__PACKAGE__.": lookupnet: $err\n"); } my $fcode; if (Net::Pcap::compile($pd, \$fcode, $self->filter, $self->filterCodeOptimizer, $mask) < 0) { print("[-] ".__PACKAGE__.": compile: ". Net::Pcap::geterr($pd). "\n"); return; } $self->_fcode($fcode); # Used to free it later on, usually on stop call. if (Net::Pcap::setfilter($pd, $fcode) < 0) { print("[-] ".__PACKAGE__.": setfilter: ". Net::Pcap::geterr($pd). "\n"); return; } $self->getFirstLayer; # Setup onRecv enforced code, to make it simpler for user my $callback = sub { my ($userData, $hdr, $pkt) = @_; my $h = { raw => $pkt, timestamp => $hdr->{tv_sec}.'.'.sprintf("%06d", $hdr->{tv_usec}), firstLayer => $self->firstLayer, }; &{$self->onRecv}($h, $userData); }; { # We have to access onRecvData ARRAY indice to pass a ref no strict 'vars'; Net::Pcap::loop( $pd, $self->onRecvCount, $callback, $self->[$__onRecvData], ); } } sub start { my $self = shift; _check() or return; $self->isRunning(1); if (-f $self->file && !$self->overwrite) { print("[-] ".__PACKAGE__."We will not overwrite a file by default. ". "Use `overwrite' attribute to do it.\n"); return; } if ($self->onRecv) { $self->_startOnRecv; } else { $self->_sStore(0); $self->_sWaitFileSize; $self->_startTcpdump; $self->_openFile; } return 1; } sub _clean { my $self = shift; if ($self->unlinkOnStop && $self->file && -f $self->file) { unlink($self->file); $self->cgDebugPrint(1, "@{[$self->file]} removed"); } if ($self->_sName && -f $self->_sName) { unlink($self->_sName); } return 1; } sub stop { my $self = shift; if ($self->onRecv && $self->_pcapd) { Net::Pcap::breakloop($self->_pcapd); Net::Pcap::close($self->_pcapd); } else { $self->_killTcpdump; Net::Pcap::breakloop($self->_pcapd); Net::Pcap::close($self->_pcapd); } # Free the pcap filter if (defined($self->_fcode)) { Net::Pcap::pcap_freecode($self->_fcode); $self->_fcode(undef); } $self->isRunning(0); $self->_pcapd(undef); $self->_clean; return 1; } sub getStats { my $self = shift; if (!defined($self->_pcapd)) { print("[-] ".__PACKAGE__.": unable to get stats, no pcap descriptor ". "opened.\n"); return; } my %stats; Net::Pcap::stats($self->_pcapd, \%stats); return \%stats; } sub isFather { ! shift->_son } sub isSon { shift->_son } sub _sonPrintStats { my $self = shift; my $stats = $self->getStats; $self->cgDebugPrint(1, 'Frames received : '.$stats->{ps_recv}); $self->cgDebugPrint(1, 'Frames dropped : '.$stats->{ps_drop}); $self->cgDebugPrint(1, 'Frames if dropped: '.$stats->{ps_ifdrop}); return; } sub _startTcpdump { my $self = shift; my $err; my $pd = Net::Pcap::open_live( $self->dev, $self->snaplen, $self->promisc, 1000, \$err, ); unless ($pd) { print("[-] ".__PACKAGE__.": open_live: $err\n"); return; } my $net = 0; my $mask = 0; Net::Pcap::lookupnet($self->dev, \$net, \$mask, \$err); if ($err) { print("[!] ".__PACKAGE__.": lookupnet: $err\n"); } my $fcode; if (Net::Pcap::compile($pd, \$fcode, $self->filter, $self->filterCodeOptimizer, $mask) < 0) { print("[-] ".__PACKAGE__.": compile: ". Net::Pcap::geterr($pd). "\n"); return; } $self->_fcode($fcode); # Used to free it later on, usually on stop call. if (Net::Pcap::setfilter($pd, $fcode) < 0) { print("[-] ".__PACKAGE__.": setfilter: ". Net::Pcap::geterr($pd). "\n"); return; } my $p = Net::Pcap::dump_open($pd, $self->file); unless ($p) { print("[-] ".__PACKAGE__.": dump_open: ". Net::Pcap::geterr($pd). "\n"); return; } Net::Pcap::dump_flush($p); my $pid = fork(); die("[-] ".__PACKAGE__.": fork: $!\n") unless defined $pid; if ($pid) { # Parent $self->_son(0); $self->_pid($pid); $SIG{CHLD} = 'IGNORE'; return 1; } else { # Son $self->_son(1); $self->_pcapd($pd); $SIG{HUP} = sub { $self->_sonPrintStats }; $self->cgDebugPrint(1, "dev: [@{[$self->dev]}]\n". "file: [@{[$self->file]}]\n". "filter: [@{[$self->filter]}]"); Net::Pcap::loop($pd, -1, \&_tcpdumpCallback, [ $p, $self ]); Net::Pcap::close($pd); exit(0); } } sub _tcpdumpCallback { my ($data, $hdr, $pkt) = @_; my $p = $data->[0]; my $self = $data->[1]; Net::Pcap::dump($p, $hdr, $pkt); Net::Pcap::dump_flush($p); my $n = $self->_sRetrieve; $self->_sStore(++$n); } sub _killTcpdump { my $self = shift; return if $self->isSon; kill('KILL', $self->_pid); $self->_pid(undef); } sub _openFile { my $self = shift; my $err; my $pcapd = Net::Pcap::open_offline($self->file, \$err); if (!defined($pcapd)) { print("[-] ".__PACKAGE__.": Net::Pcap::open_offline: ". "@{[$self->file]}: $err\n"); return; } $self->_pcapd($pcapd); return $self->getFirstLayer; } sub _getNextAwaitingFrame { my $self = shift; my $last = $self->_sDataAwaiting; my $new = $self->_sRetrieve; # Return if nothing new is awaiting return if ($new <= $last); $self->_sDataAwaiting($self->_sDataAwaiting + 1); return $self->SUPER::next; } sub _nextTimeoutHandle { my $self = shift; # Handle timeout my $thisTime = gettimeofday(); if ($self->timeoutOnNext && !$self->_firstTime) { $self->_firstTime($thisTime); } if ($self->timeoutOnNext && $self->_firstTime) { if (($thisTime - $self->_firstTime) > $self->timeoutOnNext) { $self->timeout(1); $self->_firstTime(0); $self->cgDebugPrint(1, "Timeout occurred"); return; } } return 1; } sub _nextTimeoutReset { shift->_firstTime(0) } sub timeoutReset { shift->timeout(0) } sub next { my $self = shift; $self->_nextTimeoutHandle or return; my $frame = $self->_getNextAwaitingFrame; $self->_nextTimeoutReset if $frame; return $frame; } 1; __END__ =head1 NAME Net::Frame::Dump::Online - tcpdump like implementation, online mode =head1 SYNOPSIS use Net::Frame::Dump::Online; # # Simply create a Dump object # my $oDump = Net::Frame::Dump::Online->new( dev => 'eth0', ); $oDump->start; # Gather frames while (1) { if (my $f = $oDump->next) { my $raw = $f->{raw}; my $firstLayerType = $f->{firstLayer}; my $timestamp = $f->{timestamp}; } } $oDump->stop; # # Create a Dump object, using on-event loop # sub callOnRecv { my ($h, $data) = @_; print "Data: $data\n"; my $oSimple = Net::Frame::Simple->newFromDump($h); print $oSimple->print."\n"; } my $oDumpEvent = Net::Frame::Dump::Online->new( dev => 'eth0', onRecv => \&callOnRecv, onRecvCount => 1, onRecvData => 'test', ); # Will block here, until $onRecvCount packets read, or a stop() call has # been performed. $oDumpEvent->start; # # Default parameters on creation # my $oDumpDefault = Net::Frame::Dump::Online->new( dev => undef, timeoutOnNext => 3, timeout => 0, promisc => 0, unlinkOnStop => 1, file => "netframe-tmp-$$.$int.pcap", filter => '', overwrite => 0, isRunning => 0, keepTimestamp => 0, onRecvCount => -1, frames => [], ); =head1 DESCRIPTION This module implements a tcpdump-like program, for live capture from networks. =head1 ATTRIBUTES =over 4 =item B The network interface to listen on. No default value. =item B Each time you call B method, an internal counter is updated. This counter tells you if you have not received any data since B seconds. When a timeout occurred, B is set to true. =item B When B seconds has been reached, this variable is set to true, and never reset. See B if you want to reset it. =item B If you want to capture a different snaplen, set it a number. Default to 1514. =item B By default, interface is not put into promiscuous mode, set this parameter to true if you want it. =item B When you call B method, the generated .pcap file is removed, unless you set this parameter to a false value. =item B If you place a reference to a sub in this attribute, it will be called each time a packet is received on the interface. See B for an example usage. =item B This parameter will store additional data to be passed to B callback. =item B By default, it is set to read forever packets that reach your network interface. Set it to a positive value to read only B frames. =back The following are inherited attributes: =over 4 =item B Name of the generated .pcap file. See B for the default name. =item B Pcap filter to use. Default to no filter. =item B Overwrites a .pcap file that already exists. Default to not. =item B Stores information about the first layer type contained on read frame. This attribute is filled only after a call to B method. =item B Returns true if a call to B has been done, false otherwise or if a call to B has been done. =item B Sometimes, when frames are captured and saved to a .pcap file, timestamps sucks. That is, you send a frame, and receive the reply, but your request appear to have been sent after the reply. So, to correct that, you can use B own timestamping system. The default is 0. Set it manually to 1 if you need original .pcap frames timestamps. =back =head1 METHODS =over 4 =item B =item B (hash) Object constructor. You can pass attributes that will overwrite default ones. See B for default values. =item B When you want to start reading frames from network, call this method. =item B When you want to stop reading frames from network, call this method. =item B Returns the next captured frame; undef if none awaiting. Each time this method is called, a comparison is done to see if no frame has been captured during B number of seconds. If so, B attribute is set to 1 to reflect the pending timeout. =item B (B object) This method will store internally, sorted, the B object passed as a single parameter. B methods, implemented in various B objects will be used to efficiently retrieve (via B method) frames. Basically, it is used to make B method (from B) to retrieve quickly the reply frame for a request frame. =item B (B object) This will return an array of possible reply frames for the specified B object. For example, reply frames for a UDP probe will be all the frames which have the same source port and destination port as the request. =item B Will flush stored frames, the one which have been stored via B method. =item B Reset the internal timeout state (B attribute). =item B Tries to get packet statistics on an open descriptor. It returns a reference to a hash that has to following fields: B, B, B. =item B =item B These methods will tell you if your current process is respectively the father, or son process of B object. =back =head1 SEE ALSO L =head1 AUTHOR Patrice EGomoRE Auffret =head1 COPYRIGHT AND LICENSE Copyright (c) 2006-2020, Patrice EGomoRE Auffret You may distribute this module under the terms of the Artistic license. See LICENSE.Artistic file in the source distribution archive. =cut Net-Frame-Dump-1.18/lib/Net/Frame/Dump/Online2.pm000444001750001750 2775013652762052 21054 0ustar00gomorgomor000000000000# # $Id: Online2.pm,v a93481b098ec 2020/05/01 08:55:56 gomor $ # package Net::Frame::Dump::Online2; use strict; use warnings; use base qw(Net::Frame::Dump); our @AS = qw( dev timeoutOnNext timeout maxRunTime promisc snaplen file overwrite _startTime _pid _sel _frames _timeWithoutReceiving _fcode ); __PACKAGE__->cgBuildIndices; __PACKAGE__->cgBuildAccessorsScalar(\@AS); BEGIN { my $osname = { cygwin => [ \&_checkWin32, ], MSWin32 => [ \&_checkWin32, ], }; *_check = $osname->{$^O}->[0] || \&_checkOther; } use Net::Frame::Dump qw(:consts); use IO::Select; use Net::Pcap; use Time::HiRes qw(gettimeofday); use Net::Frame::Layer qw(:subs); sub _checkWin32 { return 1; } sub _checkOther { if ($>) { die("[-] Net::Frame::Dump::Online2: Must be EUID 0 (or equivalent) to ". "open a device for live capture\n"); } return 1; } sub new { my $self = shift->SUPER::new( timeoutOnNext => 3, timeout => 0, promisc => 0, snaplen => 1514, file => '', overwrite => 0, maxRunTime => 0, _startTime => 0, _frames => [], _timeWithoutReceiving => 0, @_, ); if (!defined($self->dev)) { print("[-] ".__PACKAGE__.": You MUST pass `dev' attribute\n"); return; } return $self; } sub start { my $self = shift; _check() or return; $self->isRunning(1); if (length($self->file)) { if (-f $self->file && ! $self->overwrite) { print("[-] ".__PACKAGE__.": We will not overwrite a file by default. ". "Use `overwrite' attribute to do it.\n"); return; } } my $err; my $pd = Net::Pcap::open_live( $self->dev, $self->snaplen, $self->promisc, 100, # 100 ms timeout \$err, ); unless ($pd) { print("[-] ".__PACKAGE__.": open_live: $err\n"); return; } my $net = 0; my $mask = 0; Net::Pcap::lookupnet($self->dev, \$net, \$mask, \$err); if ($err) { print("[!] ".__PACKAGE__.": lookupnet: $err\n"); } my $fcode; if (Net::Pcap::compile($pd, \$fcode, $self->filter, $self->filterCodeOptimizer, $mask) < 0) { print("[-] ".__PACKAGE__.": compile: ". Net::Pcap::geterr($pd). "\n"); return; } $self->_fcode($fcode); # Used to free it later on, usually on stop call. if (Net::Pcap::setfilter($pd, $fcode) < 0) { print("[-] ".__PACKAGE__.": setfilter: ". Net::Pcap::geterr($pd). "\n"); return; } if (! length($self->file)) { #my $r = Net::Pcap::setnonblock($pd, 1, \$err); #if ($r == -1) { #print("[-] ".__PACKAGE__.": setnonblock: $err\n"); #return; #} # Gather a file descriptor to use by select() # Avoid eating 100% CPU my $fd = Net::Pcap::get_selectable_fd($pd); if ($fd < 0) { print("[-] ".__PACKAGE__.": get_selectable_fd failed\n"); return; } my $sel = IO::Select->new; $sel->add($fd); $self->_sel($sel); $self->_startTime(gettimeofday()); } $self->_pcapd($pd); $self->getFirstLayer; if (length($self->file)) { my $pid = fork(); if (! defined($pid)) { die("[-] ".__PACKAGE__.": fork: $!\n"); } if ($pid) { # Parent $self->_pid($pid); $SIG{CHLD} = 'IGNORE'; return 1; } else { # Son $self->_pid(0); $self->_pcapd($pd); my $dumper = Net::Pcap::dump_open($pd, $self->file); if (! defined($dumper)) { print("[-] ".__PACKAGE__.": dump_open: ".Net::Pcap::geterr($pd). "\n"); exit(1); } Net::Pcap::dump_flush($dumper); Net::Pcap::loop($pd, -1, \&_saveCallback, [ $dumper, $self ]); exit(0); } } return 1; } sub _isFather { my $self = shift; return $self->_pid; } sub _isSon { my $self = shift; return ! $self->_pid; } sub _saveCallback { my ($data, $hdr, $pkt) = @_; my $p = $data->[0]; my $self = $data->[1]; Net::Pcap::dump($p, $hdr, $pkt); Net::Pcap::dump_flush($p); } sub _killTcpdump { my $self = shift; return if $self->_isSon; kill('KILL', $self->_pid); $self->_pid(undef); } sub stop { my $self = shift; # We are in capture mode if (defined($self->file) && length($self->file) && $self->_isFather) { $self->_killTcpdump; } # Free the pcap filter if (defined($self->_fcode)) { Net::Pcap::pcap_freecode($self->_fcode); $self->_fcode(undef); } if (defined($self->_pcapd)) { Net::Pcap::close($self->_pcapd); $self->_pcapd(undef); $self->isRunning(0); } return 1; } sub getStats { my $self = shift; if (!defined($self->_pcapd)) { print("[-] ".__PACKAGE__.": unable to get stats, no pcap descriptor ". "opened.\n"); return; } my %stats; Net::Pcap::stats($self->_pcapd, \%stats); return \%stats; } sub _printStats { my $self = shift; my $stats = $self->getStats; Net::Pcap::close($self->_pcapd); $self->cgDebugPrint(1, 'Frames received : '.$stats->{ps_recv}); $self->cgDebugPrint(1, 'Frames dropped : '.$stats->{ps_drop}); $self->cgDebugPrint(1, 'Frames if dropped: '.$stats->{ps_ifdrop}); return 1; } sub timeoutReset { my $self = shift; $self->_timeWithoutReceiving(0); $self->timeout(0); return 1; } sub _dispatch { my ($user, $header, $packet) = @_; my $self = $user->{self}; my $frames = $user->{frames}; my $ts = $self->keepTimestamp ? $self->_getTimestamp($header) : $self->_setTimestamp; my $frame = { firstLayer => 'ETH', timestamp => $ts, raw => $packet, }; push @$frames, $frame; $user->{frames} = $frames; } sub next { my $self = shift; if (length($self->file)) { die("[-] ".__PACKAGE__.": next method not available while in ". "capture mode.\n"); } # We look ar our internal ring buffer my $frames = $self->_frames; if (@$frames > 0) { my $next = shift @$frames; $self->_frames($frames); return $next; } # If we reach here, we don't have frames anymore in our buffer, # we ask for more except if we have a timeout condition. if ($self->timeout) { return; } # We fetch new awaiting frames, if no timeout occurred yet. # can_read will return as soon as there is something to read, # or will block if nothing to read for timeoutOnNext seconds. my $sel = $self->_sel; my $some_received = 0; my $thisTime = gettimeofday(); # Gather all available frames if ($sel->can_read($self->timeoutOnNext)) { my $h = { self => $self, frames => $frames }; Net::Pcap::pcap_dispatch($self->_pcapd, -1, \&_dispatch, $h); $some_received = scalar(@{$h->{frames}}); } # Update the ring buffer, or we loose those frames $self->_frames($frames); # Check if we didn't received frames during $timeoutOnNext seconds, my $endTime = gettimeofday(); my $diff = $endTime - $thisTime; if (! $some_received) { $self->_timeWithoutReceiving($self->_timeWithoutReceiving + $diff); } else { $self->_timeWithoutReceiving(0); } #print STDERR "*** diff [$diff] received [$some_received] thisTime [$thisTime] endTime [$endTime]\n"; if ($self->_timeWithoutReceiving > $self->timeoutOnNext) { #print STDERR "*** Timeout occurred\n"; $self->timeout(1); } # Check if maximum run time has been reached my $maxRunTime = $self->maxRunTime; my $startTime = $self->_startTime; if ($maxRunTime && ($endTime - $startTime) > $startTime) { #print STDERR "*** Max running time reached\n"; $self->timeout(1); } return; } 1; __END__ =head1 NAME Net::Frame::Dump::Online2 - tcpdump like implementation, online mode and non-blocking =head1 SYNOPSIS use Net::Frame::Dump::Online2; # # Simply create a Dump object # my $oDump = Net::Frame::Dump::Online2->new( dev => 'eth0', ); $oDump->start; # Gather frames while (1) { if (my $f = $oDump->next) { my $raw = $f->{raw}; my $firstLayerType = $f->{firstLayer}; my $timestamp = $f->{timestamp}; } } $oDump->stop; # # Default parameters on creation # my $oDumpDefault = Net::Frame::Dump::Online2->new( timeoutOnNext => 3, timeout => 0, promisc => 0, snaplen => 1514, file => '', overwrite => 0, ); =head1 DESCRIPTION This module implements a tcpdump-like program, for live capture from networks. =head1 ATTRIBUTES =over 4 =item B The network interface to listen on. No default value. =item B Each time you call B method, an internal counter is updated. This counter tells you if you have not received any data since B seconds. When a timeout occurred, B is set to true. =item B When B seconds has been reached, this variable is set to true, and never reset. See B if you want to reset it. =item B If you want to capture a different snaplen, set it a number. Default to 1514. =item B By default, interface is not put into promiscuous mode, set this parameter to true if you want it. =back The following are inherited attributes: =over 4 =item B Pcap filter to use. Default to no filter. =item B Stores information about the first layer type contained on read frame. This attribute is filled only after a call to B method. =item B Returns true if a call to B has been done, false otherwise or if a call to B has been done. =item B Sometimes, when frames are captured and saved to a .pcap file, timestamps sucks. That is, you send a frame, and receive the reply, but your request appear to have been sent after the reply. So, to correct that, you can use B own timestamping system. The default is 0. Set it manually to 1 if you need original .pcap frames timestamps. =back =head1 METHODS =over 4 =item B =item B (hash) Object constructor. You can pass attributes that will overwrite default ones. See B for default values. =item B When you want to start reading frames from network, call this method. =item B When you want to stop reading frames from network, call this method. =item B Returns the next captured frame; undef if none awaiting. Each time this method is called, a comparison is done to see if no frame has been captured during B number of seconds. If so, B attribute is set to 1 to reflect the pending timeout. =item B (B object) This method will store internally, sorted, the B object passed as a single parameter. B methods, implemented in various B objects will be used to efficiently retrieve (via B method) frames. Basically, it is used to make B method (from B) to retrieve quickly the reply frame for a request frame. =item B (B object) This will return an array of possible reply frames for the specified B object. For example, reply frames for a UDP probe will be all the frames which have the same source port and destination port as the request. =item B Will flush stored frames, the one which have been stored via B method. =item B Reset the internal timeout state (B attribute). =item B Tries to get packet statistics on an open descriptor. It returns a reference to a hash that has to following fields: B, B, B. =back =head1 SEE ALSO L =head1 AUTHOR Patrice EGomoRE Auffret =head1 COPYRIGHT AND LICENSE Copyright (c) 2006-2020, Patrice EGomoRE Auffret You may distribute this module under the terms of the Artistic license. See LICENSE.Artistic file in the source distribution archive. =cut Net-Frame-Dump-1.18/lib/Net/Frame/Dump/Writer.pm000444001750001750 1223613652762052 21013 0ustar00gomorgomor000000000000# # $Id: Writer.pm,v a93481b098ec 2020/05/01 08:55:56 gomor $ # package Net::Frame::Dump::Writer; use strict; use warnings; use base qw(Net::Frame::Dump); our @AS = qw( append _fd ); __PACKAGE__->cgBuildIndices; __PACKAGE__->cgBuildAccessorsScalar(\@AS); use Net::Frame::Dump qw(:consts); sub new { my $self = shift->SUPER::new( firstLayer => 'RAW', append => 0, overwrite => 0, @_, ); return $self; } my $mapLinks = { 'NULL' => NF_DUMP_LAYER_NULL(), 'ETH' => NF_DUMP_LAYER_ETH(), 'PPP' => NF_DUMP_LAYER_PPP(), 'RAW' => NF_DUMP_LAYER_RAW(), '80211' => NF_DUMP_LAYER_80211(), 'SLL' => NF_DUMP_LAYER_SLL(), '80211::Radiotap' => NF_DUMP_LAYER_80211_RADIOTAP(), 'ERF' => NF_DUMP_LAYER_ERF(), }; sub _getPcapHeader { my $self = shift; my $dlt = $mapLinks->{$self->firstLayer} or do { print("[-] ".__PACKAGE__.": Can't get pcap header information for ". "this layer type\n"); return; }; # http://wiki.wireshark.org/Development/LibpcapFileFormat return CORE::pack('VvvVVVV', 0xa1b2c3d4, # magic number 2, # major version number 4, # minor version number 0, # GMT to local correction 0, # accuracy of timestamps 1500, # max length of captured packets, in octets $dlt, # data link type ); } sub _openFile { my $self = shift; my $file = $self->file; if (-f $self->file && $self->append) { open(my $fd, '>>', $file) or do { print("[-] ".__PACKAGE__.": open[append]: $file: $!\n"); return; }; $self->_fd($fd); } elsif (!-f $self->file || $self->overwrite) { my $hdr = $self->_getPcapHeader; open(my $fd, '>', $file) or do { print("[-] ".__PACKAGE__.": open[overwrite]: $file: $!\n"); return; }; my $r = syswrite($fd, $hdr, length($hdr)); if (!defined($r)) { print("[-] ".__PACKAGE__.": syswrite: $file: $!\n"); return; } $self->_fd($fd); } return 1; } sub start { my $self = shift; $self->isRunning(1); if (-f $self->file && !$self->overwrite && !$self->append) { print("[-] ".__PACKAGE__.": We will not overwrite a file by default. ". "Use `overwrite' attribute to do it or use `append' mode\n"); return; } $self->_openFile; return 1; } sub stop { my $self = shift; if (!$self->isRunning) { return; } if (defined($self->_fd)) { close($self->_fd); $self->_fd(undef); } $self->isRunning(0); return 1; } sub write { my $self = shift; my ($h) = @_; if (!defined($self->_fd)) { print("[-] ".__PACKAGE__.": file @{[$self->file]} not open for ". "writing\n"); return; } my $raw = $h->{raw}; my $ts = $h->{timestamp}; my $len = length($raw); # Create record header my ($sec, $usec) = split('\.', $ts); my $recHdr = CORE::pack('VVVV', $sec, $usec, $len, $len, ); my $r = syswrite($self->_fd, $recHdr.$raw, length($recHdr.$raw)); if (!defined($r)) { print("[-] ".__PACKAGE__.": syswrite: @{[$self->file]}: $!\n"); return; } return $r; } 1; __END__ =head1 NAME Net::Frame::Dump::Writer - tcpdump like implementation, writer mode =head1 SYNOPSIS use Net::Frame::Dump::Writer; my $oDump = Net::Frame::Dump::Writer->new( file => 'new-file.pcap', firstLayer => 'ETH', ); $oDump->start; $oDump->write({ timestamp => '10.10', raw => ('A' x 14) }); $oDump->stop; =head1 DESCRIPTION This module implements a pcap file builder. You will be able to create frames, then write them in the pcap file format to a file. =head1 ATTRIBUTES The following are inherited attributes: =over 4 =item B Name of the .pcap file to generate. =item B Overwrites a .pcap file that already exists. Default to not. =item B Append new frames to an existing pcap file. Create it if does not exists yet. =item B Stores information about the first layer type. It is used to write .pcap file header information. =item B Returns true if a call to B has been done, false otherwise or if a call to B has been done. =back =head1 METHODS =over 4 =item B =item B (hash) Object constructor. You can pass attributes that will overwrite default ones. See B for default values. =item B When you want to start writing frames to the file, call this method. =item B When you want to stop writing frames to the file, call this method. =item B ({ timestamp => $value, raw => $rawFrame }) Takes a hashref as a parameter. This hashref MUST have timestamp and raw keys, with values. The raw data will be stored to the .pcap file. =back =head1 SEE ALSO L =head1 AUTHOR Patrice EGomoRE Auffret =head1 COPYRIGHT AND LICENSE Copyright (c) 2006-2020, Patrice EGomoRE Auffret You may distribute this module under the terms of the Artistic license. See LICENSE.Artistic file in the source distribution archive. =cut Net-Frame-Dump-1.18/t000755001750001750 013652762052 14030 5ustar00gomorgomor000000000000Net-Frame-Dump-1.18/t/01-use.t000444001750001750 31213652762052 15340 0ustar00gomorgomor000000000000use Test; BEGIN { plan(tests => 1) } use Net::Frame::Dump qw(:consts); use Net::Frame::Dump::Online; use Net::Frame::Dump::Online2; use Net::Frame::Dump::Offline; use Net::Frame::Dump::Writer; ok(1); Net-Frame-Dump-1.18/t/02-pod-coverage.t000444001750001750 53113652762052 17123 0ustar00gomorgomor000000000000eval "use Test::Pod::Coverage tests => 4"; if ($@) { use Test; plan(tests => 1); skip("Test::Pod::Coverage required for testing"); } else { pod_coverage_ok("Net::Frame::Dump::Online"); pod_coverage_ok("Net::Frame::Dump::Online2"); pod_coverage_ok("Net::Frame::Dump::Offline"); pod_coverage_ok("Net::Frame::Dump::Writer"); } Net-Frame-Dump-1.18/t/03-test-pod.t000444001750001750 23113652762052 16305 0ustar00gomorgomor000000000000eval "use Test::Pod 1.00"; if ($@) { use Test; plan(tests => 1); skip("Test::Pod 1.00 required for testing"); } else { all_pod_files_ok(); }