aboutsummaryrefslogtreecommitdiffstats
path: root/test/functional
diff options
context:
space:
mode:
authordenpamusic <denpa@netfleet.space>2019-09-15 23:15:44 +0300
committerdenpamusic <denpa@netfleet.space>2019-09-15 23:15:44 +0300
commite4e8e675b610b49975c8b90d1d207f6f56ac6f93 (patch)
tree8fe355be19edbad9347a5155e1b0b60f86db4e2b /test/functional
parentfba84f7f415307fdc3df3efd60ec8b910e888ef5 (diff)
downloadrspamd-e4e8e675b610b49975c8b90d1d207f6f56ac6f93.tar.gz
rspamd-e4e8e675b610b49975c8b90d1d207f6f56ac6f93.zip
[Feature] Add p0f scanner
Diffstat (limited to 'test/functional')
-rw-r--r--test/functional/cases/161_p0f.robot88
-rw-r--r--test/functional/configs/p0f.conf11
-rw-r--r--test/functional/lib/vars.py1
-rwxr-xr-xtest/functional/util/dummy_p0f.py98
4 files changed, 198 insertions, 0 deletions
diff --git a/test/functional/cases/161_p0f.robot b/test/functional/cases/161_p0f.robot
new file mode 100644
index 000000000..9acbf7b2d
--- /dev/null
+++ b/test/functional/cases/161_p0f.robot
@@ -0,0 +1,88 @@
+*** Settings ***
+Suite Setup p0f Setup
+Suite Teardown p0f Teardown
+Library Process
+Library ${TESTDIR}/lib/rspamd.py
+Resource ${TESTDIR}/lib/rspamd.robot
+Variables ${TESTDIR}/lib/vars.py
+
+*** Variables ***
+${CONFIG} ${TESTDIR}/configs/plugins.conf
+${MESSAGE} ${TESTDIR}/messages/spam_message.eml
+${MESSAGE2} ${TESTDIR}/messages/freemail.eml
+${REDIS_SCOPE} Suite
+${RSPAMD_SCOPE} Suite
+${URL_TLD} ${TESTDIR}/../lua/unit/test_tld.dat
+
+*** Test Cases ***
+p0f MISS
+ Run Dummy p0f
+ ${result} = Scan Message With Rspamc ${MESSAGE} --ip 1.1.1.1
+ Check Rspamc ${result} P0F
+ Check Rspamc ${result} WINDOWS inverse=1
+ Check Rspamc ${result} P0F_FAIL inverse=1
+ Shutdown p0f
+
+p0f HIT
+ Run Dummy p0f ${P0F_SOCKET} windows
+ ${result} = Scan Message With Rspamc ${MESSAGE} --ip 1.1.1.2
+ Check Rspamc ${result} P0F inverse=1
+ Check Rspamc ${result} ETHER
+ Check Rspamc ${result} DISTGE10
+ Check Rspamc ${result} WINDOWS
+ Shutdown p0f
+
+p0f NOREDIS
+ Shutdown Process With Children ${REDIS_PID}
+ Run Dummy p0f
+ ${result} = Scan Message With Rspamc ${MESSAGE} --ip 1.1.1.3
+ Check Rspamc ${result} P0F
+ Check Rspamc ${result} ETHER
+ Check Rspamc ${result} DISTGE10
+ Check Rspamc ${result} P0F_FAIL inverse=1
+ Shutdown p0f
+
+p0f NOMATCH
+ Run Dummy p0f ${P0F_SOCKET} windows no_match
+ ${result} = Scan Message With Rspamc ${MESSAGE} --ip 1.1.1.4
+ Check Rspamc ${result} P0F inverse=1
+ Check Rspamc ${result} WINDOWS inverse=1
+ Shutdown p0f
+
+p0f BADQUERY
+ Run Dummy p0f ${P0F_SOCKET} windows bad_query
+ ${result} = Scan Message With Rspamc ${MESSAGE} --ip 1.1.1.5
+ Check Rspamc ${result} P0F_FAIL
+ Check Rspamc ${result} Malformed Query
+ Check Rspamc ${result} WINDOWS inverse=1
+ Shutdown p0f
+
+p0f FAILURE
+ Run Dummy p0f ${P0F_SOCKET} windows fail
+ ${result} = Scan Message With Rspamc ${MESSAGE} --ip 1.1.1.6
+ Check Rspamc ${result} P0F_FAIL
+ Check Rspamc ${result} Malformed Response
+ Check Rspamc ${result} WINDOWS inverse=1
+ Shutdown p0f
+
+*** Keywords ***
+p0f Setup
+ ${PLUGIN_CONFIG} = Get File ${TESTDIR}/configs/p0f.conf
+ Set Suite Variable ${PLUGIN_CONFIG}
+ Generic Setup PLUGIN_CONFIG
+ Run Redis
+
+p0f Teardown
+ Normal Teardown
+ Shutdown Process With Children ${REDIS_PID}
+ Shutdown p0f
+ Terminate All Processes kill=True
+
+Shutdown p0f
+ ${p0f_pid} = Get File if exists /tmp/dummy_p0f.pid
+ Run Keyword if ${p0f_pid} Shutdown Process With Children ${p0f_pid}
+
+Run Dummy p0f
+ [Arguments] ${socket}=${P0F_SOCKET} ${os}=linux ${status}=ok
+ ${result} = Start Process ${TESTDIR}/util/dummy_p0f.py ${socket} ${os} ${status}
+ Wait Until Created /tmp/dummy_p0f.pid
diff --git a/test/functional/configs/p0f.conf b/test/functional/configs/p0f.conf
new file mode 100644
index 000000000..69303772a
--- /dev/null
+++ b/test/functional/configs/p0f.conf
@@ -0,0 +1,11 @@
+redis {
+ servers = "${REDIS_ADDR}:${REDIS_PORT}";
+}
+p0f {
+ socket = "${P0F_SOCKET}";
+ patterns {
+ WINDOWS = '^Windows.*';
+ ETHER = '^Ethernet.*';
+ DISTGE10 = '^distance:[0-9]{2}$';
+ }
+}
diff --git a/test/functional/lib/vars.py b/test/functional/lib/vars.py
index 97b53b2e1..4559db205 100644
--- a/test/functional/lib/vars.py
+++ b/test/functional/lib/vars.py
@@ -15,6 +15,7 @@ PORT_PROXY = 56795
PORT_CLAM = 56796
PORT_FPROT = 56797
PORT_FPROT2_DUPLICATE = 56798
+P0F_SOCKET = '/tmp/p0f.sock'
REDIS_ADDR = u'127.0.0.1'
REDIS_PORT = 56379
NGINX_ADDR = u'127.0.0.1'
diff --git a/test/functional/util/dummy_p0f.py b/test/functional/util/dummy_p0f.py
new file mode 100755
index 000000000..e44844812
--- /dev/null
+++ b/test/functional/util/dummy_p0f.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+
+PID = "/tmp/dummy_p0f.pid"
+
+import os
+import sys
+import struct
+import socket
+import dummy_killer
+try:
+ import SocketServer as socketserver
+except:
+ import socketserver
+
+class MyStreamHandler(socketserver.BaseRequestHandler):
+
+ def handle(self):
+ S = {
+ 'bad_query' : 0x0,
+ 'ok' : 0x10,
+ 'no_match' : 0x20
+ }
+
+ OS = {
+ 'windows' : ('Windows', '7 or 8'),
+ 'linux' : ('Linux', '3.11 and newer')
+ }
+
+ self.data = self.request.recv(21).strip()
+
+ if self.server.p0f_status == 'fail':
+ response = 0
+ else:
+ response = struct.pack(
+ "IbIIIIIIIhbb32s32s32s32s32s32s",
+ 0x50304602, # magic
+ S[self.server.p0f_status], # status
+ 1568493408, # first_seen
+ 1568493408, # last_seen
+ 1, # total_conn
+ 1, # uptime_min
+ 4, # up_mod_days
+ 1568493408, # last_nat
+ 1568493408, # last_chg
+ 10, # distance
+ 0, # bad_sw
+ 0, # os_match_q
+ OS[self.server.p0f_os][0], # os_name
+ OS[self.server.p0f_os][1], # os_flavor
+ '', # http_name
+ '', # http_flavor
+ 'Ethernet or modem', # link_type
+ '' # language
+ )
+
+ self.request.sendall(response)
+ self.request.close()
+
+def cleanup(SOCK):
+ if os.path.exists(SOCK):
+ try:
+ os.unlink(SOCK)
+ except OSError:
+ logging.warning("Could not unlink socket %s", SOCK)
+
+if __name__ == "__main__":
+ SOCK = '/tmp/p0f.sock'
+ p0f_status = 'ok'
+ p0f_os = 'linux'
+
+ alen = len(sys.argv)
+ if alen > 1:
+ SOCK = sys.argv[1]
+ if alen >= 4:
+ p0f_os = sys.argv[2]
+ p0f_status = sys.argv[3]
+ elif alen >= 3:
+ p0f_os = sys.argv[2]
+
+ cleanup(SOCK)
+
+ server = socketserver.UnixStreamServer(SOCK, MyStreamHandler, bind_and_activate=False)
+ server.allow_reuse_address = True
+ server.p0f_status = p0f_status
+ server.p0f_os = p0f_os
+ server.server_bind()
+ server.server_activate()
+
+ dummy_killer.setup_killer(server)
+ dummy_killer.write_pid(PID)
+
+ try:
+ server.handle_request()
+ except socket.error:
+ print "Socket closed"
+
+ server.server_close()
+ cleanup(SOCK)