minor changes to facilitate testing
improved error reporting of several failures
added p0f v2 compatibility to p0f v3 results: in addition to all the newer 
values, also report the old ones too.
---
 plugins/ident/p0f        |   43 +++++++++++++++--------
 t/plugin_tests/ident/p0f |   87 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 115 insertions(+), 15 deletions(-)
 create mode 100644 t/plugin_tests/ident/p0f

diff --git a/plugins/ident/p0f b/plugins/ident/p0f
index 772d965..9027aa8 100644
--- a/plugins/ident/p0f
+++ b/plugins/ident/p0f
@@ -147,7 +147,7 @@ sub register {
 sub hook_connect {
     my($self, $qp) = @_;
 
-    my $p0f_version = $self->{_args}->{version} || 3;
+    my $p0f_version = $self->{_args}{version} || 3;
     if ( $p0f_version == 3 ) {
         my $response = $self->query_p0f_v3() or return DECLINED;
         $self->test_v3_response( $response ) or return DECLINED;
@@ -167,18 +167,18 @@ sub get_v2_query {
 
     my $local_ip = $self->{_args}{local_ip} || $self->qp->connection->local_ip;
 
-    my $src = new Net::IP ($self->qp->connection->remote_ip) 
+    my $src = new Net::IP ($self->qp->connection->remote_ip)
         or $self->log(LOGERROR, "p0f: ".Net::IP::Error()), return;
 
     my $dst = new Net::IP($local_ip)
         or $self->log(LOGERROR, "p0f: ".NET::IP::Error()), return;
 
     return pack("L L L N N S S",
-                   $QUERY_MAGIC_V2, 
-                   1, 
+                   $QUERY_MAGIC_V2,
+                   1,
                    rand ^ 42 ^ time,
-                   $src->intip(), 
-                   $dst->intip(), 
+                   $src->intip(),
+                   $dst->intip(),
                    $self->qp->connection->remote_port,
                    $self->qp->connection->local_port);
 };
@@ -186,7 +186,10 @@ sub get_v2_query {
 sub get_v3_query {
     my $self = shift;
 
-    my $src_ip = $self->qp->connection->remote_ip;
+    my $src_ip = $self->qp->connection->remote_ip or do {
+        $self->log( LOGERROR, "unable to determine remote IP");
+        return;
+    };
 
     if ( $src_ip =~ /:/ ) {   # IPv6
         my @bits = split(/\:/, $src_ip );
@@ -200,8 +203,11 @@ sub get_v3_query {
 sub query_p0f_v3 {
     my $self = shift;
 
-    my $p0f_socket = $self->{_args}->{p0f_socket} or return;
-    my $query = $self->get_v3_query();
+    my $p0f_socket = $self->{_args}{p0f_socket} or do {
+        $self->log(LOGERROR, "socket not defined in config.");
+        return;
+    };
+    my $query = $self->get_v3_query() or return;
 
 # Open the connection to p0f
     my $sock;
@@ -243,15 +249,15 @@ sub query_p0f_v2 {
     my $query = $self->get_v2_query() or return;
 
     # Open the connection to p0f
-    socket(SOCK, PF_UNIX, SOCK_STREAM, 0) 
+    socket(SOCK, PF_UNIX, SOCK_STREAM, 0)
         or $self->log(LOGERROR, "p0f: socket: $!"), return;
-    connect(SOCK, sockaddr_un($p0f_socket)) 
+    connect(SOCK, sockaddr_un($p0f_socket))
         or $self->log(LOGERROR, "p0f: connect: $!"), return;
-    defined syswrite SOCK, $query 
+    defined syswrite SOCK, $query
         or $self->log(LOGERROR, "p0f: write: $!"), close SOCK, return;
 
     my $response;
-    defined sysread SOCK, $response, 1024 
+    defined sysread SOCK, $response, 1024
         or $self->log(LOGERROR, "p0f: read: $!"), close SOCK, return;
     close SOCK;
     return $response;
@@ -314,7 +320,7 @@ sub store_v2_results {
         $nat, $real, $score, $mflags, $uptime) =
             unpack ("L L C Z20 Z40 c Z30 Z30 C C C s S N", $response);
 
-    my $p0f = { 
+    my $p0f = {
         genre    => $genre,
         detail   => $detail,
         distance => $dist,
@@ -325,6 +331,7 @@ sub store_v2_results {
     $self->qp->connection->notes('p0f', $p0f);
     $self->log(LOGINFO, $genre." (".$detail.")");
     $self->log(LOGERROR,"error: $@") if $@;
+    return $p0f;
 };
 
 sub store_v3_results {
@@ -341,10 +348,16 @@ sub store_v3_results {
         next if ! defined $values[$i];
         $r{ $labels[$i] } = $values[$i];
     };
+    if ( $r{os_name} ) {                             # compat with p0f v2
+        $r{genre}  = "$r{os_name} $r{os_flavor}";
+        $r{link}   = $r{link_type} if $r{link_type};
+        $r{uptime} = $r{uptime_min} if $r{uptime_min};
+    };
 
     $self->qp->connection->notes('p0f', \%r);
-    $self->log(LOGINFO, "$values[12] $values[13]");
+    $self->log(LOGINFO, "$r{os_name} $r{os_flavor}");
     $self->log(LOGDEBUG, join(' ', @values ));
     $self->log(LOGERROR,"error: $@") if $@;
+    return \%r;
 };
 
diff --git a/t/plugin_tests/ident/p0f b/t/plugin_tests/ident/p0f
new file mode 100644
index 0000000..cf743c9
--- /dev/null
+++ b/t/plugin_tests/ident/p0f
@@ -0,0 +1,87 @@
+#!perl -w
+
+use strict;
+use warnings;
+
+use Qpsmtpd::Constants;
+
+sub register_tests {
+    my $self = shift;
+
+    $self->register_test('test_get_v2_query', 1);
+    $self->register_test('test_get_v3_query', 1);
+    $self->register_test('test_store_v2_results', 2);
+    $self->register_test('test_store_v3_results', 2);
+}
+
+sub test_query_p0f_v2 {
+#TODO
+# get path to p0f socket
+# see if it exists
+# try to connect to it
+# if connection succeeds, send it a query
+#   do we  a) pick an IP that recently connected?
+#     or   b) create a connection to localhost...
+#     or   c) is there a p0f test value?
+# parse and validate the response
+# using $self->test_v2_response()
+};
+
+sub test_query_p0f_v3 {
+#TODO: similar to v2 ....
+};
+
+sub test_get_v2_query {
+    my $self = shift;
+
+    my $local_ip = '208.75.177.101';
+    my $remote   = '108.60.149.81';
+    $self->{_args}{local_ip} = $local_ip;
+    $self->qp->connection->local_ip($local_ip);
+    $self->qp->connection->remote_ip($remote);
+    $self->qp->connection->local_port(25);
+    $self->qp->connection->remote_port(2500);
+
+    my $r = $self->get_v2_query();
+    ok( $r, 'get_v2_query' );
+    #use Data::Dumper; warn Data::Dumper::Dumper( $r );
+};
+
+sub test_get_v3_query {
+    my $self = shift;
+
+    my $remote   = '108.60.149.81';
+    $self->qp->connection->remote_ip($remote);
+
+    my $r = $self->get_v3_query();
+    ok( $r, 'get_v3_query' );
+    #use Data::Dumper; warn Data::Dumper::Dumper( $r );
+};
+
+sub test_store_v2_results {
+    my $self = shift;
+
+    my $response = pack("L L C Z20 Z40 c Z30 Z30 C C C s S N",
+        '233811181', '1336687857', '0', 'Windows', 'XP/2000 (RFC1323+, w+, 
tstamp-)',
+        '11', 'ethernet/modem', '', '0', '0', '1', '-25600', '255', '255' );
+
+    my $r = $self->store_v2_results( $response );
+
+    ok( $r, "query_p0f_v2 result") or return;
+    ok( $r->{genre} =~ /windows/i, "store_v2_results, genre" );
+    #use Data::Dumper; warn Data::Dumper::Dumper( $r );
+};
+
+sub test_store_v3_results {
+    my $self = shift;
+
+    my $response = pack("L L L L L L L L L s C C A32 A32 A32 A32 A32 A32 A32", 
+        1345340930, 16, 1336676595, 1336680290, 3, 0, 0, 0, 0, 13, 0, 0,
+        'Windows', '7 or 8', '', '', 'Ethernet or modem', '', '');
+    my $r = $self->store_v3_results( $response );
+
+    ok( $r, "query_p0f_v3 result");
+    ok( $r->{genre} =~ /windows/i, "store_v3_results, genre" );
+};
+
+
-- 
1.7.9.6

Reply via email to