Skip to content

Instantly share code, notes, and snippets.

@ksurent
Created March 5, 2012 20:32
Show Gist options
  • Save ksurent/1980907 to your computer and use it in GitHub Desktop.
Save ksurent/1980907 to your computer and use it in GitHub Desktop.
Simple PCAP viewer
#!/usr/bin/env perl
use v5.14;
use UUID::Tiny;
use Mojolicious::Lite;
use Net::Frame::Simple;
use Net::Frame::Dump::Offline;
BEGIN {
$ENV{MOJO_MAX_MESSAGE_SIZE} = 1024 * 1024 * 1024 * 10;
$ENV{MOJO_MODE} = 'production';
}
get '/' => sub {
my $self = shift;
$self->render('index');
} => 'index';
post '/upload' => sub {
my $self = shift;
my $upload = $self->req->upload('file');
if(defined $upload) {
if($self->req->is_limit_exceeded) {
return $self->render(text => 'The file is too big (must be <= 10MB)');
}
my $uuid = create_UUID_as_string(UUID_V5, $upload->asset->slurp);
my $file = $self->app->home->rel_file("uploads/$uuid");
$upload->move_to($file);
$self->redirect_to('view', uuid => $uuid);
}
} => 'upload';
get '/:uuid' => sub {
my $self = shift;
my $uuid = $self->param('uuid');
my $file = $self->app->home->rel_file("uploads/$uuid");
if(not is_UUID_string($uuid) or not -e $file) {
return $self->render(text => 'File not found', status => 404);
}
unless(`file $file` =~ /tcpdump capture file/) {
unlink $file;
return $self->render(text => 'Not a pcap file');
}
my $dump = Net::Frame::Dump::Offline->new(file => $file);
$dump->start;
my @frames;
while(my $frame = $dump->next) {
my $simple = Net::Frame::Simple->new(
firstLayer => $frame->{firstLayer},
timestamp => $frame->{timestamp},
raw => $frame->{raw},
);
my @layers = $simple->layers;
my @layers_short = map +{name => $_->layer, desc => $_->print}, @layers;
# Net::Frame can only handle these L3 and L4 protocols
my($l3) = grep { $_->layer =~ /^(?:IPv[46])$/ } @layers;
my($l4) = grep { $_->layer =~ /^(?:TCP|UDP)$/} @layers;
my $summary = $layers[-1]->layer . ': '
. $l3->src . ':' . $l4->src
. ' => '
. $l3->dst . ':' . $l4->dst;
push @frames, {
summary => $summary,
layers => \@layers_short,
};
}
$dump->stop;
$self->stash(frames => \@frames);
} => 'view';
app->start;
__DATA__
@@ not_found.html.ep
% title 'Not found';
Not found
@@ index.html.ep
% layout 'default';
% title 'Welcome';
<form action="<%= url_for 'upload' %>" method="post" enctype="multipart/form-data">
<input type="file" name="file"/>
<input type="submit" value="Upload"/>
</form>
@@ view.html.ep
% layout 'default';
% title 'Pcap Viewer';
% for my $f (@{ stash 'frames' }) {
<table>
<tr>
<td>
<span class="pseudo-link">+</span>
</td>
<td>
<%= $f->{summary} %>
</td>
</tr>
% for my $l (@{ $f->{layers} }) {
<tr class="hidden">
<td colspan="2">
<pre><%= $l->{desc} %></pre>
</td>
</tr>
% }
</table>
% }
@@ layouts/default.html.ep
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<style type="text/css">
.pseudo-link {
cursor: pointer;
border-bottom: 1px dotted;
color: #1B86C6;
}
.hidden {
display: none;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function() {
$('.pseudo-link').click(function() {
var $next = $(this).parent().parent().nextAll('tr');
if($next.is(':hidden')) {
$(this).text('-');
$next.removeClass('hidden');
}
else {
$(this).text('+');
$next.addClass('hidden');
}
});
});
</script>
</head>
<body>
<%= content %>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment