Import from old repository

This commit is contained in:
Stefan
2020-04-06 18:44:45 +02:00
commit 5382fa57a4
204 changed files with 19878 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
This file is part of ${project.name}.
Copyright (C) ${project.inceptionYear},${recentYears} ${owner} (${email})
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
+84
View File
@@ -0,0 +1,84 @@
<?xml version="1.0"?>
<!--
This file is part of Rubanetra.
Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<licenseSummary xmlns="http://mojo.codehaus.org/">
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<licenses>
<license>
<name>The MIT License</name>
<url>http://www.slf4j.org/license.html</url>
</license>
</licenses>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
</licenses>
</dependency>
<dependency>
<groupId>dnsjava</groupId>
<artifactId>dnsjava</artifactId>
<licenses>
<license>
<name>The BSD License</name>
<url>http://www.dnsjava.org/dnsjava-current/README</url>
</license>
</licenses>
</dependency>
<dependency>
<groupId>xpp3</groupId>
<artifactId>xpp3_min</artifactId>
<licenses>
<license>
<name>Indiana University Extreme! Lab Software License, version 1.1.1</name>
<url>http://www.bearcave.com/software/java/xml/xmlpull_license.html</url>
</license>
</licenses>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
</licenses>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
</license>
</licenses>
</dependency>
</dependencies>
</licenseSummary>
@@ -0,0 +1,183 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
grammar RubanetraSystemConfiguration;
/**
* This file defines the EBNF grammar of the default system configuration
* using the ANTLR v4 syntax (see http://www.antlr.org/wiki/display/ANTLR4/ANTLR+4+Documentation).
* The general settings '*_configuration_file' may be used
* to indicate the location of the corresponding configuration files, however
* the main configuration file path containing the 'general' section
* must be known by the system a priori at a fixed location (usually the classpath) or it has to be passed as
* command line parameter during runtime.
* The recommended setup is to use two configuration files, i.e.
* one file containing all static configuration blocks ('general', 'protocols')
* and another file containing the dynamic runtime configuration ('input', 'output').
* The system configuration itself may be split into four different files according
* to the main settings categories 'general', 'protocols', 'input/output' (a coarser split into a single, two or three
* file(s) is possible as well).
*
* The 'system' node serves as grammar entry point and consists of a mandatory 'general'
* configuration section, followed by the remaining configurations optionally within the
* same file. However, this does not implicate that the protocol/library/runtime configurations
* are optional in general.
**/
system : (generalConfig|inputConfig|outputConfig|protocolConfig)* ;
/**
* Although the general configuration contains a predefined number of settings,
* future extensions may lead to a different set of settings, therefore no semantic
* restrictions will be laid upon the parser at the top level nodes.
**/
generalConfig : 'general' '{' (generalSetting)* '}' ';' ;
protocolConfig : 'protocols' '{' (protocolConfigurationFileSetting | (protocolSetting)*) '}' ';' ;
inputConfig : 'input' '{' (inputConfigurationFileSetting | (inputSetting)*) '}' ';' ;
outputConfig : 'output' '{' (outputConfigurationFileSetting | (outputSetting)*) '}' ';' ;
/**
* ##################################
* # General Setting Specification #
* ##################################
**/
generalSetting : ( 'drools_base_model_name' '=' droolsBaseModelName // optional, the name of the knowledge base to use (defined in 'kmodule.xml')
| 'drools_session_model_name' '=' droolsSessionModelName // optional, the name of the session to use (defined in 'kmodule.xml')
| 'drools_configuration_directory' '=' droolsConfigurationFile // required, path of a separate file containing the Drools knowledge base configuration ('kmodule.xml')
) ';' ;
droolsBaseModelName : STRING ;
droolsSessionModelName : STRING ;
droolsConfigurationFile : filePath ;
// End of General Setting Specification
/**
* ###################################
* # Protocol Setting Specification #
* ###################################
**/
protocolConfigurationFileSetting : 'protocol_configuration_file' '=' filePath ;
protocolSetting : (protocolHeader (portSpecification|protocolBinding|transportLayerMappingSetting)*) ;
protocolHeader : '[' protocolId ']' ':' ; // every protocol parser configuration section has an unique identifier
portSpecification : 'port' '=' ( ALL // matches all network ports [0;2^16-1]
| NONE // matches no ports (i.e. another way to disable a protocol)
| multiplePorts // matches either one or several single ports or port ranges
) ';' ;
multiplePorts : portRange (',' portRange)* ;
portRange : singlePort ('-' singlePort)? ;
protocolBinding : 'protocol_binding' '=' protocolId BIND_OP protocolId ';';
transportLayerMappingSetting : 'transport_layer_mapping_strategy' '=' transportLayerMappingStrategy ';' ;
transportLayerMappingStrategy : (DESTINATION_PORT) ;
// End of Protocol Setting Specification
/**
* ###########################################
* # Dynamic Runtime Setting Specification #
* ###########################################
* The following settings will be used to adjust the concrete runtime behaviour of the system,
* i.e. a set of one or multiple PCAP input files, a set of predefined protocols (see Protocol Setting Specification),
* a set of rules to enable.
**/
inputConfigurationFileSetting : 'input_configuration_file' '=' filePath ';' ; // optional, path of a separate file containing the I/O configuration
inputSetting : ( 'input_file' '=' inputSpecification // required, list of network capture input files
| 'input_format' '=' inputFormat // required, currently only PCAP (all format versions supported by the utilized libpcap library)
| 'bpf_filter' '=' bpfFilterString // optional, String representing a Berkeley Packet Filter (BPF) expression - a syntax check will not be performed
| 'bpf_netmask' '=' bpfNetmask // optional, integer representing a Berkeley Packet Filter (BPF) netmask
| 'bpf_optimize' '=' bpfOptimize // optional, boolean representing a Berkeley Packet Filter (BPF) optimization flag
| 'sort_by_first_timestamp' '=' sortByFirstTimestamp // optional, boolean flag indicating whether or not to sort according to the first timestamp of the PCAP entry
) ';' ;
outputConfigurationFileSetting : 'output_configuration_file' '=' filePath ';'; // optional, path of a separate file containing the I/O configuration
outputSetting : ( 'output_file' '=' outputSpecification // required, content output stream
| 'output_format' '=' outputFormat // plaso parser or general csv format
) ';' ;
inputSpecification : filePath (',' filePath)* ;
inputFormat : (PCAP) ;
bpfFilterString : STRING ;
bpfNetmask : INT ;
bpfOptimize : (TRUE|FALSE) ;
sortByFirstTimestamp : (TRUE|FALSE) ;
outputSpecification : (STDOUT|filePath) ;
outputFormat : (PLASO|CSV|NOP) ;
// End of Runtime Setting Specification
regexFlag : // see docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html#UNIX_LINES
CASE_INSENSITIVE | MULTILINE | DOTALL | UNICODE_CASE | CANON_EQ
| UNIX_LINES | LITERAL | UNICODE_CHARACTER_CLASS | COMMENTS | NONE ;
ipv6 : // loosely based on RFC 5954, see http://tools.ietf.org/html/rfc5954, semantic check (int range) will be performed after parsing
( INT ':' INT ':' INT ':' INT ':' INT ':' INT ':' optIPv6Trailer
| '::' INT ':' INT ':' INT ':' INT ':' INT ':' optIPv6Trailer
| INT? '::' INT ':' INT ':' INT ':' INT ':' optIPv6Trailer
| ((INT ':' )? INT)? '::' INT ':' INT ':' INT ':' optIPv6Trailer
| ((INT ':' )? (INT ':' )? INT)? '::' INT ':' INT ':' optIPv6Trailer
| ((INT ':' )? (INT ':' )? (INT ':' )? INT)? '::' INT ':' optIPv6Trailer
| ((INT ':' )? (INT ':' )? (INT ':' )? (INT ':' )? INT)? '::' optIPv6Trailer
| ((INT ':' )? (INT ':' )? (INT ':' )? (INT ':' )? (INT ':' )? INT)? '::' INT
| ((INT ':' )? (INT ':' )? (INT ':' )? (INT ':' )? (INT ':' )? (INT ':' )? INT)? '::'
)
;
optIPv6Trailer : ( INT ':' INT) | ipv4 ;
ipv4 : INT '.' INT '.' INT '.' INT ; // Match an IPv4 address, e.g. 127.0.0.1
protocolId : ID ;
singlePort : INT ;
filePath : STRING ;
DEBUG : [Dd][Ee][Bb][Uu][Gg] ;
WARNING : [Ww][Aa][Rr][Nn][Ii][Nn][Gg] ;
INFO : [Ii][Nn][Ff][Oo] ;
DISABLED : [Dd][Ii][Ss][Aa][Bb][Ll][Ee][Dd] ;
PLASO : [Pp][Ll][Aa][Ss][Oo] ;
PCAP : [Pp][Cc][Aa][Pp] ;
DEFAULT : [Dd][Ee][Ff][Aa][Uu][Ll][Tt] ;
DESTINATION_PORT: [Dd][Ee][Ss][Tt][Ii][Nn][Aa][Tt][Ii][Oo][Nn]'_'[Pp][Oo][Rr][Tt] ;
CSV : [Cc][Ss][Vv] ;
CUSTOM : [Cc][Uu][Ss][Tt][Oo][Mm] ;
TRUE : ([Tt][Rr][Uu][Ee]) ;
FALSE : ([Ff][Aa][Ll][Ss][Ee]) ;
BIND_OP : ([Tt][Oo]) | '->' ;
ALL : ([Aa][Ll][Ll]) | ([Aa][Nn][Yy]) ;
NONE : [Nn][Oo][Nn][Ee] ;
NOP : [Nn][Oo][Pp] ;
STDOUT : ([Ss][Tt][Dd][Oo][Uu][Tt]) ;
/**
* Possible Java regular expression flags
* See docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html#UNIX_LINES
**/
CASE_INSENSITIVE : [Cc][Aa][Ss][Ee][_][Ii][Nn][Ss][Ee][Nn][Ss][Ii][Tt][Ii][Vv][Ee] ;
MULTILINE : [Mm][Uu][Ll][Tt][Ii][Ll][Ii][Nn][Ee];
DOTALL : [Dd][Oo][Tt][Aa][Ll][Ll] ;
UNICODE_CASE : [Uu][Nn][Ii][Cc][Oo][Dd][Ee][_][Cc][Aa][Ss][Ee] ;
CANON_EQ : [Cc][Aa][Nn][Oo][Nn][_][Ee][Qq] ;
UNIX_LINES : [Uu][Nn][Ii][Xx][_][Ll][Ii][Nn][Ee][Ss] ;
LITERAL : [Ll][Ii][Tt][Ee][Rr][Aa][Ll] ;
UNICODE_CHARACTER_CLASS : [Uu][Nn][Ii][Cc][Oo][Dd][Ee][_][Cc][Hh][Aa][Rr][Aa][Cc][Tt][Ee][Rr][_][Cc][Ll][Aa][Ss][Ss] ;
COMMENTS : [Cc][Oo][Mm][Mm][Ee][Nn][Tt][Ss] ;
fragment DIGIT : [0-9] ;
ID : ([a-zA-Z]+ DIGIT*)+ ; // match lower-case and upper-case identifiers
INT : DIGIT+ ; // match natural, positive numbers including 0
STRING : '"' ('\\"'|.)*? '"' ; // match any characters between double quotes, including escaped '"'
LINE_COMMENT : ('//'|'#') .*? '\r'? '\n' -> skip ; // match line comments
COMMENT : '/*' .*? '*/' -> skip ; // match "/*" multiline comment "*/"
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
+96
View File
@@ -0,0 +1,96 @@
<!--
This file is part of Rubanetra.
Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>distribution</id>
<baseDirectory>${project.build.finalName}-distribution</baseDirectory>
<formats>
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.basedir}</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>README*</include>
<include>LICENSE*</include>
<include>NOTICE*</include>
</includes>
<filtered>true</filtered>
</fileSet>
<fileSet>
<directory>${archive.output.directory}</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/lib</directory>
<outputDirectory>lib</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.build.directory}/${jnetpcap.native.lib.directory}/native</directory>
<outputDirectory>lib/${jnetpcap.native.lib.directory}</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.build.directory}/generated-sources/license</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.build.directory}/generated-resources/licenses</directory>
<outputDirectory>lib/licenses</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.build.directory}/generated-resources</directory>
<outputDirectory>lib</outputDirectory>
<includes>
<include>licenses.xml</include>
</includes>
</fileSet>
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory>conf</outputDirectory>
<includes>
<include>DefaultKnowledgeBase/**</include>
<include>META-INF/kmodule.xml</include>
<include>*.conf</include>
<include>*.properties</include>
<include>*.xml</include>
</includes>
<filtered>true</filtered>
</fileSet>
</fileSets>
<files>
<file>
<source>pom.xml</source>
<outputDirectory>conf/META-INF/maven/${project.groupId}/${project.artifactId}</outputDirectory>
<filtered>true</filtered>
</file>
<file>
<source>${project.build.directory}/maven-archiver/pom.properties</source>
<outputDirectory>conf/META-INF/maven/${project.groupId}/${project.artifactId}</outputDirectory>
<filtered>true</filtered>
</file>
</files>
</assembly>
+68
View File
@@ -0,0 +1,68 @@
<!--
This file is part of Rubanetra.
Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>package</id>
<baseDirectory>${project.build.finalName}-package</baseDirectory>
<formats>
<!--<format>tar.gz</format>-->
<!--<format>tar.bz2</format>-->
<format>zip</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.basedir}</directory>
<includes>
<include>README*</include>
<include>NOTICE*</include>
</includes>
<useDefaultExcludes>true</useDefaultExcludes>
<filtered>true</filtered>
</fileSet>
<fileSet>
<directory>${project.basedir}</directory>
<includes>
<include>pom.xml</include>
</includes>
<filtered>false</filtered>
</fileSet>
<fileSet>
<directory>${project.basedir}/src/main</directory>
</fileSet>
<fileSet>
<directory>${project.basedir}/src/license</directory>
</fileSet>
<fileSet>
<directory>${project.build.directory}/generated-sources/license</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.basedir}/src</directory>
<outputDirectory>/src</outputDirectory>
<useDefaultExcludes>true</useDefaultExcludes>
<excludes>
<exclude>**/*.log</exclude>
<exclude>**/${project.build.directory}/**</exclude>
<exclude>**/test/**</exclude>
</excludes>
</fileSet>
</fileSets>
</assembly>
@@ -0,0 +1,638 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.config;
import at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration;
import at.jku.fim.rubanetra.config.model.impl.RubanetraSystemConfigurationBaseListenerImpl;
import at.jku.fim.rubanetra.config.model.impl.RubanetraSystemConfigurationBuilder;
import at.jku.fim.rubanetra.drools.DroolsKrakenProtocolHandler;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.apache.commons.cli.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
/**
* This class serves as the projects main entry point, i.e. it contains a main-method and provides
* a basic command line interface. However, it may also serve as basic configuration initializer for use by other
* classes.
* <p>
* The main method of this class relies on the fact that it has been executed and loaded from within a runnable
* jar archive containing a manifest file consisting of basic project information.
* By default, i.e. without providing any command line arguments, the main configuration will be looked up at the
* following locations:
* <ul>
* <li>File named "rubanetra.conf" in the classpath and as a relative path</li>
* <li>"./conf/rubanetra.conf" in the classpath and as relative path</li>
* <li>Value of environment variable "RUBANETRA_SYSTEM_CONF_FILE" interpreted as file path.</li>
* </ul>
* <p>
* If the file could not be located successfully an exception will be thrown.
* Alternatively, the main configuration file path may be provided as command line argument using
* the switch '-c' or '--configuration-file'.
* <p>
* During the initialization procedure the ANTLR configuration parser will be invoked. Any encountered syntax errors
* will be printed to the standard error stream, however, unless any critical errors during the setup process
* occurred the application will continue running.
* <p>
* After completing the initialization procedure the control over the program flow will be returned
* to the caller in the main method, who in turn is able to retrieve the parsed and validated @{RubanetraSystemConfiguration}.
* By default, invoking the start-method
* of this class will initialize a default @{DroolsKrakenProtocolHandler} which effectively passes all PCAP-entries
* to the Kraken protocol decoding pipeline and ultimately to the Drools rule engine.
*
* @see at.jku.fim.rubanetra.drools.DroolsKrakenProtocolHandler
* @see at.jku.fim.rubanetra.config.model.impl.RubanetraSystemConfigurationBuilder
* @see at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration
*/
public class ConfigurationController {
public static final String defaultMainConfigName = "rubanetra.conf";
public static final String defaultMainConfigPath = "./conf/" + defaultMainConfigName;
public static final String defaultMainConfigEnvVar = "RUBANETRA_SYSTEM_CONF_FILE";
private static final Logger log = LoggerFactory.getLogger(ConfigurationController.class);
private static final Option helpOption = new Option("h", "help", false, "print the option overview message");
private static final Option versionOption = new Option("v", "version", false, "print the version information");
private static final Option mainConfigFileOption = new Option("c", "configuration-file", true, "path to the main configuration file");
private static final Option inputFiles = new Option("i", "input-files", true, String.format("pcap input files, delimited by '%s' (complements config file)", File.pathSeparatorChar));
private static final Option outputFile = new Option("o", "output-file", true, "path to the output file (contents will be overridden)");
private static final Option outputType = new Option("t", "output-type", true, "output type/format of the generated content (PLASO, CSV, JSON, XML or NOP)");
private final RubanetraSystemConfigurationBuilder systemConfigBuilder;
private RubanetraSystemConfiguration systemConfiguration;
private final LinkedList<String> inputFilePaths = new LinkedList<>();
private String outputFilePath;
private String outputFormat;
/**
* Creates a new instance of itself and a {@link at.jku.fim.rubanetra.config.model.impl.RubanetraSystemConfigurationBuilder}
*/
public ConfigurationController() {
systemConfigBuilder = RubanetraSystemConfigurationBuilder.create();
}
/**
* @return the default command line options, i.e. help (h), version (v), configuration-file (c), input-files (i),
* output-file (o), output-type (t)
*/
public static Options createDefaultOptions() {
inputFiles.setArgs(Option.UNLIMITED_VALUES);
inputFiles.setValueSeparator(File.pathSeparatorChar);
Options options = new Options();
options.addOption(helpOption);
options.addOption(versionOption);
options.addOption(mainConfigFileOption);
options.addOption(inputFiles);
options.addOption(outputFile);
options.addOption(outputType);
return options;
}
/**
* Creates a {@link at.jku.fim.rubanetra.config.ConfigurationController} using default options
* {@link #createDefaultOptions()} and a {@link org.apache.commons.cli.GnuParser} to parse the command line
* arguments.
* After a successful parsing process, {@link ConfigurationController#start()}
* will be invoked.
*
* @param args command line arguments as defined by {@link #createDefaultOptions()}
*/
public static void main(String[] args) {
ConfigurationController controller = new ConfigurationController();
Options defaultOptions = ConfigurationController.createDefaultOptions();
CommandLineParser cliParser = new GnuParser();
CommandLine cli;
try {
cli = cliParser.parse(defaultOptions, args);
} catch (ParseException e) {
log.error("Unable to process parsed arguments: ", e);
printHelp(defaultOptions);
return;
}
if (cli.hasOption(ConfigurationController.helpOption.getOpt())) {
printHelp(defaultOptions);
return;
} else if (cli.hasOption(ConfigurationController.versionOption.getOpt())) {
printVersion();
return;
}
if (cli.hasOption(ConfigurationController.inputFiles.getOpt())) {
String[] inputFileValues = cli.getOptionValues(ConfigurationController.inputFiles.getOpt());
controller.addInputFiles(inputFileValues);
}
if (cli.hasOption(ConfigurationController.outputFile.getOpt())) {
String outputFileValue = cli.getOptionValue(ConfigurationController.outputFile.getOpt());
controller.setOutputFile(outputFileValue);
}
if (cli.hasOption(ConfigurationController.outputType.getOpt())) {
String outputType = cli.getOptionValue(ConfigurationController.outputType.getOpt());
controller.setOutputFormat(outputType);
}
boolean initialized;
String mainConfigurationPath = cli.getOptionValue(ConfigurationController.mainConfigFileOption.getOpt());
if (mainConfigurationPath == null) {
// config was not provided via command line arg
initialized = controller.initialize();
} else {
initialized = controller.initialize(mainConfigurationPath);
}
if (!initialized || controller.getRubanetraSystemConfiguration() == null) {
log.error("Unable to initialize the system: configuration file does either not exist or it is not readable or it is not valid.");
return;
}
log.info("System configuration has been initialized successfully, starting the rule engine and input file parsing process.");
controller.start();
}
private static void printVersion() {
Package classPackage = ConfigurationController.class.getPackage();
StringBuilder strBuilder = new StringBuilder();
Attributes manifestAttributes = getMainAttributes();
if (manifestAttributes != null) {
strBuilder.append(String.format("%s %s%n", classPackage.getImplementationTitle(), classPackage.getImplementationVersion()));
String licShort = manifestAttributes.getValue("License-Short-Name");
String licLong = manifestAttributes.getValue("License-Long-Name");
String licHeaderLine = manifestAttributes.getValue("License-Short-Header");
String licUrl = manifestAttributes.getValue("License-Url");
String licInceptionYear = manifestAttributes.getValue("License-Inception-Year");
String licRecentYears = manifestAttributes.getValue("License-Recent-Years");
String copyrightOwner = manifestAttributes.getValue("Copyright-Owner");
strBuilder.append(String.format("Copyright (C) %s,%s %s%n", licInceptionYear, licRecentYears, copyrightOwner));
strBuilder.append(String.format("License %s: %s <%s>%n", licShort, licLong, licUrl));
String[] headerSplit = licHeaderLine.split("\\.\\s", 2);
String headerML = headerSplit.length == 2 ?
String.format("%s.%n%s%n", headerSplit[0], headerSplit[1]) : String.format("%s%n", licHeaderLine);
strBuilder.append(headerML);
} else {
strBuilder.append("Unknown version.%n");
}
System.out.println(strBuilder.toString());
}
private static void printHelp(Options defaultOptions) {
Package classPackage = ConfigurationController.class.getPackage();
HelpFormatter formatter = new HelpFormatter();
Attributes manifestAttributes = getMainAttributes();
String cmdLineSyntax;
try {
cmdLineSyntax = Paths.get(ConfigurationController.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getFileName().toString();
} catch (URISyntaxException e) {
cmdLineSyntax = String.format("./rubanetra-%s.jar", classPackage.getImplementationVersion());
}
formatter.printHelp(String.format("java -jar %s <args>", cmdLineSyntax), defaultOptions);
if (manifestAttributes != null) {
StringBuilder strBuilder = new StringBuilder();
strBuilder.append(String.format("%nReport bugs to: <%s> or <stefan_rubanetra@swerk.priv.at>%n",
manifestAttributes.getValue("Issue-Management")));
strBuilder.append(String.format("Project home page: <%s>%n", manifestAttributes.getValue("Project-Home")));
System.out.println(strBuilder.toString());
}
}
/**
* @return the main attributes of the invoked JAR's manifest, containing at least "Implementation-Title",
* "Implementation-Version" and "Implementation-Vendor" attributes or null, if the manifest was not found
*/
public static Attributes getMainAttributes() {
Package classPackage = ConfigurationController.class.getPackage();
try {
Enumeration<URL> resourceEnum = ClassLoader.getSystemResources("META-INF/MANIFEST.MF");
while (resourceEnum.hasMoreElements()) {
try {
URL nextManifestUrl = resourceEnum.nextElement();
Manifest manifest = new Manifest(nextManifestUrl.openStream());
Attributes manifestAttributes = manifest.getMainAttributes();
String mainClassVal = manifestAttributes.getValue("Main-Class");
String implementationTitleVal = manifestAttributes.getValue("Implementation-Title");
String implementationVersionVal = manifestAttributes.getValue("Implementation-Version");
String implementationVendorVal = manifestAttributes.getValue("Implementation-Vendor");
if (mainClassVal != null && mainClassVal.equals(ConfigurationController.class.getCanonicalName())
&& classPackage.getImplementationTitle().equals(implementationTitleVal)
&& classPackage.getImplementationVendor().equals(implementationVendorVal)
&& classPackage.getImplementationVersion().equals(implementationVersionVal)) {
return manifestAttributes;
}
} catch (IOException e) {
log.warn("Unable to open manifest, skipping...", e);
}
}
} catch (IOException e) {
log.warn("Unable to find any manifest", e);
}
return null;
}
/**
* This method initializes the system invoking a parser for the main configuration file
* denoted by mainConfigFile. It tries to canonicalize all encountered file paths and will fail if it is unable
* to create a canonical representation.
* It relies on {@link at.jku.fim.rubanetra.config.RubanetraSystemConfigurationParser} and
* {@link at.jku.fim.rubanetra.config.model.impl.RubanetraSystemConfigurationBaseListenerImpl} for parsing
* functionality and builds the final {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration}
* object which can be obtained iff this method returned true via {@link #getRubanetraSystemConfiguration()}.
*
* @param mainConfigFile the main configuration file, which may not be null
* @return true, iff a main configuration instance was created (one may obtain this reference by calling
* {@link #getRubanetraSystemConfiguration()}.
* @throws java.lang.NullPointerException if the main config file is a null pointer
* @throws java.lang.IllegalArgumentException if the main config file is not valid
*/
public boolean initialize(File mainConfigFile) {
if (mainConfigFile == null) {
throw new NullPointerException("The main configuration file must not be a null pointer");
}
try {
mainConfigFile = mainConfigFile.getCanonicalFile();
} catch (IOException e) {
log.error("Unable to canonicalize file {}", mainConfigFile);
return false;
}
if (!checkMainConfigFile(mainConfigFile)) {
return false;
}
log.info("Trying to load main configuration from file {}", mainConfigFile);
RubanetraSystemConfigurationParser systemConfigParser = RubanetraSystemConfigurationBaseListenerImpl.createConfigurationFileParser(mainConfigFile);
RubanetraSystemConfigurationBaseListenerImpl systemConfigBaseListener = new RubanetraSystemConfigurationBaseListenerImpl(systemConfigBuilder);
RubanetraSystemConfigurationParser.SystemContext systemContext = systemConfigParser.system();
if (systemContext == null) {
throw new IllegalArgumentException("Invalid configuration file.");
}
String errorMsg = "Exactly one %s configuration section required, %d have been found";
int generalConfigurations = systemContext.generalConfig().size();
int inputConfigurations = systemContext.inputConfig().size();
int outputConfigurations = systemContext.outputConfig().size();
int protocolConfigurations = systemContext.protocolConfig().size();
if (generalConfigurations != 1)
throw new IllegalArgumentException(String.format(errorMsg, "general", generalConfigurations));
if (inputConfigurations != 1)
throw new IllegalArgumentException(String.format(errorMsg, "input", inputConfigurations));
if (outputConfigurations != 1)
throw new IllegalArgumentException(String.format(errorMsg, "output", outputConfigurations));
if (protocolConfigurations != 1)
throw new IllegalArgumentException(String.format(errorMsg, "protocol", protocolConfigurations));
/*
The general section should be parsed first, as it could contain vital information,
followed by the I/O configuration. Due to potentially existing file path entries in those sections and the
possibility of a redundant path specification via command line arguments, these settings must be adjusted
accordingly, i.e. either complemented or replaced altogether before the Pcap handler can be built, since the
protocol configuration depends on an already existing Pcap handler in order to setup listeners.
*/
ParseTreeWalker.DEFAULT.walk(systemConfigBaseListener, systemContext.generalConfig(0));
ParseTreeWalker.DEFAULT.walk(systemConfigBaseListener, systemContext.inputConfig(0));
ParseTreeWalker.DEFAULT.walk(systemConfigBaseListener, systemContext.outputConfig(0));
complementOrReplaceExistingConfig();
systemConfigBuilder.createPcapHandler();
ParseTreeWalker.DEFAULT.walk(systemConfigBaseListener, systemContext.protocolConfig(0));
systemConfiguration = systemConfigBuilder.build();
return true;
}
/**
* Tries to either complement (input files) or replace (output file/type) the existing configuration parsed from
* the main config files by the provided command line arguments.
*/
private void complementOrReplaceExistingConfig() {
if (this.inputFilePaths.size() > 0) {
if (!systemConfigBuilder.getInputFiles().isEmpty()) {
log.warn("The configuration file contains at least one reference to an input file, complementing with" +
" specified command line input file(s)");
}
for (String inputFilePath : this.inputFilePaths) {
systemConfigBuilder.addInputPath(inputFilePath);
}
}
if (this.outputFilePath != null) {
final OutputStream oldOutputStream = systemConfigBuilder.getOutputStream();
if (oldOutputStream != null) {
log.warn("The configuration file contains at least one reference to an output stream ... replacing it");
if (oldOutputStream != System.out && oldOutputStream != System.err) {
try {
oldOutputStream.close();
} catch (IOException e) {
log.warn("IOException while trying to close the replaced output stream", e);
}
}
}
if (outputFilePath.equalsIgnoreCase("stdout") || outputFilePath.equals("-")) {
systemConfigBuilder.setOutputStream(System.out);
} else {
systemConfigBuilder.setOutputFile(outputFilePath);
}
}
if (this.outputFormat != null) {
if (systemConfigBuilder.getOutputFormat() != null) {
log.warn("The configuration file contains at least one reference to an output format ... replacing it");
}
systemConfigBuilder.setOutputFormat(outputFormat);
}
}
/**
* Convenience method for {@link #initialize(java.io.File)}.
* It creates a file based on the given path.
*
* @param mainConfigurationPath the path of the main configuration file
* @return true, iff the configuration could be parsed (one may obtain a reference to the configuration object by
* calling {@link #getRubanetraSystemConfiguration()}.
*/
public boolean initialize(String mainConfigurationPath) {
File systemConfigurationFile = createFileFromResourceString(mainConfigurationPath);
if (systemConfigurationFile == null) {
systemConfigurationFile = getCanonicalFile(mainConfigurationPath);
}
return initialize(systemConfigurationFile);
}
/**
* This method initializes the system invoking a parser for the main configuration file
* denoted by (in processing order):
* <ul>
* <li>"rubanetra.conf" in the classpath and working directory</li>
* <li>"./conf/rubanetra.conf" in the classpath and working directory</li>
* <li>path denoted by environment variable FNA_SYSTEM_CONF_FILE in the class path and working directory</li>
* </ul>
* It tries to canonicalize all encountered file paths and will fail if it is unable
* to create a canonical representation.
* It relies on {@link at.jku.fim.rubanetra.config.RubanetraSystemConfigurationParser} and
* {@link at.jku.fim.rubanetra.config.model.impl.RubanetraSystemConfigurationBaseListenerImpl} for parsing
* functionality and builds the final {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration}
* object which can be obtained iff this method returned true via {@link #getRubanetraSystemConfiguration()}.
*
* @return true, iff a main configuration instance was created (one may obtain this reference by calling
* {@link #getRubanetraSystemConfiguration()}.
* @throws java.lang.NullPointerException if the main config file is a null pointer
* @throws java.lang.IllegalArgumentException if the main config file is not valid
*/
public boolean initialize() {
boolean initialized;
log.info("Trying to look up {} in the classpath and the working directory", defaultMainConfigName);
// try the default name
initialized = initialize(defaultMainConfigName);
if (!initialized) {
log.info("Trying to look up {} in the classpath and the working directory", defaultMainConfigPath);
// try the default full path
initialized = initialize(defaultMainConfigPath);
}
if (!initialized) {
log.info("Trying to look up path denoted by environment variable {} in the classpath and the working directory",
defaultMainConfigEnvVar);
// try the default env variable
initialized = initializeFromEnvVar(defaultMainConfigEnvVar);
}
return initialized;
}
/**
* Tries to look up the value of the environment variable denoted by envVariableName, and interprets it as
* main configuration path ({@link #initialize(java.io.File)} will be called)
*
* @param envVariableName the name of the environment variable to look up (the value should represent the main
* configuration file path)
* @return true, iff a main configuration instance was created (one may obtain this reference by calling
* {@link #getRubanetraSystemConfiguration()} afterwards.
*/
public boolean initializeFromEnvVar(String envVariableName) {
// try environment variable
log.info("Trying to load main configuration path from environment variable {}", envVariableName);
File systemConfigurationFile = createFileFromEnvVariable(envVariableName);
return initialize(systemConfigurationFile);
}
/**
* Tries to represent the path denoted by mainConfigurationPath canonically.
*
* @param mainConfigurationPath the file to canonicalize.
* @return the file denoted by mainConfigurationPath using a canonical representation or null, if not possible
*/
public File getCanonicalFile(String mainConfigurationPath) {
File systemConfigurationFile;
try {
systemConfigurationFile = new File(mainConfigurationPath).getCanonicalFile();
} catch (IOException e) {
log.warn("Unable to canonicalize the configuration file path");
return null;
}
return systemConfigurationFile;
}
private File createFileFromEnvVariable(String optionalMainConfigEnvVar) {
String mainConfigPath;
try {
mainConfigPath = System.getenv(optionalMainConfigEnvVar);
} catch (SecurityException e) {
log.warn("Unable to access environment variable", e);
return null;
}
if (mainConfigPath == null) {
log.warn("Unable to load configuration file via environment variable.");
} else {
File fileFromResourceString = createFileFromResourceString(mainConfigPath);
return fileFromResourceString == null ? getCanonicalFile(mainConfigPath) : fileFromResourceString;
}
return null;
}
/**
* Tries to create a file object by looking up filePath in the current classloader's classpath.
*
* @param filePath the path of the file in the current classpath
* @return a file object for filePath, or null if not found
*/
public File createFileFromResourceString(String filePath) {
log.info("Trying to load main configuration as classpath resource: {}", filePath);
URL fileUrl = ClassLoader.getSystemResource(filePath);
if (fileUrl == null) {
log.warn("Unable to find configuration file via class path resource loader: {}", filePath);
return null;
}
File file;
try {
file = new File(fileUrl.toURI());
} catch (URISyntaxException e) {
file = new File(filePath);
}
return file;
}
/**
* Performs a simple file metadata validation (is it readable, is it a file),
* however, it does not verify the contents of the file itself.
*
* @param file the file to check
* @return true, iff the file is not a directory and is readable and is not null
*/
public boolean checkMainConfigFile(File file) {
if (file != null && !file.isDirectory() && file.canRead()) {
return true;
}
log.warn("Cannot read configuration file {}", file);
return false;
}
/**
* Adds files containing network captures to the configuration builder
* {@link at.jku.fim.rubanetra.config.model.impl.RubanetraSystemConfigurationBuilder}.
* <p>
* This method may not be invoked after the initialization step has been completed, i.e. this method must be
* invoked before {@link #initialize(java.io.File)}.
*
* @param filePaths a number of file paths containing network captures in the specified
* {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration.InputFormat}
*/
public void addInputFiles(String... filePaths) {
checkUninitialized();
if (filePaths == null) {
log.error("Invalid command line input file specification.");
throw new IllegalArgumentException();
}
for (String filePath : filePaths) {
if (filePath == null || filePath.isEmpty()) {
log.warn("Input file path passed from command line appears to be null/empty - ignoring");
} else {
inputFilePaths.addLast(filePath);
}
}
}
/**
* Sets the default output file for the derived and parsed data using
* {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration.OutputFormat}
* <p>
* This method may not be invoked after the initialization step has been completed, i.e. this method must be
* invoked before {@link #initialize(java.io.File)}.
*
* @param outputFilePath the path of the default output file
*/
public void setOutputFile(String outputFilePath) {
checkUninitialized();
if (outputFilePath == null || outputFilePath.isEmpty()) {
log.error("Output file path passed from command line appears to be null/empty");
throw new IllegalArgumentException();
}
this.outputFilePath = outputFilePath;
}
/**
* Sets the {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration.OutputFormat} to apply
* as serialization variant.
* <p>
* This method may not be invoked after the initialization step has been completed, i.e. this method must be
* invoked before {@link #initialize(java.io.File)}.
*
* @param outputFormat the String representation of a {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration.OutputFormat}
*/
public void setOutputFormat(String outputFormat) {
checkUninitialized();
if (outputFormat == null || outputFormat.isEmpty()) {
log.error("Output format path passed from command line appears to be null/empty");
throw new IllegalArgumentException();
}
this.outputFormat = outputFormat;
}
private void checkUninitialized() {
if (this.systemConfiguration != null) {
log.error("This system configuration has already been initialized, unable to add additional input files");
throw new IllegalStateException();
}
}
/**
* @return this method will always return null, unless one of the {@link #initialize(java.io.File)} methods was invoked
* successfully, then it will return the constructed {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration}.
*/
public RubanetraSystemConfiguration getRubanetraSystemConfiguration() {
return systemConfiguration;
}
/**
* This method will do nothing, unless one of the {@link #initialize(java.io.File)} methods has been invoked
* successfully.
* <p>
* If a valid {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration} is encountered,
* a {@link at.jku.fim.rubanetra.drools.DroolsKrakenProtocolHandler} will be constructed and
* {@link at.jku.fim.rubanetra.pcap.PcapHandler#readNextPcapEntry(at.jku.fim.rubanetra.pcap.PcapActivityListener)}
* will be called as long as it returns true (using an infinite loop), i.e. until all network packets have been read
* from the input files. Afterwards, it will try to close all opened input and output streams, that is it will call
* {@link at.jku.fim.rubanetra.drools.DroolsKrakenProtocolHandler#close()}
* and {@link at.jku.fim.rubanetra.pcap.PcapHandler#close()}.
* Ultimately the existing reference to the {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration}
* will be erased, therefore allowing for a subsequent initialization call while reusing the same object.
*/
public void start() {
if (this.systemConfiguration == null || systemConfiguration.getPcapHandler() == null) {
log.error("System configuration has not been initialized completely.");
return;
}
DroolsKrakenProtocolHandler droolsKrakenProtocolHandler = new DroolsKrakenProtocolHandler(systemConfiguration);
while (true) {
try {
final boolean processingSuccessful = (systemConfiguration.getPcapHandler().readNextPcapEntry(droolsKrakenProtocolHandler));
if (!processingSuccessful) break;
} catch (Exception e) {
log.debug("Catch all exception block was entered", e);
}
}
try {
droolsKrakenProtocolHandler.close();
} catch (IOException e) {
log.warn("Exception occurred while trying to close the Drools/Kraken Protocol Handler:", e);
} finally {
this.systemConfiguration.getPcapHandler().close();
this.systemConfiguration = null;
}
}
}
@@ -0,0 +1,34 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.config.model;
import org.jnetpcap.protocol.JProtocol;
/**
* This interface serves as an identifier for a potential {@link org.jnetpcap.protocol.JProtocol}.
* Since the current system utilizes the Kraken-library as main reference point, this interface is currently a
* placeholder for future refinements.
*/
public interface JNetPcapProtocolId extends ProtocolId {
/**
* @return the represented {@link org.jnetpcap.protocol.JProtocol}
*/
JProtocol getJNetPcapProtocol();
}
@@ -0,0 +1,33 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.config.model;
import org.krakenapps.pcap.Protocol;
/**
* Implementations of this interface serve as identifiers for potential {@link org.krakenapps.pcap.Protocol}s, i.e.
* the application layer specific Kraken-library protocol identifier.
*/
public interface KrakenApplicationProtocolId extends ProtocolId {
/**
* @return the represented Kraken-library specific {@link org.krakenapps.pcap.Protocol}
*/
public abstract Protocol getKrakenApplicationProtocol();
}
@@ -0,0 +1,111 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.config.model;
import at.jku.fim.rubanetra.pcap.PcapActivityListener;
import at.jku.fim.rubanetra.pcap.PcapHandler;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.mapper.KrakenTransportLayerMappingFactory;
import java.util.Map;
/**
* This interface specifies methods that may be used to represent the configuration of a single application layer
* protocol of the Kraken-library. A {@link at.jku.fim.rubanetra.config.model.impl.KrakenProtocolConfigurationBuilderImpl}
* should be used to obtain an implementation of this interface.
* <p>
* Due to the current design of the Kraken-PCAP-library all application layer protocols
* rely on either a UDP ({@link org.krakenapps.pcap.decoder.udp.UdpProtocolMapper}) or a TCP
* ({@link org.krakenapps.pcap.decoder.tcp.TcpProtocolMapper}) protocol mapper.
* One of the design goals of this framework is the abstraction of this mechanism to enable customizations that do
* not require modifications of existing application layer parsers (e.g. HTTP over a custom transport layer protocol
* or multiple ports).
* A first step to achieve this goal is the introduction of a generic
* {@link at.jku.fim.rubanetra.protocol.mapper.TransportLayerMappingStrategy}.
* However, currently only a port/service based approach has been implemented. Since Kraken apparently does not allow
* the use of multiple ports per application layer parser (since the TCP/UDP-Mapper would override the bindings with
* the default parser), a custom {@link at.jku.fim.rubanetra.config.model.PortSpecification} approach
* was implemented on an application layer basis, i.e. a single application layer protocol may be bound to a
* Kraken-application layer parser for a single, multiple, all or no port(s).
* However, as soon as JNetPcap provides suitable application layer parsers for the currently supported protocols,
* this mechanism should be replaced by JNetPcap-bindings which is more elegant and convenient.
* This interface is implemented by
* {@link at.jku.fim.rubanetra.config.model.impl.KrakenProtocolConfigurationBuilderImpl.KrakenProtocolConfigurationImpl}.
*/
public interface KrakenProtocolConfiguration {
/**
* The underlying source of all PCAP-packets for this application layer protocol. A listener will be attached
* by default to listen for new {@link at.jku.fim.rubanetra.pcap.PcapActivity}-objects.
* All parsed PCAP-packets will be either parsed by a Kraken-Application layer parser for this protocol or
* discarded by the transport layer mapping strategy and/or port-specification.
*
* @return the {@link at.jku.fim.rubanetra.pcap.PcapHandler} to use as input stream for
* {@link at.jku.fim.rubanetra.pcap.PcapActivity}-objects.
* @see #getTransportLayerMappingStrategy()
* @see #getPortSpecification()
*/
public abstract PcapHandler<PcapActivityListener> getPcapHandler();
/**
* Represents a primitive protocol to Kraken-protocol decoder mapping mechanism.
* A network protocol within this framework is always identified by a
* {@link at.jku.fim.rubanetra.config.model.ProtocolId}. Therefore a mapping of a protocol to
* an appropriate Kraken-protocol decoder/parser is needed. All
* {@link at.jku.fim.rubanetra.protocol.KrakenBaseProtocol} implementations provide a method to
* retrieve a suitable identifier
* {@link at.jku.fim.rubanetra.protocol.KrakenBaseProtocol#getProtocolId()}.
* These bindings may be configured by using a
* {@link at.jku.fim.rubanetra.config.model.impl.KrakenProtocolConfigurationBuilderImpl}.
*
* @return the immutable copy of all application layer protocol bindings
*/
public abstract Map<ProtocolId, KrakenBaseProtocol> getBoundProtocols();
/**
* The transport layer mapping strategy to use for this Kraken-application layer protocol decoder.
*
* @return the transport layer protocol mapping strategy identifier which may be used to obtain an instance of
* a {@link at.jku.fim.rubanetra.protocol.mapper.TransportLayerMappingStrategy} by using
* {@link at.jku.fim.rubanetra.protocol.mapper.KrakenTransportLayerMappingFactory}.
*/
public abstract String getTransportLayerMappingStrategy();
/**
* The specification of a number of {@link at.jku.fim.rubanetra.config.model.PortRange}s or
* {@link at.jku.fim.rubanetra.config.model.Port}s which filters packets at the transport layer
* for this application layer protocol. A simpler alternative to this method is the use of
* {@link at.jku.fim.rubanetra.config.model.PortSpecification.UniversalPortSpecification} and
* by restricting all transport layer packets globally via a BP-Filter,
* see {@link at.jku.fim.rubanetra.pcap.PcapHandler}.
*
* @return the {@link at.jku.fim.rubanetra.config.model.PortSpecification} as a transport layer
* filter for this application layer protocol only
* @see at.jku.fim.rubanetra.config.model.PortSpecification.UniversalPortSpecification
*/
public abstract PortSpecification getPortSpecification();
/**
* This {@link at.jku.fim.rubanetra.protocol.mapper.KrakenTransportLayerMappingFactory} may be used
* by the {@link at.jku.fim.rubanetra.protocol.KrakenBaseProtocol} parsers
* to obtain instances of the configured {@link #getTransportLayerMappingStrategy()}.
*
* @return a factory to obtain instances of the transport layer mapping strategy
*/
public abstract KrakenTransportLayerMappingFactory getTransportLayerMappingFactory();
}
@@ -0,0 +1,88 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.config.model;
import at.jku.fim.rubanetra.pcap.PcapActivityListener;
import at.jku.fim.rubanetra.pcap.PcapHandler;
import at.jku.fim.rubanetra.protocol.mapper.KrakenTransportLayerMappingFactory;
/**
* Generic specification of a Kraken application protocol configuration builder.
*/
public interface KrakenProtocolConfigurationBuilder {
/**
* Builds the protocol configuration that has been set up until now
* @return the immutable protocol configuration that has been set up until now
*/
public abstract KrakenProtocolConfiguration buildProtocolConfiguration();
/**
* Set a transport layer filter for the parser of this protocol
* @param transportProtocolPortSpec the port specification for this application layer protocol parser
* @return the builder instance
* @see at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration
*/
public abstract KrakenProtocolConfigurationBuilder setPortSpecification(PortSpecification transportProtocolPortSpec);
/**
* Set the transport layer mapping strategy to use for this protocol
* @param strategy the {@link at.jku.fim.rubanetra.protocol.mapper.TransportLayerMappingStrategy} to use
* @return the builder instance
* @see at.jku.fim.rubanetra.protocol.mapper.KrakenTransportLayerMappingFactory
*/
public abstract KrakenProtocolConfigurationBuilder setTransportProtocolMappingStrategy(String strategy);
/**
* Set the transport layer mapping factory to use for building transport layer mapping strategies
* @param mappingFactory the transport layer mapping factory to use
* @return the builder instance
*/
public abstract KrakenProtocolConfigurationBuilder setTransportProtocolMappingFactory(KrakenTransportLayerMappingFactory mappingFactory);
/**
* Set the pcap packet input stream for this application layer protocol parser
* @param pcapHandler the Pcap packet input stream
* @return the builder instance
*/
public abstract KrakenProtocolConfigurationBuilder setPcapHandler(PcapHandler<PcapActivityListener> pcapHandler);
/**
* Resets all settings of the current protocol configuration
*/
public abstract void resetCurrentProtocolConfiguration();
/**
* Bind a protocol handler to another protocol handler, i.e. define the stream of decoded data
* (in general one way only, unless bound vice-versa as well)
* @param bindFrom the wild protocol identifier which can be mapped by the protocol registry to
* an existing protocol handler, and that will be bound to the handler of the bindTo protocol
* @param bindTo the wild protocol identifier which can be mapped by the protocol registry to
* an existing protocol handler, and that will receive decoded data from the bindFrom handler
*
* @return the builder instance
*/
public abstract KrakenProtocolConfigurationBuilder bindProtocol(String bindFrom, String bindTo);
/**
* Set the protocol identifier to use for this protocol configuration.
* @param currentProtocolIdentifier a protocol identifier (should be unique within this framework)
* @return the builder instance
*/
public abstract KrakenProtocolConfigurationBuilder setCurrentProtocolIdentifier(String currentProtocolIdentifier);
}
@@ -0,0 +1,102 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.config.model;
import java.util.HashMap;
import java.util.Map;
/**
* A transport layer TCP/UDP port number.
*/
public class Port implements Comparable<Port> {
/**
* the lower bound of the available port number pool
*/
public static final int LOWER_BOUND = 0;
/**
* the upper bound of the available port number pool
*/
public static final int UPPER_BOUND = 65535;
private static final Map<Integer, Port> numberPortMap = new HashMap<>();
private int portNumber;
/**
* Private constructor, use {@link #create(int)} instead
*
* @throws java.lang.IllegalArgumentException if the port-number is not within the allowed range
*/
private Port(int portNumber) {
checkPortNumber(portNumber);
this.portNumber = portNumber;
}
/**
* Create a new {@link at.jku.fim.rubanetra.config.model.Port}
*
* @param portNumber the port number to use
* @return a new {@link at.jku.fim.rubanetra.config.model.Port} or
* an instance of an already instantiated port.
* @throws java.lang.IllegalArgumentException if the port-number is not within the allowed range
*/
public static Port create(int portNumber) {
if (!numberPortMap.containsKey(portNumber)) {
numberPortMap.put(portNumber, new Port(portNumber));
}
return numberPortMap.get(portNumber);
}
/**
* This method is equivalent to calling {@link #create(int)}.
* Create a new {@link at.jku.fim.rubanetra.config.model.Port}
*
* @param portNumber the port number to use
* @return a new {@link at.jku.fim.rubanetra.config.model.Port} or
* an instance of an already instantiated port.
* @throws java.lang.IllegalArgumentException if the port-number is not within the allowed range
*/
public static Port getPort(int portNumber) {
return create(portNumber);
}
private void checkPortNumber(int portNumber) {
if (portNumber < LOWER_BOUND || portNumber > UPPER_BOUND) {
throw new IllegalArgumentException(String.format("Port %d is out of range", portNumber));
}
}
/**
* @return the represented port number
*/
public int getPortNumber() {
return portNumber;
}
@Override
public int compareTo(Port otherPort) {
return Integer.compare(getPortNumber(), otherPort.getPortNumber());
}
@Override
public String toString() {
return String.valueOf(portNumber);
}
}
@@ -0,0 +1,95 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.config.model;
/**
* A port range is defined by an interval that is bound by two ports, i.e.
* [lowerPort;upperPort], both bounds are inclusive.
*/
public class PortRange {
private Port lowerPort;
private Port higherPort;
/**
* Create a new port range, which represents all possible ports within the interval [lowerPort;upperPort], inclusive
*
* @param lowerPort the lower bound of the range, must be less than higherPort.
* @param higherPort the upper bound of the range, must be greater than lowerPort.
* @throws java.lang.IllegalArgumentException if invalid arguments are passed
*/
public PortRange(Port lowerPort, Port higherPort) {
this.lowerPort = lowerPort;
this.higherPort = higherPort;
checkPortRange(lowerPort, higherPort);
}
private void checkPortRange(Port lowerPort, Port higherPort) {
if (lowerPort == null || higherPort == null) {
throw new IllegalArgumentException();
}
if (lowerPort.compareTo(higherPort) >= 0) {
throw new IllegalArgumentException();
}
}
/**
* Checks whether the given port is within this range, i.e. port in [lowerPort;higherPort]
* @param port to check
* @return true, iff if port is in this range
* @throws java.lang.IllegalArgumentException if the port is null
*/
public boolean isWithinRange(Port port) {
if (port == null) {
throw new IllegalArgumentException();
}
return port.compareTo(getLowerPort()) >= 0 && port.compareTo(getHigherPort()) <= 0;
}
/**
* @return the upper bound of this range
*/
public Port getHigherPort() {
return higherPort;
}
/**
* Sets the upper bound of this range
* @param higherPort the upper bound of the range, must be greater than lowerPort.
*/
public void setHigherPort(Port higherPort) {
checkPortRange(lowerPort, higherPort);
this.higherPort = higherPort;
}
/**
* @return the lower bound of this range
*/
public Port getLowerPort() {
return lowerPort;
}
/**
* Sets the lower bound of the range
* @param lowerPort the lower bound of the range, must be less than higherPort.
*/
public void setLowerPort(Port lowerPort) {
checkPortRange(lowerPort, higherPort);
this.lowerPort = lowerPort;
}
}
@@ -0,0 +1,141 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.config.model;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* This class constitutes a higher level specification of the mapping between an application layer protocol and
* the associated transport layer (TCP/UDP) port numbers and/or port ranges.
* It is utilized for a number of Kraken application layer protocol parser to transport layer mapping abstractions, e.g.
* {@link at.jku.fim.rubanetra.protocol.mapper.TransportLayerMappingStrategy}, and configured by
* an instance of {@link at.jku.fim.rubanetra.config.model.KrakenProtocolConfigurationBuilder} resulting
* in a {@link at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration} object.
* Note, that this implementation currently utilizes the Java 8 - Stream API for performing {@link at.jku.fim.rubanetra.config.model.PortRange}
* bound checking via predicates, see {@link #contains(Port)}.
* Additionally, the subclass {@link at.jku.fim.rubanetra.config.model.PortSpecification.UniversalPortSpecification}
* was defined for convenience reasons.
* This class is not thread-safe.
*/
public class PortSpecification {
private final Map<Integer, Port> portMap;
private final Set<PortRange> portRangeSet;
public PortSpecification() {
this.portMap = new HashMap<>();
this.portRangeSet = new HashSet<>();
}
/**
* Adds a single port to this specification.
* A previously defined port with the same port number will be overwritten silently.
*
* @param singlePort the port to add to this specification
* @throws java.lang.IllegalArgumentException if singlePort == null
*/
public void add(Port singlePort) {
if (singlePort == null) {
throw new IllegalArgumentException();
}
this.portMap.put(singlePort.getPortNumber(), singlePort);
}
/**
* Adds a new port-range to this specification.
* A {@link java.util.HashSet} is used internally to store the port ranges.
*
* @param portRange the port range to add to this specification
* @throws java.lang.IllegalArgumentException if the portRange is null
*/
public void add(PortRange portRange) {
if (portRange == null) {
throw new IllegalArgumentException();
}
this.portRangeSet.add(portRange);
}
/**
* This is a convenience method for {@link #contains(Port)}.
* A new {@link at.jku.fim.rubanetra.config.model.Port} is constructed for the passed
* integer argument.
*
* @param destinationPort the port number to check against this specification
* @return false, if the given port was null or if it does not conform to the bounds of this specification,
* true, iff the given port is backed by either a port-range or a single port number.
* @throws java.lang.IllegalArgumentException if the port number is invalid
* @see #contains(Port)
*/
public boolean contains(int destinationPort) {
Port p = Port.getPort(destinationPort);
return contains(p);
}
/**
* Performs a bound check according to the underlying specification.
* A port adheres to the specification iff it was specified as a single {@link at.jku.fim.rubanetra.config.model.Port}
* or any specified {@link at.jku.fim.rubanetra.config.model.PortRange} returns true by calling
* {@link at.jku.fim.rubanetra.config.model.PortRange#isWithinRange(Port)}.
* A Java 8 predicate check via the new stream-API is currently used for this check.
*
* @param destinationPort the port to check against this specification
* @return false, if the given port was null or if it does not conform to the bounds of this specification,
* true, iff the given port is backed by either a port-range or a single port number.
*/
public boolean contains(Port destinationPort) {
if (destinationPort == null) {
return false;
}
return this.portMap.containsKey(destinationPort.getPortNumber())
|| portRangeSet.stream().anyMatch(portRange -> portRange.isWithinRange(destinationPort));
}
/**
* A port specification that returns either always true or always false for any given port number.
*/
public static class UniversalPortSpecification extends PortSpecification {
public static final PortSpecification ACCEPT_ALL = new UniversalPortSpecification(true);
public static final PortSpecification REJECT_ALL = new UniversalPortSpecification(false);
private final boolean acceptPort;
/**
* Only two meaningful implementations available - either return true or false,
* therefore a public constructor was deemed to be unnecessary.
*/
private UniversalPortSpecification(boolean accept) {
super();
this.acceptPort = accept;
}
@Override
public boolean contains(int destinationPort) {
Port p = Port.getPort(destinationPort);
return contains(p);
}
@Override
public boolean contains(Port destinationPort) {
return destinationPort != null && acceptPort;
}
}
}
@@ -0,0 +1,42 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.config.model;
/**
* This interface represents a meta-identifier for network protocols of arbitrary layers and frameworks.
* Since the currently included network stack parser/decoder (Kraken/JNetPcap) use their own, partly incompatible
* protocol identifier mechanism, this interface tries to provide a common denominator which allows the identification
* of a single network protocol within the system's framework.
* The provided information of implementations is intended for internal usage only, i.e. the used identifiers are
* neither globally unique nor adhere to a specific standard/RFC.
* See {@link at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId} and
* {@link at.jku.fim.rubanetra.config.model.JNetPcapProtocolId}.
*/
public interface ProtocolId {
/**
* @return a unique identifier (within the framework) for the network protocol to represent
*/
public abstract String getProtocolId();
/**
* @return a human-readable name for the network protocol to represent
*/
public abstract String getName();
}
@@ -0,0 +1,137 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.config.model;
import at.jku.fim.rubanetra.output.OutputWriterStrategy;
import at.jku.fim.rubanetra.pcap.PcapFileHandler;
import org.kie.api.runtime.KieSession;
import java.io.File;
import java.io.OutputStream;
import java.util.Set;
/**
* The generic specification of the system configuration itself.
* This may be considered to constitute many of the essential settings, i.e. how and where to write the derived data, the Pcap
* packet input stream, the input files, the Drools knowledge session.
* Use a {@link at.jku.fim.rubanetra.config.model.impl.RubanetraSystemConfigurationBuilder} to obtain an implementation
* instance programmatically.
*/
public interface RubanetraSystemConfiguration {
/**
* The main output stream that is used to write all derived information to.
* The format of this data is defined by the {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration.OutputFormat},
* see{@link #getOutputFormat()}.
* The point of time that defines when the data is actually written to this stream, is controlled by the
* {@link at.jku.fim.rubanetra.output.OutputWriterStrategy}, see {@link #getOutputWriterStrategy()},
* and the Drools knowledge session (i.e. the Rule engine and custom rules).
*
* @return the main output stream of the derived information
*/
public OutputStream getOutputStream();
/**
* The format that is used to transform the derived data before it is written to the output-stream.
*
* @return the format to use for data transformation
* @see #getOutputStream()
*/
public OutputFormat getOutputFormat();
/**
* The writer strategy that is used to transform (using {@link #getOutputFormat()}) and finally write (using
* {@link #getOutputStream()}) the derived data (delivered from {@link #getKieSession()}).
*
* @return the defined writer strategy
* @see #getOutputStream()
* @see #getOutputFormat()
*/
public OutputWriterStrategy getOutputWriterStrategy();
/**
* The set of all defined protocol configurations, each representing the complete configuration of a Kraken protocol
* parser/decoder, containing protocol mappings (ETHERNET->IPv4->...->...).
*
* @return the immutable set of defined Kraken protocol parser configurations
*/
public Set<KrakenProtocolConfiguration> getProtocolSettings();
/**
* The Pcap packet input stream handler that is used to parse and react to Pcap-information
* from the set of defined input files.
*
* @return the pcap input stream handler
*/
public PcapFileHandler<?> getPcapHandler();
/**
* The set of input files possibly adhering to a PCAP-specification.
*
* @return the set of pcap input files used for parsing pcap-packets
*/
public Set<File> getInputFiles();
/**
* The format of all input files (currently always PCAP)
*
* @return the common input format of all files
*/
public InputFormat getInputFormat();
/**
* The Drools knowledge session reference, used to pass new facts to the rule engine
* decoded by the protocol parsers.
*
* @return the Drools knowledge session reference
*/
public KieSession getKieSession();
/**
* The enumeration of possible output formats, used for transforming derived data before it is written to the output
* stream.
*
* @see #getOutputFormat()
*/
public enum OutputFormat {
/**
* PLASO may be considered synonymous to XML,
*/
PLASO,
CSV,
JSON,
XML,
/**
* no-op, i.e. data will not be written at all
*/
NOP, CALLBACK
}
/**
* The enumeration of possible input formats, used for defining the appropriate input file handler without
* guessing
*
* @see #getInputFormat()
*/
public enum InputFormat {
PCAP
}
}
@@ -0,0 +1,334 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.config.model.impl;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfigurationBuilder;
import at.jku.fim.rubanetra.config.model.PortSpecification;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.pcap.PcapActivityListener;
import at.jku.fim.rubanetra.pcap.PcapHandler;
import at.jku.fim.rubanetra.protocol.BaseProtocol;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.mapper.KrakenTransportLayerMappingFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.stream.Collectors;
/**
* The default implementation of the {@link at.jku.fim.rubanetra.config.model.KrakenProtocolConfigurationBuilder}.
* This class may be used to configure protocol parsers from the Kraken-library.
*/
public class KrakenProtocolConfigurationBuilderImpl implements KrakenProtocolConfigurationBuilder {
private final Logger log = LoggerFactory.getLogger(getClass());
private final ProtocolClassRegistry protocolRegistry;
// a single protocol handler may forward data to multiple receivers, e.g. consider eth->ipv4, eth->ipv6
private final Map<ProtocolId, Set<ProtocolId>> currentProtocolBindingMap;
private PortSpecification currentProtocolPortSpec;
private KrakenTransportLayerMappingFactory currentTransportLayerMappingFactory;
private String currentTransportLayerStrategy;
private PcapHandler<PcapActivityListener> currentPcapHandler;
// the currently set protocol identifier for the protocol-config being built (arbitrary value, should be unique)
private String currentProtocolIdentifier;
/**
* Creates a new builder instance using a default protocol lookup registry by calling
* {@link ProtocolClassRegistry#createDefaultKrakenProtocolRegistry()}.
*/
public KrakenProtocolConfigurationBuilderImpl() {
this(ProtocolClassRegistry.createDefaultKrakenProtocolRegistry());
}
/**
* Creates a new builder instance using a custom protocol lookup registry.
* Be aware that all classes specified in the registry must define a default constructor.
*
* @param protocolRegistry the custom protocol registry to use for {@link at.jku.fim.rubanetra.config.model.ProtocolId}
* lookup
*/
public KrakenProtocolConfigurationBuilderImpl(ProtocolClassRegistry protocolRegistry) {
if (protocolRegistry == null) {
log.error("The protocol registry must not be null.");
throw new IllegalArgumentException();
}
this.protocolRegistry = protocolRegistry;
this.currentProtocolBindingMap = new HashMap<>();
resetCurrentProtocolConfiguration();
log.info("Decoder available for protocols: {}",
protocolRegistry.getRegisteredProtocolIdSet().stream().map(ProtocolId::getName).collect(Collectors.toList()));
}
/*
* (non-Javadoc)
*
* @see at.jku.fim.rubanetra.config.KrakenProtocolConfigurationBuilder#
* resetCurrentProtocolConfiguration()
*/
@Override
public void resetCurrentProtocolConfiguration() {
this.currentProtocolIdentifier = null;
this.currentPcapHandler = null;
this.currentProtocolPortSpec = new PortSpecification();
this.currentProtocolBindingMap.clear();
}
/*
* (non-Javadoc)
*
* @see at.jku.fim.rubanetra.config.KrakenProtocolConfigurationBuilder#bindProtocol
*
*/
@Override
public KrakenProtocolConfigurationBuilder bindProtocol(String protocolBindFrom, String protocolBindTo) {
if (!existsProtocolId(protocolBindFrom) || !existsProtocolId(protocolBindTo)) {
throw new IllegalArgumentException(String.format("Unable to bind protocol %s to %s (unkown protocol(s))", protocolBindFrom, protocolBindTo));
}
ProtocolId bindFrom = getProtocolIdFromWildId(protocolBindFrom);
ProtocolId bindTo = getProtocolIdFromWildId(protocolBindTo);
log.debug("Adding binding from {} to {}", bindFrom, bindTo);
if (bindFrom == bindTo) {
// or should it be allowed?
throw new IllegalArgumentException(String.format("It is not allowed to bind a protocol handler to itself (%s)", bindFrom));
}
if (!currentProtocolBindingMap.containsKey(bindFrom)) {
currentProtocolBindingMap.put(bindFrom, new HashSet<>());
}
currentProtocolBindingMap.get(bindFrom).add(bindTo);
return this;
}
private ProtocolId getProtocolIdFromWildId(String wildProtocolId) {
if (wildProtocolId == null || wildProtocolId.isEmpty()) {
throw new IllegalArgumentException("Unknown protocol or no protocol is mapped to a protocol id");
}
// This reduction does not handle multiple registrations to a similar protocolId-String well (it picks the first match found one)
return protocolRegistry.getRegisteredProtocolIdSet().stream()
.filter(protocolId -> protocolId.getProtocolId().equalsIgnoreCase(wildProtocolId)).findFirst().get();
}
private boolean existsProtocolId(String wildProtocolId) {
return wildProtocolId != null
&& protocolRegistry.getRegisteredProtocolIdSet().stream()
.anyMatch(protocolId -> protocolId.getProtocolId().equalsIgnoreCase(wildProtocolId));
}
/*
* (non-Javadoc)
*
* @see at.jku.fim.rubanetra.config.KrakenProtocolConfigurationBuilder#
* setPortSpecification
* (at.jku.fim.rubanetra.config.PortSpecification)
*/
@Override
public KrakenProtocolConfigurationBuilder setPortSpecification(PortSpecification transportProtocolPortSpec) {
this.currentProtocolPortSpec = transportProtocolPortSpec;
return this;
}
@Override
public KrakenProtocolConfigurationBuilder setTransportProtocolMappingStrategy(String strategy) {
if (strategy == null) {
throw new IllegalArgumentException("Invalid transport layer mapping strategy");
}
currentTransportLayerStrategy = strategy;
return this;
}
@Override
public KrakenProtocolConfigurationBuilder setTransportProtocolMappingFactory(KrakenTransportLayerMappingFactory mappingFactory) {
if (mappingFactory == null) {
throw new IllegalArgumentException("Invalid transport layer mapping factory");
}
currentTransportLayerMappingFactory = mappingFactory;
return this;
}
@Override
public KrakenProtocolConfigurationBuilder setPcapHandler(PcapHandler<PcapActivityListener> pcapHandler) {
this.currentPcapHandler = pcapHandler;
return this;
}
@Override
public KrakenProtocolConfiguration buildProtocolConfiguration() {
KrakenProtocolConfiguration setting = new KrakenProtocolConfigurationImpl();
resetCurrentProtocolConfiguration();
return setting;
}
/**
* @return the currently set protocol identifier (may be null)
*/
public String getCurrentProtocolIdentifier() {
return currentProtocolIdentifier;
}
@Override
public KrakenProtocolConfigurationBuilder setCurrentProtocolIdentifier(String currentProtocolIdentifier) {
if (this.currentProtocolIdentifier != null) {
// build is in progress
throw new IllegalStateException(String.format("Protocol configuration for %s is incomplete.", this.currentProtocolIdentifier));
}
this.currentProtocolIdentifier = currentProtocolIdentifier;
return this;
}
/**
* This inner class represents a final protocol configuration object which will be used to initialize Kraken protocol
* handlers.
* There are no setter methods, since all fields are now considered to be immutable and must not change during the
* lifetime of the object.
* Instances of this class may be obtained by using and configuring a
* {@link at.jku.fim.rubanetra.config.model.impl.KrakenProtocolConfigurationBuilderImpl}.
* This implementation relies on the fact that all registered protocol handlers (even custom handlers) define
* a default constructor. All handlers may utilize information of this configuration by overriding the
* {@link at.jku.fim.rubanetra.protocol.KrakenBaseProtocol#initialize(at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration)}
* method, which will be called as soon as possible after construction.
* Further note, that this implementation tries to reduce the number of protocol handler instances. The current
* mechanism first takes an unbound protocol id of the binding map, constructs an instance and saves this reference
* for the provided protocol id.
* <p>
* Constraints:
* The call of the initialization method is in general unordered. However, it is always ensured that
* for a given binding 'from'->'to', the protocol handler for 'from' is constructed and initialized before 'to'.
* Double bindings 'from'->'to' + 'to'->'from' _should_ be possible, but must be handled carefully (untested).
* All bindings are performed after initialization by calling 'from'.bind('to'). All handlers are responsible
* to take care of the concrete binding and must handle unsupported bindings appropriately.
*
* @see at.jku.fim.rubanetra.protocol.BaseProtocol#bind(at.jku.fim.rubanetra.protocol.BaseProtocol)
*/
public class KrakenProtocolConfigurationImpl implements KrakenProtocolConfiguration {
private final PortSpecification transportLayerPortSpec;
private final String transportLayerMappingStrategy;
private final KrakenTransportLayerMappingFactory transportLayerMappingFactory;
private final PcapHandler<PcapActivityListener> pcapHandler;
private final Map<ProtocolId, KrakenBaseProtocol> registeredProtocols;
/**
* keep private
*/
private KrakenProtocolConfigurationImpl() {
this.pcapHandler = currentPcapHandler;
this.transportLayerPortSpec = currentProtocolPortSpec;
this.transportLayerMappingStrategy = currentTransportLayerStrategy;
this.transportLayerMappingFactory = currentTransportLayerMappingFactory;
this.registeredProtocols = new HashMap<>();
check();
log.debug("Bindings to process: {}", currentProtocolBindingMap);
for (ProtocolId protocolId : currentProtocolBindingMap.keySet()) {
KrakenBaseProtocol baseProtocolFrom;
if (registeredProtocols.containsKey(protocolId)) {
// instance was already constructed and initialized
baseProtocolFrom = registeredProtocols.get(protocolId);
} else {
// construct and initialize a new instance
baseProtocolFrom = createBaseProtocolInstance(protocolId);
baseProtocolFrom.initialize(this);
// remember this instance
registeredProtocols.put(protocolId, baseProtocolFrom);
}
log.debug("[BIND_FROM] ProtocolId: {}, Class: {}", protocolId, baseProtocolFrom.getClass());
// bind this protocol to ...
for (ProtocolId bindToId : currentProtocolBindingMap.get(protocolId)) {
KrakenBaseProtocol baseProtocolTo;
if (registeredProtocols.containsKey(bindToId)) {
// instance was already constructed and initialized
baseProtocolTo = registeredProtocols.get(bindToId);
} else {
// construct and initialize a new instance
baseProtocolTo = createBaseProtocolInstance(bindToId);
baseProtocolTo.initialize(this);
// remember this instance
registeredProtocols.put(bindToId, baseProtocolTo);
}
log.debug("[BIND_TO] ProtocolId: {}, Class: {}", bindToId, baseProtocolTo.getClass());
// perform the actual binding
baseProtocolFrom.bind(baseProtocolTo);
}
}
}
//ENHANCEMENT: do not rely on the fact that there is always a default constructor
private KrakenBaseProtocol createBaseProtocolInstance(ProtocolId protocolId) {
IllegalStateException unknownProtocol = new IllegalStateException(String.format("Unable to instantiate a BaseProtocol class from protocol identifier: %s", protocolId));
Class<? extends BaseProtocol> protocolClass = protocolRegistry.getRegisteredProtocolClass(protocolId);
if (protocolClass == null) {
throw unknownProtocol;
}
if (KrakenBaseProtocol.class.isAssignableFrom(protocolClass)) {
try {
return protocolClass.asSubclass(KrakenBaseProtocol.class).newInstance();
} catch (InstantiationException | IllegalAccessException e) {
log.error("Unable to instantiate protocol handler {} - does it provide a default constructor?", protocolClass, e);
throw unknownProtocol;
}
} else {
log.error("This Protocol Configuration Builder supports KrakenBaseProtocol sub-classes only.");
throw new IllegalArgumentException("This Protocol Configuration Builder supports KrakenBaseProtocol sub-classes only.");
}
}
/**
* @return an immutable map consisting of all protocol id to handler bindings
*/
public Map<ProtocolId, KrakenBaseProtocol> getBoundProtocols() {
return Collections.unmodifiableMap(registeredProtocols);
}
private void check() {
if (transportLayerMappingFactory == null || transportLayerMappingStrategy == null || pcapHandler == null || transportLayerPortSpec == null) {
log.error("Factory {}, strategy {}, pcapHandler {}, portSpec {}", transportLayerMappingFactory, transportLayerMappingStrategy, pcapHandler, transportLayerPortSpec);
throw new IllegalStateException("Unable to build a KrakenProtocolConfiguration.");
}
}
@Override
public String getTransportLayerMappingStrategy() {
return this.transportLayerMappingStrategy;
}
@Override
public PortSpecification getPortSpecification() {
return this.transportLayerPortSpec;
}
@Override
public PcapHandler<PcapActivityListener> getPcapHandler() {
return this.pcapHandler;
}
@Override
public KrakenTransportLayerMappingFactory getTransportLayerMappingFactory() {
return transportLayerMappingFactory;
}
}
}
@@ -0,0 +1,74 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.config.model.impl;
import at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration;
import at.jku.fim.rubanetra.output.OutputWriterStrategy;
import at.jku.fim.rubanetra.output.impl.CsvOutputWriterStrategy;
import at.jku.fim.rubanetra.output.impl.JsonOutputWriterStrategy;
import at.jku.fim.rubanetra.output.impl.NopOutputWriterStrategy;
import at.jku.fim.rubanetra.output.impl.XmlOutputWriterStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.OutputStream;
/**
* A simple factory constructing {@link at.jku.fim.rubanetra.output.OutputWriterStrategy} objects
* for a known {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration.OutputFormat}
*/
public class OutputStrategyFactory {
private static final Logger log = LoggerFactory.getLogger(OutputStrategyFactory.class);
private static final NopOutputWriterStrategy nopOutputWriterStrategy = new NopOutputWriterStrategy();
/**
* Returns an object capable of transforming data into the provided {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration.OutputFormat}
*
* @param outputFormat the format to use for transforming data before writing to out
* @param out the output stream to write to
* @return if any provided argument is null, a {@link at.jku.fim.rubanetra.output.impl.NopOutputWriterStrategy},
* or a capable strategy object if an appropriate class is known
*/
public static OutputWriterStrategy createOutputWriterStrategy(RubanetraSystemConfiguration.OutputFormat outputFormat, OutputStream out) {
if (outputFormat == null || out == null) {
log.warn("Unable to create a suitable OutputWriterStrategy - null argument was passed.");
return nopOutputWriterStrategy;
}
switch (outputFormat) {
case PLASO:
case XML:
return new XmlOutputWriterStrategy(out);
case JSON:
return new JsonOutputWriterStrategy(out);
case CSV:
try {
return new CsvOutputWriterStrategy(out);
} catch (IOException e) {
log.warn("Unable to create a suitable OutputWriterStrategy for format {}, using NOP instead", outputFormat);
return nopOutputWriterStrategy;
}
case NOP:
return nopOutputWriterStrategy;
default:
log.warn("Unable to create a suitable OutputWriterStrategy for format {}, using NOP instead", outputFormat);
return nopOutputWriterStrategy;
}
}
}
@@ -0,0 +1,147 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.config.model.impl;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.BaseProtocol;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.handler.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
/**
* A representation of a simple protocol identifier ({@link at.jku.fim.rubanetra.config.model.ProtocolId})
* to an arbitrary {@link at.jku.fim.rubanetra.protocol.BaseProtocol} class.
* It is expected that all registered classes provide a default constructor.
*
* @see at.jku.fim.rubanetra.config.model.impl.KrakenProtocolConfigurationBuilderImpl
*/
public class ProtocolClassRegistry {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Map<ProtocolId, Class<? extends BaseProtocol>> protocolRegistry;
private final Map<String, ProtocolId> protocolIdMap;
/**
* create a new instance
*/
public ProtocolClassRegistry() {
protocolIdMap = new HashMap<>();
protocolRegistry = new HashMap<>();
}
/**
* Register a protocol identifier to a protocol handler class, which must provide a default constructor
*
* @param protocolId a protocol identifier, which should be advertised by the protocolHandlerClazz as well
* @param protocolHandlerClazz a class that is able to parse the protocol identified by the protocol id and
* provides a default constructor
* @throws java.lang.IllegalArgumentException if any argument is null or the protocol id was already registered
*/
public void registerHandler(ProtocolId protocolId, Class<? extends BaseProtocol> protocolHandlerClazz) {
if (protocolId == null || protocolId.getProtocolId() == null || protocolHandlerClazz == null) {
log.error("The protocol id and the protocol handler must not be null.");
throw new IllegalArgumentException();
}
if (protocolRegistry.containsKey(protocolId) || protocolIdMap.containsKey(protocolId.getProtocolId())) {
log.error("A protocol handler for the given protocol id {} was already registered before.", protocolId);
throw new IllegalArgumentException();
}
log.debug("Registering {} to class {}.", protocolId, protocolHandlerClazz);
protocolIdMap.put(protocolId.getProtocolId(), protocolId);
protocolRegistry.put(protocolId, protocolHandlerClazz);
}
/**
* Delete a registry record, does nothing if the protocol id was not registered
* @param protocolId the identifier for the handler to delete from the registry
*/
public void unregisterHandler(ProtocolId protocolId) {
if(protocolId!=null) {
protocolRegistry.remove(protocolId);
protocolIdMap.remove(protocolId.getProtocolId());
}
}
/**
* Return the registered class for a given protocol identifier
*
* @param id the identifier that was registered to this registry
* @return a class that should (unchecked) provide a default constructor and should be able to parse
* data adhering to the protocol identified by the protocol id
*/
public Class<? extends BaseProtocol> getRegisteredProtocolClass(ProtocolId id) {
return protocolRegistry.get(id);
}
/**
* @return all currently registered protocol identifiers
*/
public Set<ProtocolId> getRegisteredProtocolIdSet() {
return Collections.unmodifiableSet(protocolRegistry.keySet());
}
/**
* @return all currently registered protocol handler classes (duplicates may be possible)
*/
public Collection<Class<? extends BaseProtocol>> getRegisteredProtocolClassList() {
return Collections.unmodifiableCollection(protocolRegistry.values());
}
private static ProtocolClassRegistry defaultKrakenProtocolRegistry;
/**
* @return a default registry containing all originally known protocol ids and handlers.
* It will never contain custom protocol handlers unless they are added explicitly afterwards.
*/
public static ProtocolClassRegistry createDefaultKrakenProtocolRegistry() {
if (defaultKrakenProtocolRegistry != null) {
return defaultKrakenProtocolRegistry;
}
defaultKrakenProtocolRegistry = new ProtocolClassRegistry();
// it would be nice to implement some kind of class auto detection mechanism (Reflection maybe?)...
KrakenBaseProtocol[] krakenProtocolHandlers = {
new KrakenArpHandler(),
new KrakenEthernetProtocolHandler(),
new KrakenIpv4Handler(),
new KrakenIpv6Handler(),
new KrakenIcmpv4Handler(),
new KrakenIcmpv6Handler(),
new KrakenTcpHandler(),
new KrakenUdpHandler(),
new KrakenHttpHandler(),
new KrakenDnsProtocolHandler(),
new KrakenMsnHandler(),
new KrakenSmtpHandler(),
new KrakenDhcpHandler(),
new KrakenFtpHandler(),
new KrakenNetbiosHandler(),
new KrakenPop3Handler(),
new KrakenSnmpHandler(),
new KrakenTelnetHandler()
};
for (KrakenBaseProtocol p : krakenProtocolHandlers) {
defaultKrakenProtocolRegistry.registerHandler(p.getProtocolId(), p.getClass());
}
return defaultKrakenProtocolRegistry;
}
}
@@ -0,0 +1,402 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.config.model.impl;
import at.jku.fim.rubanetra.config.RubanetraSystemConfigurationBaseListener;
import at.jku.fim.rubanetra.config.RubanetraSystemConfigurationLexer;
import at.jku.fim.rubanetra.config.RubanetraSystemConfigurationParser;
import at.jku.fim.rubanetra.config.RubanetraSystemConfigurationParser.*;
import at.jku.fim.rubanetra.config.model.Port;
import at.jku.fim.rubanetra.config.model.PortRange;
import at.jku.fim.rubanetra.config.model.PortSpecification;
import at.jku.fim.rubanetra.protocol.mapper.KrakenTransportLayerMappingFactory;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* This class represents the implementation of the listener interface that was created previously by the ANTLRv4 parser.
* One of the first step of the system is to parse its own configuration. This implementation may be used to parse
* all main configuration parts, i.e. input/output, protocol and Drools specific settings can be handled by this
* class.
* <p>
* A {@link at.jku.fim.rubanetra.config.model.impl.RubanetraSystemConfigurationBuilder} is mainly utilized,
* including a {@link at.jku.fim.rubanetra.config.model.impl.KrakenProtocolConfigurationBuilderImpl}
* to perform the construction of the main configuration that may be used to further initialize the system.
* In order to allow further customizations this listener does not invoke the final call to
* {@link RubanetraSystemConfigurationBuilder#create()}, therefore it is the objects instantiator's obligation to ensure
* completeness of the configuration and the construction
* of the {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration}.
* <p>
* An appropriate ANTLR parser object can be created by calling the static method {@link #createConfigurationFileParser(java.io.File)}
* by passing a valid a configuration file.
* The parser should then be pointed to the root node, i.e.:
* <p>
* {@code RubanetraSystemConfigurationParser systemConfigParser = RubanetraSystemConfigurationBaseListenerImpl.createConfigurationFileParser(mainConfigFile);}
* {@code RubanetraSystemConfigurationParser.SystemContext systemContext = systemConfigParser.system();}
* <p>
* And finally a {@link org.antlr.v4.runtime.tree.ParseTreeWalker} may then be used to traverse the parse tree of the
* general configuration:
* <p>
* {@code ParseTreeWalker.DEFAULT.walk(systemConfigBaseListener, systemContext.generalConfig(0));}
*/
public class RubanetraSystemConfigurationBaseListenerImpl extends RubanetraSystemConfigurationBaseListener {
private final Logger log = LoggerFactory.getLogger(getClass());
private final RubanetraSystemConfigurationBuilder systemConfigBuilder;
private final KrakenProtocolConfigurationBuilderImpl protocolSettingBuilder;
/**
* Creates a new listener instance, which may be used to react to parsing event generated by a
* {@link at.jku.fim.rubanetra.config.RubanetraSystemConfigurationParser}.
*
* @param systemConfigBuilder an instance of a builder which will be used to setup the system configuration,
* may not be null
* @throws java.lang.IllegalArgumentException if parser errors are encountered
*/
public RubanetraSystemConfigurationBaseListenerImpl(RubanetraSystemConfigurationBuilder systemConfigBuilder) {
if (systemConfigBuilder == null) {
log.error("The passed system configuration may not be null");
throw new IllegalArgumentException();
}
this.systemConfigBuilder = systemConfigBuilder;
protocolSettingBuilder = new KrakenProtocolConfigurationBuilderImpl();
}
public static RubanetraSystemConfigurationParser createConfigurationFileParser(File configurationFile) {
final Logger log = LoggerFactory.getLogger(RubanetraSystemConfigurationParser.class);
ANTLRFileStream fileStream;
try {
log.debug("Trying to create an ANTLRFileStream for file {}", configurationFile);
fileStream = new ANTLRFileStream(configurationFile.getCanonicalPath());
} catch (IOException e) {
log.error("Invalid configuration file: {}", configurationFile, e);
throw new IllegalArgumentException(e);
}
log.debug("Creating a system configuration lexer.");
RubanetraSystemConfigurationLexer systemConfigLexer = new RubanetraSystemConfigurationLexer(fileStream);
systemConfigLexer.removeErrorListeners();
CommonTokenStream configTokens = new CommonTokenStream(systemConfigLexer);
log.debug("Creating a system configuration parser.");
RubanetraSystemConfigurationParser systemConfigurationParser = new RubanetraSystemConfigurationParser(configTokens);
systemConfigurationParser.removeErrorListeners();
systemConfigurationParser.addErrorListener(new BaseErrorListener() {
@Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) {
String sourceName = recognizer.getInputStream().getSourceName();
if (!sourceName.isEmpty()) {
sourceName = String.format("%s: ", sourceName);
}
log.error("Configuration syntax error: {} line {}:{} {}", sourceName, line, charPositionInLine, msg);
}
});
return systemConfigurationParser;
}
@Override
public void enterSystem(@NotNull SystemContext ctx) {
log.debug("Entered system node");
}
@Override
public void exitSystem(@NotNull SystemContext ctx) {
// we could call systemConfigBuilder.build() here, instead the user is expected to call it
log.debug("Exiting system node");
}
/**
* This method will be called by a {@link org.antlr.v4.runtime.tree.ParseTreeWalker} only if the
* protocol configuration section was specified within the main system
* configuration file and not in a separate file.
*/
@Override
public void enterProtocolConfig(@NotNull ProtocolConfigContext ctx) {
// Use another listener to handle the complex protocol configuration
// settings
//ParseTreeWalker.DEFAULT.walk(this, ctx);
log.debug("Entered protocol config node");
}
@Override
public void enterProtocolSetting(@NotNull ProtocolSettingContext ctx) {
log.debug("Entered protocol setting node");
ProtocolHeaderContext protocolHeaderContext = ctx.protocolHeader();
List<PortSpecificationContext> portSpecificationContext = ctx.portSpecification();
List<ProtocolBindingContext> protocolBindingContexts = ctx.protocolBinding();
List<TransportLayerMappingSettingContext> mappingStrategyContexts = ctx.transportLayerMappingSetting();
if (protocolHeaderContext == null) {
throw new IllegalArgumentException("Missing protocol identifier.");
}
String protocolIdentifier = protocolHeaderContext.protocolId().getText();
protocolSettingBuilder.setCurrentProtocolIdentifier(protocolIdentifier);
if (portSpecificationContext.isEmpty()) {
protocolSettingBuilder.setPortSpecification(PortSpecification.UniversalPortSpecification.ACCEPT_ALL);
} else if (portSpecificationContext.size() > 1) {
throw new IllegalArgumentException(String.format("Multiple port specifications encountered, but only one is permitted (protocol setting: %s)", protocolIdentifier));
} else {
portSpecificationContext.get(0).enterRule(this);
}
for (ProtocolBindingContext bindingContext : protocolBindingContexts) {
List<ProtocolIdContext> protocolIdContexts = bindingContext.protocolId();
if (protocolIdContexts.size() != 2)
throw new IllegalArgumentException(String.format("Invalid protocol binding encountered (protocol setting: %s, binding: %s)", protocolIdentifier, protocolIdContexts));
protocolSettingBuilder.bindProtocol(bindingContext.protocolId(0).ID().getText(), bindingContext.protocolId(1).ID().getText());
}
protocolSettingBuilder.setTransportProtocolMappingFactory(systemConfigBuilder.getTransportLayerMappingFactory());
if (mappingStrategyContexts.isEmpty()) {
protocolSettingBuilder.setTransportProtocolMappingStrategy(KrakenTransportLayerMappingFactory.DESTINATION_PORT_STRATEGY);
} else if (mappingStrategyContexts.size() > 1) {
throw new IllegalArgumentException(String.format("Multiple transport layer mapping strategies encountered (protocol setting: %s, binding: %s)", protocolIdentifier, mappingStrategyContexts));
} else {
protocolSettingBuilder.setTransportProtocolMappingStrategy(mappingStrategyContexts.get(0).transportLayerMappingStrategy().getText().toUpperCase());
}
protocolSettingBuilder.setPcapHandler(systemConfigBuilder.getPcapHandler());
systemConfigBuilder.addProtocolSetting(protocolSettingBuilder.buildProtocolConfiguration());
protocolSettingBuilder.resetCurrentProtocolConfiguration();
}
@Override
public void enterPortSpecification(@NotNull PortSpecificationContext ctx) {
PortSpecification portSpec;
if (ctx.ALL() != null) {
portSpec = PortSpecification.UniversalPortSpecification.ACCEPT_ALL;
} else if (ctx.NONE() != null) {
portSpec = PortSpecification.UniversalPortSpecification.REJECT_ALL;
} else {
portSpec = new PortSpecification();
}
MultiplePortsContext multiplePortsContext = ctx.multiplePorts();
if (multiplePortsContext != null) {
for (PortRangeContext portRangeContext : multiplePortsContext.portRange()) {
List<SinglePortContext> singlePortContexts = portRangeContext.singlePort();
switch (singlePortContexts.size()) {
case 1:
portSpec.add(parsePortNumber(singlePortContexts.get(0)));
break;
case 2:
portSpec.add(parsePortRange(singlePortContexts.get(0), singlePortContexts.get(1)));
break;
default:
throw new IllegalArgumentException("Invalid port specification encountered");
}
}
}
protocolSettingBuilder.setPortSpecification(portSpec);
}
private PortRange parsePortRange(SinglePortContext portContextA, SinglePortContext portContextB) {
Port lowerPort, upperPort;
lowerPort = parsePortNumber(portContextA);
upperPort = parsePortNumber(portContextB);
if (lowerPort.compareTo(upperPort) > 0) {
Port tmpPort = lowerPort;
lowerPort = upperPort;
upperPort = tmpPort;
}
return new PortRange(lowerPort, upperPort);
}
private Port parsePortNumber(SinglePortContext singlePortContext) {
return Port.create(Integer.parseUnsignedInt(singlePortContext.INT().getText()));
}
/**
* **********************************************************************
* General configuration options will be parsed by the following methods.
* Semantic checks will be performed by the
* {@link at.jku.fim.rubanetra.config.model.impl.RubanetraSystemConfigurationBuilder} only.
* **********************************************************************
*/
@Override
public void enterBpfFilterString(@NotNull BpfFilterStringContext ctx) {
String bpfFilter = unquoteString(ctx.STRING().getText());
// syntax check will be performed by the native BPF compiler
systemConfigBuilder.setBpfFilter(bpfFilter);
}
@Override
public void enterBpfNetmask(@NotNull BpfNetmaskContext ctx) {
int bpfNetmask = Integer.parseUnsignedInt(ctx.INT().getText());
systemConfigBuilder.setBpfNetmask(bpfNetmask);
}
@Override
public void enterBpfOptimize(@NotNull BpfOptimizeContext ctx) {
boolean bpfOptimize = false;
if (ctx.TRUE() != null) {
bpfOptimize = true;
}
systemConfigBuilder.setBpfOptimize(bpfOptimize);
}
@Override
public void enterDroolsBaseModelName(@NotNull DroolsBaseModelNameContext ctx) {
String droolsBaseModelName = unquoteString(ctx.getText());
systemConfigBuilder.setDroolsBaseModelName(droolsBaseModelName);
}
@Override
public void enterDroolsSessionModelName(@NotNull DroolsSessionModelNameContext ctx) {
String droolsSessionModelName = unquoteString(ctx.getText());
systemConfigBuilder.setDroolsSessionModelName(droolsSessionModelName);
}
/**
* This method will be called by a {@link org.antlr.v4.runtime.tree.ParseTreeWalker} only if the
* optional protocol configuration file setting was specified by the user in
* the main system configuration file. It will be assumed that there will
* not follow a separate protocol configuration section within the main
* configuration file, i.e. the
* {@link #enterProtocolConfig(at.jku.fim.rubanetra.config.RubanetraSystemConfigurationParser.ProtocolConfigContext)} will not be called by
* the {@link org.antlr.v4.runtime.tree.ParseTreeWalker} (otherwise the user has specified two
* separate protocol configuration sections, one within the main system
* configuration and one in a different file, which is not allowed).
*/
@Override
public void enterProtocolConfigurationFileSetting(@NotNull ProtocolConfigurationFileSettingContext ctx) {
String protocolConfigurationString = unquoteString(ctx.filePath().getText());
// check file path syntax, i.e. an InvalidPathException may be thrown
systemConfigBuilder.setProtocolConfigurationPath(protocolConfigurationString);
RubanetraSystemConfigurationParser protocolConfigParser = createConfigurationFileParser(systemConfigBuilder.getProtocolConfigurationFile());
ParseTree parseTree = protocolConfigParser.protocolConfig(); // begin parsing at the protocol configuration node
if (parseTree == null) {
throw new IllegalArgumentException("The specified protocol configuration file does not contain the protocol configuration section");
}
ParseTreeWalker.DEFAULT.walk(this, parseTree);
}
@Override
public void enterDroolsConfigurationFile(@NotNull DroolsConfigurationFileContext ctx) {
String droolsConfigurationString = unquoteString(ctx.filePath().getText());
systemConfigBuilder.setDroolsConfigurationPath(droolsConfigurationString);
// we do not need to parse the drools configuration file, due to the already included Drools Parser (also based on ANTLR)
}
@Override
public void enterInputConfigurationFileSetting(@NotNull InputConfigurationFileSettingContext ctx) {
String inputConfigurationString = unquoteString(ctx.filePath().getText());
systemConfigBuilder.setInputConfigurationPath(inputConfigurationString);
RubanetraSystemConfigurationParser inputConfigurationParser = createConfigurationFileParser(systemConfigBuilder.getInputConfigurationFile());
ParseTree inputConfigurationParseTree = inputConfigurationParser.inputConfig();
if (inputConfigurationParseTree == null) {
throw new IllegalArgumentException("The specified input configuration file does not contain the input configuration section");
}
ParseTreeWalker.DEFAULT.walk(this, inputConfigurationParseTree);
}
@Override
public void enterOutputConfigurationFileSetting(@NotNull OutputConfigurationFileSettingContext ctx) {
String outputConfigurationString = unquoteString(ctx.filePath().getText());
systemConfigBuilder.setOutputConfigurationPath(outputConfigurationString);
RubanetraSystemConfigurationParser outputConfigurationParser = createConfigurationFileParser(systemConfigBuilder.getOutputConfigurationFile());
ParseTree outputConfigurationParseTree = outputConfigurationParser.outputConfig();
if (outputConfigurationParseTree == null) {
throw new IllegalArgumentException("The specified output configuration file does not contain the output configuration section");
}
ParseTreeWalker.DEFAULT.walk(this, outputConfigurationParseTree);
}
/**
* **********************************************************************
* Dynamic I/O configuration options will be parsed by the following methods.
* Semantic checks will be performed by the
* {@link at.jku.fim.rubanetra.config.model.impl.RubanetraSystemConfigurationBuilder} only.
* **********************************************************************
*/
@Override
public void exitInputConfig(@NotNull InputConfigContext ctx) {
// systemConfigBuilder.createPcapHandler();
}
@Override
public void enterInputSpecification(@NotNull InputSpecificationContext ctx) {
List<FilePathContext> filePathContexts = ctx.filePath();
if (filePathContexts == null || filePathContexts.isEmpty()) {
throw new IllegalArgumentException("At least one input file must be specified.");
}
for (FilePathContext f : filePathContexts) {
systemConfigBuilder.addInputPath(unquoteString(f.getText()));
}
}
@Override
public void enterInputFormat(@NotNull InputFormatContext ctx) {
systemConfigBuilder.setInputFormat(ctx.getText());
}
@Override
public void enterSortByFirstTimestamp(@NotNull SortByFirstTimestampContext ctx) {
systemConfigBuilder.setSortByFirstTimestamp(ctx.TRUE()!=null);
}
@Override
public void enterOutputSpecification(@NotNull OutputSpecificationContext ctx) {
if (ctx.STDOUT() != null) {
systemConfigBuilder.setOutputStream(System.out);
} else {
systemConfigBuilder.setOutputFile(unquoteString(ctx.filePath().getText()));
}
}
@Override
public void enterOutputFormat(@NotNull OutputFormatContext ctx) {
systemConfigBuilder.setOutputFormat(ctx.getText());
}
private static String unquoteString(String quotedString) {
if (quotedString == null || quotedString.length() < 2) {
return quotedString;
}
if (quotedString.startsWith("\"") && quotedString.endsWith("\"")) {
return quotedString.substring(1, quotedString.length() - 1);
}
return quotedString;
}
}
@@ -0,0 +1,666 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.config.model.impl;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration;
import at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration;
import at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration.OutputFormat;
import at.jku.fim.rubanetra.drools.DroolsKnowledgeBaseFactory;
import at.jku.fim.rubanetra.output.OutputWriterStrategy;
import at.jku.fim.rubanetra.pcap.PcapActivityListener;
import at.jku.fim.rubanetra.pcap.PcapFileHandler;
import at.jku.fim.rubanetra.pcap.PcapFilter;
import at.jku.fim.rubanetra.pcap.PcapHandler;
import at.jku.fim.rubanetra.protocol.mapper.KrakenTransportLayerMappingFactory;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import static at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration.InputFormat;
/**
* This class may be used to setup the essential configuration of the Drools rule engine, the JNetPcap file parser
* including optional Berkeley Packet Filter support, the Kraken Pcap and network protocol parser, and finally
* general input and output options.
* The builder produces a {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration} implementation,
* that may be used to create a {@link at.jku.fim.rubanetra.drools.DroolsKrakenProtocolHandler}
* which in turn is capable of managing the interaction of the Drools rule engine and the Kraken protocol decoder.
*
* @see at.jku.fim.rubanetra.drools.DroolsKrakenProtocolHandler
* @see at.jku.fim.rubanetra.config.ConfigurationController
* @see at.jku.fim.rubanetra.config.model.impl.RubanetraSystemConfigurationBuilder.RubanetraSystemConfigurationImpl
* @see at.jku.fim.rubanetra.config.model.impl.RubanetraSystemConfigurationBaseListenerImpl
*/
public class RubanetraSystemConfigurationBuilder {
private final Logger log = LoggerFactory.getLogger(getClass());
/**
* The system configuration may be split up into several files (optional)
*/
private File protocolConfigurationFile;
private File droolsConfigurationFile;
private File inputConfigurationFile;
private File outputConfigurationFile;
/**
* The set of files containing the captured network packets
*/
private final Set<File> inputFiles;
/**
* The general format of the files containing the network traffic dumps (usually Pcap)
*/
private InputFormat inputFormat;
/**
* The output stream to write to, this stream should be closed by calling
* {@link at.jku.fim.rubanetra.output.OutputWriterStrategy#closeWriter()}
*/
private OutputStream outputStream;
/**
* The general format of the produced output, defines the transform to apply before the dumping process
*/
private OutputFormat outputFormat;
/**
* The strategy defining the final data representation and performs the actual writing task
*/
private OutputWriterStrategy outputWriterStrategy;
/**
* The Pcap data input stream, which will parse the inputFiles and compile/apply the BPF filter
*/
private PcapFileHandler<PcapActivityListener> pcapHandler;
private String bpfFilter;
private boolean bpfOptimize = false;
private int bpfNetmask = 0;
/**
* Kraken library specific settings
*/
private final KrakenTransportLayerMappingFactory transportLayerMappingFactory;
private final Set<KrakenProtocolConfiguration> protocolSettings;
/**
* Drools rule engine specific settings
*/
private String droolsBaseModelName;
private String droolsSessionModelName;
private boolean sortByFirstTimestamp;
private RubanetraSystemConfigurationBuilder() {
// keep private to prevent public instantiation
this.protocolSettings = new HashSet<>();
this.inputFiles = new LinkedHashSet<>();
this.transportLayerMappingFactory = KrakenTransportLayerMappingFactory.createDefaultKrakenTransportLayerMappingFactory();
}
/**
* @return a new instance of the {@link at.jku.fim.rubanetra.config.model.impl.RubanetraSystemConfigurationBuilder}
*/
public static RubanetraSystemConfigurationBuilder create() {
return new RubanetraSystemConfigurationBuilder();
}
/**
* @return the currently configured Pcap handler or null
*/
public PcapFileHandler<PcapActivityListener> getPcapHandler() {
return pcapHandler;
}
/**
* This should be the last method called, i.e. after all the settings have been defined. This method will finalize
* the configuration, perform several validations and checks, create a {@link at.jku.fim.rubanetra.pcap.PcapFileHandler}
* (if it was not defined already) and return an immutable implementation of the system configuration itself.
* <p>
* This method will throw exceptions if invalid settings are encountered.
*
* @return an immutable, finalized configuration instance, which may be passed to a {@link at.jku.fim.rubanetra.drools.DroolsKrakenProtocolHandler}
* @see at.jku.fim.rubanetra.config.model.impl.RubanetraSystemConfigurationBuilder.RubanetraSystemConfigurationImpl
* @see at.jku.fim.rubanetra.drools.DroolsKrakenProtocolHandler
*/
public synchronized RubanetraSystemConfiguration build() {
if (pcapHandler == null) {
createPcapHandler();
}
return new RubanetraSystemConfigurationImpl(this);
}
/**
* Creates and sets a {@link at.jku.fim.rubanetra.pcap.PcapHandler} using all already defined
* input files, see {@link #addInputPath(String)}. Unless called before invoking {@link #build()}, this method
* will be called during the final build automatically.
*
* @return a PcapHandler using already defined input files
* @throws java.lang.IllegalStateException if this method was called more than once
*/
public PcapHandler<PcapActivityListener> createPcapHandler() {
if (this.pcapHandler != null) {
throw new IllegalStateException("A PcapHandler has already been built.");
}
final File[] pcapFiles = inputFiles.toArray(new File[inputFiles.size()]);
if (this.bpfFilter != null) {
PcapFilter bpfFilter = new PcapFilter(this.bpfFilter, this.bpfOptimize, this.bpfNetmask);
this.pcapHandler = new PcapFileHandler<>(bpfFilter, sortByFirstTimestamp, pcapFiles);
} else {
this.pcapHandler = new PcapFileHandler<>(sortByFirstTimestamp, pcapFiles);
}
return this.pcapHandler;
}
/**
* Sets a Berkeley Packet Filter expression.
* Some examples are available at http://www.tcpdump.org/manpages/pcap-filter.7.html
* See also Steven McCanne and Van Jacobson paper
* 'The BSD packet filter: A New Architecture for User-level Packet Capture',
* in USENIX Winter 1993 Conference Proceedings
* <p>
* This setting is optional.
*
* @param bpfFilter a String constituting a Berkeley Packet Filter which will be compiled by JNetPcap/libpcap
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder setBpfFilter(String bpfFilter) {
if (this.pcapHandler != null) {
throw new IllegalStateException("A PcapHandler has already been built.");
}
this.bpfFilter = bpfFilter;
return this;
}
/**
* Whether or not to try to optimize the given BPF expression, default: false
* <p>
* This setting is optional.
*
* @param bpfOptimize true, iff the compiler should try to optimize the expression
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder setBpfOptimize(boolean bpfOptimize) {
if (this.pcapHandler != null) {
throw new IllegalStateException("A PcapHandler has already been built.");
}
this.bpfOptimize = bpfOptimize;
return this;
}
/**
* Sets the BPF netmask, default: 0
* <p>
* This setting is optional.
*
* @param bpfNetmask the BPF netmask to use
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder setBpfNetmask(int bpfNetmask) {
if (this.pcapHandler != null) {
throw new IllegalStateException("A PcapHandler has already been built.");
}
this.bpfNetmask = bpfNetmask;
return this;
}
/**
* Every Drools knowledge base is identified by a name, this name must be specified only if the default
* knowledge base as specified in the Drools configuration file ('kmodule.xml') should not be used.
* See http://drools.jboss.org/documentation.
* <p>
* This setting is optional.
*
* @param droolsBaseModelName the Drools knowledge base to use for fact insertion and deduction
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder setDroolsBaseModelName(String droolsBaseModelName) {
this.droolsBaseModelName = droolsBaseModelName;
return this;
}
/**
* Every drools knowledge base may define several {@link org.kie.api.runtime.KieSession}s,
* this parameter must be specified if the default KieSession as specified in the Drools configuration
* file ('kmodule.xml') should not be used. See http://drools.jboss.org/documentation.
* <p>
* This setting is optional.
*
* @param droolsSessionModelName the Drools session to use for fact insertion and deduction
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder setDroolsSessionModelName(String droolsSessionModelName) {
this.droolsSessionModelName = droolsSessionModelName;
return this;
}
/**
* The path of the file containing the protocol configuration. The file must exist and be readable.
* <p>
* This setting is mandatory.
*
* @param protocolConfigurationPath the path of the file containing the protocol configuration
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder setProtocolConfigurationPath(String protocolConfigurationPath) {
protocolConfigurationFile = canonicalizeFilePath(protocolConfigurationPath);
checkFileReadable(protocolConfigurationFile);
return this;
}
/**
* The path of the directory containing the drools configuration directory. The directory must contain a 'META-INF'
* directory and the file 'kmodule.xml', i.e. 'META-INF/kmodule.xml', as well as 'META-INF/pom.properties' because
* Drools apparently expects the knowledge base to reside inside a JAR archive. The creation of a JAR archive for
* every change to the rule base is not possible for this
* systems use-case, due to the design goal that changes to the knowledge base are both
* convenient and easy to perform from a users perspective. The alternative to this approach is to programmatically
* define the Drools knowledge base which is currently not feasible.
* <p>
* ENHANCEMENT: This will probably change in the future,
* as soon as a new way to define the knowledge base is found.
* <p>
* This setting is mandatory.
*
* @param droolsConfigurationString the path of the directory representing the Drools configuration structure
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder setDroolsConfigurationPath(String droolsConfigurationString) {
this.droolsConfigurationFile = canonicalizeFilePath(droolsConfigurationString);
checkFileReadable(this.droolsConfigurationFile);
if (!droolsConfigurationFile.isDirectory()) {
throw new IllegalArgumentException("The drools configuration file must represent a directory (containing ./META-INF/kmodule.xml).");
}
return this;
}
/**
* Set the path of the file containing the input configuration. The file must exist and be readable.
* <p>
* This setting is mandatory.
*
* @param inputConfigurationString the path of the file containing the input configuration
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder setInputConfigurationPath(String inputConfigurationString) {
this.inputConfigurationFile = canonicalizeFilePath(inputConfigurationString);
checkFileReadable(this.inputConfigurationFile);
return this;
}
/**
* Set the path of the file containing the output configuration, this file must exist and be readable.
* <p>
* This setting is mandatory.
*
* @param outputConfigurationString the path of the file containing the output configuration
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder setOutputConfigurationPath(String outputConfigurationString) {
outputConfigurationFile = canonicalizeFilePath(outputConfigurationString);
checkFileReadable(outputConfigurationFile);
return this;
}
/**
* The main output stream for writing the derived and parsed information to. A {@link at.jku.fim.rubanetra.output.OutputWriterStrategy}
* performs the actual writing process and transformation.
* <p>
* This setting is mandatory
*
* @param out the output stream to use, may not be null
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder setOutputStream(OutputStream out) {
if (out == null) {
throw new IllegalArgumentException("Invalid output stream");
}
this.outputStream = out;
return this;
}
/**
* A convenience method for {@link #setOutputStream(java.io.OutputStream)}, i.e. it takes a file path
* and sets the output stream setting.
* The specified file must be writable.
* <p>
* This setting is optional and replaces the obligation to call {@link #setOutputStream(java.io.OutputStream)}
*
* @param outputPath the path depicting a file to write to
*/
public void setOutputFile(String outputPath) {
File outputFile = canonicalizeFilePath(outputPath);
if (outputFile.isDirectory()) {
throw new IllegalArgumentException();
}
if(outputFile.exists()) {
log.warn("Output file {} exists, it will be overwritten.", outputFile.toString());
}
try {
setOutputStream(new FileOutputStream(outputFile));
} catch (FileNotFoundException e) {
throw new IllegalArgumentException("Invalid output file, is it writable?");
}
}
/**
* The {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration.OutputFormat} to use
* for transforming the written output before it is written.
* <p>
* This setting is mandatory.
*
* @param parsedOutputFormat the output format to use as serialization representation
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder setOutputFormat(String parsedOutputFormat) {
if (parsedOutputFormat == null) {
throw new IllegalArgumentException("Invalid output format.");
} else {
OutputFormat outputFormat = OutputFormat.valueOf(trimAndCapitalize(parsedOutputFormat));
if (outputFormat == null) {
throw new IllegalArgumentException(String.format("Unknown output format, %s)", parsedOutputFormat));
} else {
this.outputFormat = outputFormat;
}
}
return this;
}
/**
* Sets the strategy used to control the serialization process.
* <p>
* This setting is mandatory.
*
* @param outputWriter the strategy to use
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder setOutputWriterStrategy(OutputWriterStrategy outputWriter) {
if (outputWriter == null) {
throw new IllegalArgumentException("OutputWriter must not be null.");
}
this.outputWriterStrategy = outputWriter;
return this;
}
private String trimAndCapitalize(String aString) {
return aString.trim().toUpperCase();
}
/**
* Adds a file-path of a readable file containing data adhering to the specified {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration.InputFormat}
* to the list of files that are to be parsed.
* Please note that a input file handler {@link #createPcapHandler()} must not be created before adding all
* required input files.
* <p>
* This setting is optional.
*
* @param inputPath a readable file containing data adhering to the configured input format (unchecked)
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder addInputPath(String inputPath) {
if (this.pcapHandler != null) {
throw new IllegalStateException("A PcapHandler has already been built.");
}
File inputFile = canonicalizeFilePath(inputPath);
checkFileReadable(inputFile);
if (inputFiles.contains(inputFile)) {
throw new IllegalArgumentException(String.format("Duplicate input file: %s", inputFile));
}
this.inputFiles.add(inputFile);
return this;
}
/**
* Sets the flag that specifies whether or not the Pcap file handler should sort the input files according
* to the chronological order of their first PCAP entry.
* @param sortByFirstTimestamp true, iff the first timestamp of each file entry should be used for specifying
* the order of the input files.
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder setSortByFirstTimestamp(boolean sortByFirstTimestamp) {
if (this.pcapHandler != null) {
throw new IllegalStateException("A PcapHandler has already been built.");
}
this.sortByFirstTimestamp = sortByFirstTimestamp;
return this;
}
/**
* Sets the format/type of the input files to parse. Only a single common type is allowed, i.e. format mixing
* is currently not implemented.
* <p>
* This setting is mandatory.
*
* @param parsedInputFormat the {@link at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration.InputFormat}
* of the input files to parse
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder setInputFormat(String parsedInputFormat) {
if (parsedInputFormat == null) {
throw new IllegalArgumentException("Invalid input format.");
} else {
InputFormat inputFormat = InputFormat.valueOf(trimAndCapitalize(parsedInputFormat));
if (inputFormat == null) {
throw new IllegalArgumentException(String.format("Unknown input format, %s)", parsedInputFormat));
} else {
this.inputFormat = inputFormat;
}
}
return this;
}
private void checkFileReadable(File file) {
if (!file.canRead()) {
log.warn(String.format("The specified file (%s) is not readable.", file));
}
}
/**
* Adds a concrete protocol configuration to the system's configuration.
* It is assumed that the object has finished all required protocol handler bindings and initialization steps.
* <p>
* This setting is optional.
*
* @param setting the protocol configuration to add as a system setting
* @return the builder instance
*/
public RubanetraSystemConfigurationBuilder addProtocolSetting(KrakenProtocolConfiguration setting) {
this.protocolSettings.add(setting);
return this;
}
/**
* @return the currently configured protocol configuration file
*/
public File getProtocolConfigurationFile() {
return protocolConfigurationFile;
}
/**
* @return the currently configured directory containing the Drools configuration structure
*/
public File getDroolsConfigurationFile() {
return droolsConfigurationFile;
}
/**
* @return the currently configured input configuration file
*/
public File getInputConfigurationFile() {
return inputConfigurationFile;
}
/**
* @return the currently configured output configuration file
*/
public File getOutputConfigurationFile() {
return outputConfigurationFile;
}
/**
* @return the currently configured transport layer mapping factory
*/
public KrakenTransportLayerMappingFactory getTransportLayerMappingFactory() {
return transportLayerMappingFactory;
}
private File canonicalizeFilePath(String filePath) {
if (filePath == null) {
throw new IllegalArgumentException();
}
log.debug("Trying to locate and canonicalize file path {}", filePath);
File canonicalFile;
URL resourceUrl = ClassLoader.getSystemResource(filePath);
try {
if (resourceUrl == null) {
log.debug("Could not locate {} in the classpath, interpreting as real path", filePath);
canonicalFile = new File(filePath).getCanonicalFile();
} else {
log.debug("Located {} in the classpath", filePath);
canonicalFile = new File(resourceUrl.toURI()).getCanonicalFile();
}
} catch (IOException | URISyntaxException e) {
throw new IllegalArgumentException(String.format("The specified path (%s) cannot be canonicalized.", filePath));
}
return canonicalFile;
}
/**
* This class represents the actual immutable system configuration
*/
public Set<File> getInputFiles() {
return inputFiles;
}
public InputFormat getInputFormat() {
return inputFormat;
}
public OutputStream getOutputStream() {
return outputStream;
}
public OutputFormat getOutputFormat() {
return outputFormat;
}
public String getBpfFilter() {
return bpfFilter;
}
public boolean isBpfOptimize() {
return bpfOptimize;
}
public int getBpfNetmask() {
return bpfNetmask;
}
public String getDroolsBaseModelName() {
return droolsBaseModelName;
}
public String getDroolsSessionModelName() {
return droolsSessionModelName;
}
public boolean isSortByFirstTimestamp() {
return sortByFirstTimestamp;
}
private final class RubanetraSystemConfigurationImpl implements RubanetraSystemConfiguration {
private final Set<KrakenProtocolConfiguration> protocolSettings;
private final PcapFileHandler<?> pcapHandler;
private final Set<File> inputFiles;
private final InputFormat inputFormat;
private final OutputFormat outputFormat;
private final OutputStream outputStream;
private final OutputWriterStrategy outputWriterStrategy;
private final KieSession kieSession;
/**
* only the surrounding builder should create instances of this class
*/
private RubanetraSystemConfigurationImpl(RubanetraSystemConfigurationBuilder builder) {
this.protocolSettings = Collections.unmodifiableSet(builder.protocolSettings);
this.inputFiles = Collections.unmodifiableSet(builder.inputFiles);
this.pcapHandler = builder.pcapHandler;
this.inputFormat = builder.inputFormat;
this.outputStream = builder.outputStream;
this.outputFormat = builder.outputFormat;
this.outputWriterStrategy = builder.outputWriterStrategy != null ? builder.outputWriterStrategy : OutputStrategyFactory.createOutputWriterStrategy(outputFormat, outputStream);
KieContainer kContainer = DroolsKnowledgeBaseFactory.createKieContainer(builder.droolsConfigurationFile);
this.kieSession = DroolsKnowledgeBaseFactory.createKieSession(kContainer, builder.droolsBaseModelName, builder.droolsSessionModelName);
}
@Override
public OutputStream getOutputStream() {
return outputStream;
}
@Override
public OutputFormat getOutputFormat() {
return outputFormat;
}
@Override
public Set<KrakenProtocolConfiguration> getProtocolSettings() {
return Collections.unmodifiableSet(protocolSettings);
}
@Override
public PcapFileHandler<?> getPcapHandler() {
return pcapHandler;
}
@Override
public KieSession getKieSession() {
return kieSession;
}
@Override
public Set<File> getInputFiles() {
return inputFiles;
}
@Override
public InputFormat getInputFormat() {
return inputFormat;
}
@Override
public OutputWriterStrategy getOutputWriterStrategy() {
return outputWriterStrategy;
}
}
}
@@ -0,0 +1,178 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.drools;
import org.kie.api.KieServices;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieModule;
import org.kie.api.builder.KieRepository;
import org.kie.api.builder.model.KieBaseModel;
import org.kie.api.builder.model.KieModuleModel;
import org.kie.api.builder.model.KieSessionModel;
import org.kie.api.builder.model.KieSessionModel.KieSessionType;
import org.kie.api.conf.EqualityBehaviorOption;
import org.kie.api.conf.EventProcessingOption;
import org.kie.api.io.Resource;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.conf.ClockTypeOption;
import java.io.File;
/**
* This factory may be used to create instances of a {@link org.kie.api.runtime.KieSession} and/or
* {@link org.kie.api.runtime.KieContainer}.
* Several default values were defined to correspond to the default options used by this framework, e.g.
* a {@link org.kie.api.builder.model.KieSessionModel.KieSessionType#STATEFUL} session, a pseudo {@link org.kie.api.runtime.conf.ClockTypeOption}
* and the event stream options are currently used for this purpose.
*
* For extended documentation of the used classes and default values, please refer to the Drools documentation at
* http://drools.jboss.org/documentation
*/
public class DroolsKnowledgeBaseFactory {
/**
* The default session is a stateful one
*/
public static final KieSessionType DEFAULT_KNOWLEDGE_SESSION_TYPE = KieSessionType.STATEFUL;
/**
* The default type of the rule engines session clock is a pseudo clock, see
* https://docs.jboss.org/drools/release/6.0.1.Final/drools-docs/html/DroolsComplexEventProcessingChapter.html#d0e10068
*/
public static final ClockTypeOption DEFAULT_CLOCK_TYPE = ClockTypeOption.get("pseudo");
/**
* The default processing mode is stream mode, since reasoning over time based events should be enabled
*/
public static final EventProcessingOption DEFAULT_EVENT_PROCESSING = EventProcessingOption.STREAM;
public static final EqualityBehaviorOption DEFAULT_EQUALS_BEHAVIOUR = EqualityBehaviorOption.EQUALITY;
/**
* Creates a {@link org.kie.api.runtime.KieContainer} for all the knowledge bases found in the provided
* directory. If the provided argument is null, a default classpath container will be returned.
* <p>
* ENHANCEMENT: This method expects to find a knowledge base descriptor file, 'kmodule.xml' in the provided
* directory's sub-directory 'META-INF' among a maven project reference file ('pom.properties' and 'pom.xml', for
* Drools {@link org.kie.api.builder.ReleaseId}).
* This is inconvenient and a better way should exist to define it programmatically?
*
* @param knowledgeBaseDir the directory containing the the Drools knowledge base. When null is passed, the default
* classpath container will be returned.
* @return The container for all knowledge bases found in the provided directory.
*/
public static KieContainer createKieContainer(File knowledgeBaseDir) {
KieServices kServices = KieServices.Factory.get();
if (knowledgeBaseDir == null) {
return kServices.getKieClasspathContainer();
}
Resource fileSystemResource = kServices.getResources().newFileSystemResource(knowledgeBaseDir);
KieRepository kieRepository = kServices.getRepository();
KieModule kModule = kieRepository.addKieModule(fileSystemResource);
return kServices.newKieContainer(kModule.getReleaseId());
}
/**
* Creates a KieSession using the provided base and session name, as well as a number of arbitrary Drools rule files.
* By default, this session will be stateful, use a pseudo clock and perform reasoning in stream mode and
* use equality as object comparison mechanism.
*
* @param baseModelName the name of the knowledge base
* @param sessionModelName the name of the {@link org.kie.api.runtime.KieSession}
* @param ruleFiles a number of Drools rule files
* @return a {@link org.kie.api.runtime.KieSession} backed by the provided rule files using default options
*/
public static KieSession createKieSession(String baseModelName, String sessionModelName, File... ruleFiles) {
if (baseModelName == null || sessionModelName == null) {
throw new IllegalArgumentException();
}
KieServices kServices = KieServices.Factory.get();
KieModuleModel kModuleModel = kServices.newKieModuleModel();
KieBaseModel kBaseModel = kModuleModel.newKieBaseModel(baseModelName);
kBaseModel.setDefault(true).setEqualsBehavior(DEFAULT_EQUALS_BEHAVIOUR)
.setEventProcessingMode(DEFAULT_EVENT_PROCESSING);
KieSessionModel kSession = kBaseModel.newKieSessionModel(sessionModelName);
kSession.setDefault(true).setType(DEFAULT_KNOWLEDGE_SESSION_TYPE)
.setClockType(DEFAULT_CLOCK_TYPE);
KieFileSystem kFileSystem = kServices.newKieFileSystem();
kFileSystem.writeKModuleXML(kModuleModel.toXML());
for (File ruleFile : ruleFiles) {
kFileSystem.write(kServices.getResources().newFileSystemResource(ruleFile));
}
kServices.newKieBuilder(kFileSystem).buildAll();
KieContainer kContainer = kServices.newKieContainer(kServices.getRepository()
.getDefaultReleaseId());
return kContainer.newKieSession();
}
/**
* Creates the default {@link org.kie.api.runtime.KieSession} without changing any default values for the classpath
* container.
*
* @return a default {@link org.kie.api.runtime.KieSession} using the classpath container (no settings are applied)
*/
public static KieSession createDefaultKieSession() {
KieServices ks = KieServices.Factory.get();
KieContainer kContainer = ks.getKieClasspathContainer();
return kContainer.newKieSession();
}
/**
* Creates a {@link org.kie.api.runtime.KieSession} for the classpath container without
* changing any default options, using the knowledge base and session name exactly as they are provided.
*
* @param kBaseName the knowledge base name
* @param kSessionName the session name
* @return a default kie session instance for the knowledge base
*/
public static KieSession createKieSession(String kBaseName, String kSessionName) {
KieServices ks = KieServices.Factory.get();
KieContainer kContainer = ks.getKieClasspathContainer();
return createKieSession(kContainer, kBaseName, kSessionName);
}
/**
* Creates a new {@link org.kie.api.runtime.KieSession} without changing any default options, using the
* provided container, knowledge base and session name.
*
* @param kContainer the container to use
* @param kBaseName the knowledge base name
* @param kSessionName the session name
* @return a default kie session instance for the knowledge base in the provided container, or null if the container
* was null
*/
public static KieSession createKieSession(KieContainer kContainer, String kBaseName, String kSessionName) {
if (kContainer == null) {
return null;
}
KieSession kSession;
if (kSessionName == null || kSessionName.isEmpty()) {
kSession = kContainer.newKieSession();
} else {
kSession = kContainer.newKieSession(kSessionName);
}
return kSession;
}
}
@@ -0,0 +1,250 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.drools;
import at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.config.model.RubanetraSystemConfiguration;
import at.jku.fim.rubanetra.output.OutputWriterStrategy;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.pcap.PcapActivityListener;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.Activity;
import at.jku.fim.rubanetra.protocol.activity.DroolsBaseActivity;
import at.jku.fim.rubanetra.protocol.activity.ReplaceableActivity;
import org.drools.core.time.SessionPseudoClock;
import org.kie.api.event.rule.ObjectDeletedEvent;
import org.kie.api.event.rule.ObjectInsertedEvent;
import org.kie.api.event.rule.ObjectUpdatedEvent;
import org.kie.api.event.rule.RuleRuntimeEventListener;
import org.kie.api.runtime.KieSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.Closeable;
import java.io.IOException;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* This class may be considered the final link between the Kraken protocol parsing library, the Drools rule engine and
* the {@link at.jku.fim.rubanetra.output.OutputWriterStrategy}.
* It listens to all appropriate events creates by the protocol decoders and injects them into to Drools knowledge base
* session ({@link org.kie.api.runtime.KieSession}).
* Finally, it also reacts to Event removal events, which originate from the rule engine itself and are utilized
* as indicator to write these events to the pre-defined output stream before they are discarded from memory.
* <p>
* It is vital to invoke the {@link #close()} method after usage of the rule engine, because otherwise the
* remaining events and/or facts which are still in the active knowledge base will _not_ be written to the output stream
* and this will lead to missing output.
* <p>
* This class will provide certain global objects that may be used from the Drools rule files, e.g.
* a {@link org.slf4j.Logger} via "log" and the used {@link at.jku.fim.rubanetra.output.OutputWriterStrategy}
* via "outputWriter".
*/
public class DroolsKrakenProtocolHandler implements PcapActivityListener, Closeable {
private final Logger log = LoggerFactory.getLogger(getClass());
private final RubanetraSystemConfiguration systemConfig;
private final Map<ProtocolId, Set<DroolsProtocolPropertyChangeListener>> protocolListener = new HashMap<>();
private final SessionPseudoClock droolsClock;
private final OutputWriterStrategy outputWriterStrategy;
private KieSession kieSession;
private PcapActivity currentPcapEntry;
private Instant previousTimestamp, currentTimestamp;
private RuleRuntimeEventListener droolsRuntimeListener = new RuleRuntimeEventListener() {
@Override
public void objectInserted(ObjectInsertedEvent event) {
}
@Override
public void objectUpdated(ObjectUpdatedEvent event) {
}
@Override
public void objectDeleted(ObjectDeletedEvent event) {
/* every deleted/retracted event/fact will not be used for reasoning any more,
* therefore those objects can be written to the output stream and finally discarded from memory */
if (event.getOldObject() instanceof ReplaceableActivity) {
ReplaceableActivity oldActivity = (ReplaceableActivity) event.getOldObject();
if (!oldActivity.isReplaced() && !oldActivity.isExcludedFromOutput()) {
outputWriterStrategy.writeActivity(oldActivity);
}
logDeletedActivity(oldActivity);
} else if (event.getOldObject() instanceof Activity) {
Activity oldActivity = (Activity) event.getOldObject();
if (!oldActivity.isExcludedFromOutput()) {
outputWriterStrategy.writeActivity(oldActivity);
}
logDeletedActivity(oldActivity);
}
}
private void logDeletedActivity(Activity oldActivity) {
if (log.isDebugEnabled()) {
log.debug("Deleted activity: {} @ {} # {}", oldActivity.getActivityType(), currentTimestamp, oldActivity.getCompoundFrameNumbers());
}
}
};
/**
* Instantiates a new handler for a pre-initialized RubanetraSystemConfiguration
*
* @param systemConfig the system configuration to use, may not be null and must be initialized
*/
public DroolsKrakenProtocolHandler(RubanetraSystemConfiguration systemConfig) {
if (systemConfig == null) {
log.error("The system configuration must not constitute a null pointer");
throw new IllegalArgumentException();
}
this.systemConfig = systemConfig;
this.kieSession = systemConfig.getKieSession();
if (kieSession == null) {
log.error("The system configuration must contain a valid Kie-Session object, instead a null pointer was passed");
throw new IllegalArgumentException();
}
this.currentPcapEntry = null;
this.droolsClock = kieSession.getSessionClock();
outputWriterStrategy = systemConfig.getOutputWriterStrategy();
if (outputWriterStrategy == null) {
log.error("The system configuration must contain a valid output writer, instead a null pointer was passed");
throw new IllegalArgumentException();
}
initialize();
}
private void initialize() {
// listen to all appropriate events, however, try to prevent duplicates
for (KrakenProtocolConfiguration setting : systemConfig.getProtocolSettings()) {
Map<ProtocolId, KrakenBaseProtocol> boundProtocols = setting.getBoundProtocols();
for (ProtocolId id : boundProtocols.keySet()) {
if (!protocolListener.containsKey(id)) {
// new unique protocol discovered, therefore add a new listener
KrakenBaseProtocol krakenBaseProtocol = boundProtocols.get(id);
DroolsProtocolPropertyChangeListener changeListener = new DroolsProtocolPropertyChangeListener(krakenBaseProtocol);
krakenBaseProtocol.addPropertyChangeListener(changeListener);
protocolListener.put(id, new HashSet<>(Arrays.asList(changeListener)));
} else if (id instanceof KrakenApplicationProtocolId) {
// we want to receive all activities from the application layer, even for redundant application protocols,
// e.g. consider UDP->DNS and TCP->DNS.
// ENHANCEMENT:
// However, this will cause certain duplicate activities iff the application layer handler is bound
// to another handler above the application layer (this is currently not the case).
// As soon as this constraint is violated, an alternative mechanism has to be implemented.
KrakenBaseProtocol krakenBaseProtocol = boundProtocols.get(id);
DroolsProtocolPropertyChangeListener changeListener = new DroolsProtocolPropertyChangeListener(krakenBaseProtocol);
krakenBaseProtocol.addPropertyChangeListener(changeListener);
protocolListener.get(id).add(changeListener);
}
}
}
/**
* These variables are accessible from within the rule files
*/
kieSession.setGlobal("outputWriter", outputWriterStrategy);
kieSession.setGlobal("log", LoggerFactory.getLogger(DroolsBaseActivity.class));
kieSession.addEventListener(droolsRuntimeListener);
}
private void updateSessionClock() {
if (previousTimestamp == null) {
droolsClock.advanceTime(currentTimestamp.getEpochSecond(), TimeUnit.SECONDS);
droolsClock.advanceTime(currentTimestamp.getNano(), TimeUnit.NANOSECONDS);
log.debug("Setting initial Drools Pseudo clock time to {}", currentTimestamp);
log.debug("The current Drools clock time is {}, frame # {}", Instant.ofEpochMilli(droolsClock.getCurrentTime()),
currentPcapEntry.getFrameNumber());
} else if (previousTimestamp.isBefore(currentTimestamp)) {
Duration duration = Duration.between(previousTimestamp, currentTimestamp);
log.debug("Advancing Drools Pseudo clock by {}", duration);
droolsClock.advanceTime(duration.getSeconds(), TimeUnit.SECONDS);
droolsClock.advanceTime(duration.getNano(), TimeUnit.NANOSECONDS);
log.debug("The current Drools clock time is {}, reference {}, frame # {}",
Instant.ofEpochMilli(droolsClock.getCurrentTime()), currentTimestamp, currentPcapEntry.getFrameNumber());
}
}
@Override
public void processPcapActivity(PcapActivity pcapActivity) {
if (pcapActivity == null) {
log.debug("An invalid pcap activity was passed after {}", currentTimestamp);
return;
}
this.currentPcapEntry = pcapActivity;
this.previousTimestamp = currentTimestamp;
this.currentTimestamp = pcapActivity.getPcapTimestamp();
updateSessionClock();
kieSession.fireAllRules();
}
/**
* Closes the drools session and deletes, i.e. retracts, all remaining facts and releases any system resources associated
* with it. If the session is already closed then invoking this method has no effect.
* This method will clode the used {@link at.jku.fim.rubanetra.output.OutputWriterStrategy} and
* the used {@link org.kie.api.runtime.KieSession}.
*
* @throws java.io.IOException if an I/O error occurs
*/
@Override
public void close() throws IOException {
if (kieSession != null) {
kieSession.getFactHandles().forEach(kieSession::delete);
kieSession.dispose();
systemConfig.getOutputWriterStrategy().closeWriter();
kieSession = null;
}
}
/**
* This class listens to all events that carry an {@link at.jku.fim.rubanetra.protocol.activity.Activity}
* and injects them into the knowledge session
*/
private class DroolsProtocolPropertyChangeListener implements PropertyChangeListener {
private final KrakenBaseProtocol source;
private DroolsProtocolPropertyChangeListener(KrakenBaseProtocol source) {
this.source = source;
}
/**
* This method gets called when a bound property is changed.
*
* @param evt A PropertyChangeEvent object describing the event source
* and the property that has changed.
*/
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() instanceof Activity) {
kieSession.insert(evt.getNewValue());
} else {
log.debug("Passed event object is not an Activity: {}", evt.getNewValue());
}
}
}
}
@@ -0,0 +1,36 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.output;
import org.apache.commons.csv.CSVPrinter;
import java.io.IOException;
/**
* This interface should be implemented by all {@link at.jku.fim.rubanetra.protocol.activity.Activity} implementations
* that can provide their data in a comma separated fashion.
*/
public interface CsvRecordOutputWriter {
/**
* Write a single record, i.e. one line, using the provided csvPrinter to the output stream.
*
* @param csvPrinter An initialized csvPrinter, shared among all activities
* @throws IOException If it is not possible to write to the output stream or the printer reached an invalid state.
*/
public void writeCsvRecord(CSVPrinter csvPrinter) throws IOException;
}
@@ -0,0 +1,46 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.output;
import at.jku.fim.rubanetra.protocol.activity.Activity;
import java.io.IOException;
/**
* Defines methods for writing {@link at.jku.fim.rubanetra.protocol.activity.Activity} to a provided
* output-stream. Implementations of this interface represent the transformation to apply to the passed activties
* before they are written out.
*/
public interface OutputWriterStrategy {
/**
* Write an {@link at.jku.fim.rubanetra.protocol.activity.Activity} to the provided output-stream
* using the implementation-specific transformation of the content.
*
* @param activity the activity that will be transformed according to the strategy in use and written to the
* provided output-stream
*/
public void writeActivity(Activity activity);
/**
* Closes the writer but does not release or close the underlying output stream.
* If the writer is already closed then invoking this
* method has no effect.
*/
public void closeWriter() throws IOException;
}
@@ -0,0 +1,85 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.output.impl;
import at.jku.fim.rubanetra.output.CsvRecordOutputWriter;
import at.jku.fim.rubanetra.output.OutputWriterStrategy;
import at.jku.fim.rubanetra.protocol.activity.Activity;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
/**
* Created by stefan on 9/30/14.
*/
public class CsvOutputWriterStrategy implements OutputWriterStrategy {
private final Logger log = LoggerFactory.getLogger(getClass());
private final CSVPrinter csvPrinter;
public CsvOutputWriterStrategy(OutputStream outputStream) throws IOException {
if (outputStream == null) {
log.error("Cannot write to passed outputstream - null pointer");
throw new NullPointerException();
}
this.csvPrinter = new CSVPrinter(new BufferedWriter(new OutputStreamWriter(outputStream)), CSVFormat.RFC4180);
}
/**
* Write an {@link at.jku.fim.rubanetra.protocol.activity.Activity} to the provided output-stream
* using the implementation-specific transformation of the content.
*
* @param activity the activity that will be transformed according to the strategy in use and written to the
* provided output-stream
*/
@Override
public void writeActivity(Activity activity) {
if (activity == null) {
log.warn("Skipping activity - null pointer");
return;
}
if (CsvRecordOutputWriter.class.isAssignableFrom(activity.getClass())) {
try {
((CsvRecordOutputWriter) activity).writeCsvRecord(csvPrinter);
} catch (IOException e) {
e.printStackTrace();
log.warn("Unable to serialize activity {} to the outputstream, skipping.", activity.getCompoundFrameNumbers());
log.debug("Exception:", e);
}
} else {
log.warn("Skipping activity - CSVRecordOutputWriter interface is not implemented by class {}", activity.getClass());
}
}
/**
* Closes the writer but does not release or close the underlying output stream.
* If the writer is already closed then invoking this
* method has no effect.
*/
@Override
public void closeWriter() throws IOException {
csvPrinter.flush();
csvPrinter.close();
}
}
@@ -0,0 +1,138 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.output.impl;
import at.jku.fim.rubanetra.output.OutputWriterStrategy;
import at.jku.fim.rubanetra.output.mixin.HttpMixIn;
import at.jku.fim.rubanetra.output.mixin.PcapMixIn;
import at.jku.fim.rubanetra.protocol.activity.Activity;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.protocol.tcpip.Http;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.OutputStream;
/**
* This class implements a Json specific transformation (using the Jackson library) of the derived Event/Fact/Activity-data.
* By default, it tries to pretty-print the resulting JSON-output, flushes the output stream after each written
* activity and does not fail on empty activities, nor will it close the used output-stream.
* Because certain classes do not provide appropriate inclusion or exclusion annotations, certain MixIn classes
* will be used as substitute.
* <p>
* Additionally the implementation version, vendor and title is provided for external parsers.
*
* @see at.jku.fim.rubanetra.output.mixin.HttpMixIn
* @see at.jku.fim.rubanetra.output.mixin.PcapMixIn
*/
public class JsonOutputWriterStrategy implements OutputWriterStrategy {
private final Logger log = LoggerFactory.getLogger(getClass());
private final OutputStream outputStream;
private final ObjectMapper mapper;
private JsonGenerator jsonGenerator;
/**
* Creates a new instance of this strategy, all passed activities will be written to the provided output-stream.
*
* @param outputStream the output-stream to write the received activity-data in JSON-format to
*/
public JsonOutputWriterStrategy(OutputStream outputStream) {
if (outputStream == null) {
log.error("Cannot write to passed outputstream - null pointer");
throw new NullPointerException();
}
this.outputStream = outputStream;
mapper = new ObjectMapper();
// to enable standard indentation ("pretty-printing"):
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.enable(SerializationFeature.FLUSH_AFTER_WRITE_VALUE);
// to allow serialization of "empty" POJOs (no properties to serialize)
// (without this setting, an exception is thrown in those cases)
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// to write java.util.Date, Calendar as number (timestamp):
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.disable(SerializationFeature.CLOSE_CLOSEABLE);
mapper.addMixInAnnotations(Http.class, HttpMixIn.class);
mapper.addMixInAnnotations(PcapPacket.class, PcapMixIn.class);
initialize();
}
private void initialize() {
JsonFactory jsonFactory = mapper.getFactory();
Package classPackage = getClass().getPackage();
try {
jsonGenerator = jsonFactory.createGenerator(outputStream, JsonEncoding.UTF8);
jsonGenerator.useDefaultPrettyPrinter();
jsonGenerator.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
jsonGenerator.enable(JsonGenerator.Feature.AUTO_CLOSE_JSON_CONTENT);
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("implementationTitle", classPackage.getImplementationTitle());
jsonGenerator.writeStringField("implementationVersion", classPackage.getImplementationVersion());
jsonGenerator.writeStringField("implementationVendor", classPackage.getImplementationVendor());
jsonGenerator.writeArrayFieldStart("activities");
} catch (IOException e) {
log.error("Unable to create Json generator required for the selected output strategy.", e);
throw new IllegalStateException();
}
}
@Override
public void writeActivity(Activity activity) {
if (activity == null) {
log.warn("Skipping activity - null pointer");
return;
}
try {
mapper.writeValue(jsonGenerator, activity);
// mapper.writeValue(outputStream,activity);
// System.out.println(mapper.writeValueAsString(activity));
} catch (IOException e) {
log.warn("Unable to serialize activity {} to the outputstream, skipping.", activity);
log.debug("Exception:", e);
}
}
@Override
public void closeWriter() {
if (jsonGenerator != null) {
try {
jsonGenerator.writeEndArray();
jsonGenerator.writeEndObject();
jsonGenerator.flush();
jsonGenerator.close();
jsonGenerator = null;
} catch (IOException e) {
log.warn("Error occurred while closing the writer", e);
}
}
}
}
@@ -0,0 +1,39 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.output.impl;
import at.jku.fim.rubanetra.output.OutputWriterStrategy;
import at.jku.fim.rubanetra.protocol.activity.Activity;
import java.io.IOException;
/**
* This implementation of the {@link at.jku.fim.rubanetra.output.OutputWriterStrategy} does nothing.
* May be useful for debugging purposes without wasting I/O resources.
*/
public class NopOutputWriterStrategy implements OutputWriterStrategy {
@Override
public void writeActivity(Activity activity) {
// do nothing
}
@Override
public void closeWriter() throws IOException {
// do nothing
}
}
@@ -0,0 +1,144 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.output.impl;
import at.jku.fim.rubanetra.output.OutputWriterStrategy;
import at.jku.fim.rubanetra.output.mixin.HttpMixIn;
import at.jku.fim.rubanetra.output.mixin.PcapMixIn;
import at.jku.fim.rubanetra.protocol.activity.Activity;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlFactory;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import com.fasterxml.jackson.dataformat.xml.util.DefaultXmlPrettyPrinter;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.protocol.tcpip.Http;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.io.OutputStream;
/**
* Created by stefan on 9/30/14.
*/
public class XmlOutputWriterStrategy implements OutputWriterStrategy {
public static final QName QNAME_ACTIVITY_ARRAY = new QName("activityArray");
public static final QName QNAME_ACTIVITY = new QName("activity");
private final Logger log = LoggerFactory.getLogger(getClass());
private final XmlMapper mapper;
private ToXmlGenerator xmlGenerator;
public XmlOutputWriterStrategy(OutputStream outputStream) {
if (outputStream == null) {
log.error("Cannot write to passed outputstream - null pointer");
throw new NullPointerException();
}
this.mapper = new XmlMapper();
assignDefaultFeatures(mapper);
final XmlFactory xmlFactory = this.mapper.getFactory();
Package classPackage = getClass().getPackage();
try {
xmlGenerator = xmlFactory.createGenerator(outputStream, JsonEncoding.UTF8);
DefaultXmlPrettyPrinter pp = new DefaultXmlPrettyPrinter();
xmlGenerator.setPrettyPrinter(pp);
xmlGenerator.enable(ToXmlGenerator.Feature.WRITE_XML_1_1);
QName rootQName = new QName("root");
xmlGenerator.initGenerator();
xmlGenerator.setNextName(rootQName);
xmlGenerator.writeStartObject();
final String unknown = "Unknown";
xmlGenerator.writeStringField("implementationTitle", classPackage.getImplementationTitle() == null ?
unknown : classPackage.getImplementationTitle());
xmlGenerator.writeStringField("implementationVersion", classPackage.getImplementationVersion() == null ?
unknown : classPackage.getImplementationVersion());
xmlGenerator.writeStringField("implementationVendor", classPackage.getImplementationVendor() == null ?
unknown : classPackage.getImplementationVendor());
xmlGenerator.startWrappedValue(QNAME_ACTIVITY_ARRAY, QNAME_ACTIVITY);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void assignDefaultFeatures(ObjectMapper mapper) {
// to enable standard indentation ("pretty-printing"):
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.enable(SerializationFeature.FLUSH_AFTER_WRITE_VALUE);
// to allow serialization of "empty" POJOs (no properties to serialize)
// (without this setting, an exception is thrown in those cases)
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// to write java.util.Date, Calendar as number (timestamp):
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.disable(SerializationFeature.CLOSE_CLOSEABLE);
mapper.addMixInAnnotations(Http.class, HttpMixIn.class);
mapper.addMixInAnnotations(PcapPacket.class, PcapMixIn.class);
}
/**
* Write an {@link at.jku.fim.rubanetra.protocol.activity.Activity} to the provided output-stream
* using the implementation-specific transformation of the content.
*
* @param activity the activity that will be transformed according to the strategy in use and written to the
* provided output-stream
*/
@Override
public void writeActivity(Activity activity) {
if (activity == null) {
log.warn("Skipping activity - null pointer");
return;
}
try {
mapper.writeValue(xmlGenerator, activity);
} catch (IOException e) {
log.warn("Unable to serialize activity {} to the outputstream, skipping.", activity.getCompoundFrameNumbers());
log.debug("Exception: {}", e);
}
}
/**
* Closes the writer but does not release or close the underlying output stream.
* If the writer is already closed then invoking this
* method has no effect.
*/
@Override
public void closeWriter() throws IOException {
if (xmlGenerator != null) {
xmlGenerator.flush();
// xmlGenerator.writeEndArray();
try {
xmlGenerator.getStaxWriter().writeEndDocument();
} catch (XMLStreamException e) {
e.printStackTrace();
}
// xmlGenerator.close();
xmlGenerator = null;
}
}
}
@@ -0,0 +1,42 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.output.mixin;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* This Mixin will be used by the {@link at.jku.fim.rubanetra.output.impl.JsonOutputWriterStrategy}
* to provide Jackson specific annotations to define the properties and fields of the external
* {@link org.jnetpcap.protocol.tcpip.Http} class that are to be included in the final output.
*/
@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
public abstract class HttpMixIn {
@JsonProperty("JNetPcap-HTTP-String")
public abstract String toString();
@JsonProperty
public abstract String header();
@JsonProperty
public abstract boolean isResponse();
@JsonProperty
public abstract String contentType();
}
@@ -0,0 +1,43 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.output.mixin;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* This Mixin will be used by the {@link at.jku.fim.rubanetra.output.impl.JsonOutputWriterStrategy}
* to provide Jackson specific annotations to define the properties and fields of the external
* {@link org.jnetpcap.Pcap} class that are to be included in the final output.
*/
@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
public abstract class PcapMixIn {
@JsonProperty
abstract int getTotalSize();
@JsonProperty
abstract long getFrameNumber();
@JsonProperty
abstract int getHeaderCount();
@JsonProperty
abstract int getPacketWirelen();
}
@@ -0,0 +1,118 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.pcap;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.jnetpcap.packet.PcapPacket;
import org.kie.api.definition.type.Role;
import org.kie.api.definition.type.Role.Type;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
/**
* A PcapActivity may be interpreted as a Network Layer 1 activity type, because all common PCAP-Files contain
* individual Pcap-entries which are wrapped by this class in order to provide PCAP-specific metadata per entry.
* This class currently supports the JNetPcap library only (which in turn relies on the native libpcap library),
* however, if the limited Kraken-Pcap-Parser has to be used, consider converting the Kraken-specific Pcap packet
* to a {@link org.jnetpcap.packet.JMemoryPacket} in a manner similar to the following (at your own risk):
* <p>
* {@code byte[] arr = new byte[getPcapPacket().getPacketData().readableBytes()];}
* <p>
* {@code new ChainBuffer(getPcapPacket().getPacketData()).gets(arr);}
* <p>
* {@code JMemoryPacket jPacket = new JMemoryPacket(arr);}
* <p>
* {@code jPacket.scan(Ethernet.ID);}
* <p>
* {@code Ethernet header = jPacket.getHeader(new Ethernet());}
* <p>
* If a JNetPcap specific {@link org.jnetpcap.packet.PcapPacket} must be passed to a Kraken parser, the class
* {@link at.jku.fim.rubanetra.pcap.PcapPacketToKrakenPcapPacketAdapter} may be useful.
*
* @see at.jku.fim.rubanetra.pcap.PcapPacketToKrakenPcapPacketAdapter
*/
@Role(Type.EVENT)
public class PcapActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = 1884831815993828991L;
private final long frameNumber;
private final PcapPacket pcapPacket;
private final Instant pcapTimestamp;
/**
* Create a new PcapActivity using the provided frame number (as seen by the
* {@link at.jku.fim.rubanetra.pcap.PcapFileHandler}) and the JNetPcap specific PCAP packet.
*
* @param frameNumber the frame number of the packet, reconstructed by the {@link at.jku.fim.rubanetra.pcap.PcapFileHandler}
* @param pcapPacket the JNetPcap specific PCAP packet representation
*/
public PcapActivity(long frameNumber, PcapPacket pcapPacket) {
super(pcapPacket);
this.frameNumber = frameNumber;
this.pcapPacket = pcapPacket;
// the JNetPcap timestamp will be interpreted as a Java 8 specific Instant in time
this.pcapTimestamp = Instant.ofEpochSecond(pcapPacket.getCaptureHeader().hdr_sec()).
plus(Duration.of(pcapPacket.getCaptureHeader().hdr_usec(), ChronoUnit.MICROS));
setStartInstant(pcapTimestamp);
setEndInstant(pcapTimestamp);
}
/**
* @return The JNetPcap specific representation of this packet capture activity
*/
public PcapPacket getPcapPacket() {
return this.pcapPacket;
}
/**
* @return the frame number of this packet capture activity as reconstructed by the used
* {@link at.jku.fim.rubanetra.pcap.PcapHandler}
*/
public long getFrameNumber() {
return this.frameNumber;
}
/**
* @return The Java 8 specific Instant in time, which was built from the JNetPcap specific timestamp information.
* @see org.jnetpcap.packet.PcapPacket#getCaptureHeader()
*/
public Instant getPcapTimestamp() {
return pcapTimestamp;
}
@Override
public Collection<Long> getCompoundFrameNumbers() {
return Collections.unmodifiableCollection(Arrays.asList(getFrameNumber()));
}
@Override
public String toString() {
return "PcapActivity{" +
"frameNumber=" + frameNumber +
", pcapPacket=" + pcapPacket +
", pcapTimestamp=" + pcapTimestamp +
'}';
}
}
@@ -0,0 +1,32 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.pcap;
/**
* A listener interface for packet capture activities produced by implementations of a
* {@link at.jku.fim.rubanetra.pcap.PcapHandler}.
*/
public interface PcapActivityListener {
/**
* Do something useful with the Layer 1 packet capture metadata and the actually parsed content
*
* @param pcapActivity the wrapper of the JNetPcap specific {@link org.jnetpcap.packet.PcapPacket} that was parsed
* by a {@link at.jku.fim.rubanetra.pcap.PcapHandler}.
*/
public abstract void processPcapActivity(PcapActivity pcapActivity);
}
@@ -0,0 +1,246 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.pcap;
import org.jnetpcap.Pcap;
import org.jnetpcap.PcapBpfProgram;
import org.jnetpcap.packet.JPacket;
import org.jnetpcap.packet.JPacketHandler;
import org.jnetpcap.packet.PcapPacket;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.stream.Stream;
/**
* Implements a pcap handler for parsing one or multiple files adhering to a supported PCAP-definition (JNetPcap's
* native Pcap library, libpcap, will be used).
* This class is subject to future changes, currently it employs a custom way to support multiple files, i.e.:
* <ul>
* <li>if multiple files are passed and the sortByPcapTimestamp flag is set to false, all files are treated in
* the order they have been passed</li>
* <li>if multiple files are passed and the sortByPcapTimestamp flag is set to true, all files will be opened and
* the first available pcap entry will be parsed. All timestamps of the timestamps will be compared against each
* other and a chronological file-order will be established. This mechanism will potentially fail if
* the files contain overlapping time intervals. A possibly suitable future implementation for this use case
* could employ a chronologically sorted buffer to alleviate the effects of this issue.</li>
* <li>if a single file is passed, the sortByPcapTimestamp flag will be ignored</li>
* </ul>
* <p>
* Multiple {@link at.jku.fim.rubanetra.pcap.PcapActivityListener} may be registered with an instance of
* this class, note however that the events will be dispatched sequentially in the listener registration order.
* The only exception to this rule is a listener object that is passed directly as parameter to the
* {@link #readNextPcapEntry(PcapActivityListener)} as this object will be the first one to receive a callback with
* the appropriate {@link at.jku.fim.rubanetra.pcap.PcapActivity} event.
*
* @param <T> A custom implementation of a {@link at.jku.fim.rubanetra.pcap.PcapActivityListener} that
* will be called back before all other registered listeners as long as it is passed as argument to the
* {@link #readNextPcapEntry(PcapActivityListener)} method.
*/
public class PcapFileHandler<T extends PcapActivityListener> implements PcapHandler<T> {
private final Logger log = LoggerFactory.getLogger(getClass());
private final StringBuilder errorBuilder = new StringBuilder();
private final Queue<File> pcapInputFileQueue;
private final int numberOfInputFiles;
private final boolean sortByPcapTimestamp;
private final List<PcapActivityListener> fileActivityListeners;
private final JPacketHandler<PcapActivityListener> pcapPacketHandler = this::processNextPacket;
private final PcapFilter pcapFilter;
private long currentFrameNumber;
private PcapActivity currentPcapActivity;
private Pcap currentPcapHandle;
/**
* Instantiates a new pcap file handler using the passed Berkeley Packet Filter pcapFilter and a number of
* input files. These files will be sorted according to the first encountered timestamp in the pcap entries of each
* file iff sortByPcapTimestamp is passed as true.
*
* @param pcapFilter the Berkeley Packet Filter to apply to all specified pcapFiles
* @param sortByPcapTimestamp whether or not to sort the pcapFiles chronologically according to the first
* encountered timestamp of the individual pcap entries
* @param pcapFiles the files to parse (must adhere to a PCAP specification supported by JNetPcap/libpcap)
* @throws java.lang.IllegalArgumentException if no input files are passed
*/
public PcapFileHandler(PcapFilter pcapFilter, boolean sortByPcapTimestamp, File... pcapFiles) {
if (pcapFiles == null || pcapFiles.length <= 0) {
throw new IllegalArgumentException("No input files have been passed to the Pcap File Handler.");
}
this.pcapFilter = pcapFilter;
this.pcapInputFileQueue = new ConcurrentLinkedQueue<>();
this.sortByPcapTimestamp = sortByPcapTimestamp;
if (sortByPcapTimestamp && pcapFiles.length > 1) {
sortInputFilesByPcapTimestamp(pcapFiles);
} else {
Collections.addAll(this.pcapInputFileQueue, pcapFiles);
}
this.numberOfInputFiles = pcapFiles.length;
this.fileActivityListeners = new LinkedList<>();
this.currentFrameNumber = 1;
}
/**
* Instantiates a new pcap file handler using no Berkeley Packet Filter.
*
* @param sortByPcapTimestamp whether or not to sort the pcapFiles chronologically according to the first
* encountered timestamp of the individual pcap entries
* @param pcapFiles the files to parse (must adhere to a PCAP specification supported by JNetPcap/libpcap)
* @throws java.lang.IllegalArgumentException if no input files are passed
* @see #PcapFileHandler(PcapFilter, boolean, java.io.File...)
*/
public PcapFileHandler(boolean sortByPcapTimestamp, File... pcapFiles) {
this(null, sortByPcapTimestamp, pcapFiles);
}
/**
* Instantiates a new pcap file handler using no Berkeley Packet Filter and does not sort the input files.
*
* @param pcapFiles the files to parse (must adhere to a PCAP specification supported by JNetPcap/libpcap)
* @throws java.lang.IllegalArgumentException if no input files are passed
* @see #PcapFileHandler(PcapFilter, boolean, java.io.File...)
*/
public PcapFileHandler(File... pcapFiles) {
this(null, false, pcapFiles);
}
/**
* Tries to sort the input files using one {@link at.jku.fim.rubanetra.pcap.PcapFileHandler}
* per file. This is quite inefficient, therefore:
* ENHANCEMENT: implement in place sort and/or handle overlapping time intervals
*
* @param pcapFiles the input files to sort
*/
private void sortInputFilesByPcapTimestamp(File[] pcapFiles) {
HashMap<PcapActivity, File> firstPcapActivities = new HashMap<>();
for (File f : pcapFiles) {
PcapFileHandler fileHandler = new PcapFileHandler(pcapFilter, false, f);
fileHandler.readNextPcapEntry(pcapActivity -> firstPcapActivities.put(pcapActivity, f));
fileHandler.close();
}
Stream<PcapActivity> sortedPcapActivities = firstPcapActivities.keySet().stream()
.sorted((o1, o2) -> o1.getPcapTimestamp().compareTo(o2.getPcapTimestamp()));
sortedPcapActivities.forEachOrdered(pcapActivity -> this.pcapInputFileQueue.add(firstPcapActivities.get(pcapActivity)));
}
@Override
public boolean readNextPcapEntry(PcapActivityListener processor) {
if (this.currentPcapHandle == null && !switchToNextReadablePcapFile()) {
return false;
}
int returnCode = this.currentPcapHandle.dispatch(1, pcapPacketHandler, processor);
return returnCode > 0 || switchToNextReadablePcapFile() && readNextPcapEntry(processor);
}
private void processNextPacket(JPacket packet, PcapActivityListener user) {
// packet.scan(JRegistry.mapDLTToId(currentPcapHandle.datalink()));
PcapPacket deepCopy = new PcapPacket(packet);
this.currentPcapActivity = new PcapActivity(this.currentFrameNumber++, deepCopy);
for (PcapActivityListener l : fileActivityListeners) {
l.processPcapActivity(currentPcapActivity);
}
if (user != null)
user.processPcapActivity(currentPcapActivity);
}
/**
* This method opens, if possible, the next available input file and applies the Pcap filter and closes
* the previous file handle. Because no checks have been performed until now (whether the next file truly is a
* valid Pcap file) it tries to skip over invalid files until another valid file has been found.
* An alternative could constitute a generic bail out.
*
* @return true, iff the switch to another readable PCAP-file was performed successfully
*/
private boolean switchToNextReadablePcapFile() {
boolean switchSuccessful = false;
while (!switchSuccessful && !this.pcapInputFileQueue.isEmpty()) {
if (this.currentPcapHandle != null) {
this.currentPcapHandle.close();
this.currentPcapHandle = null;
}
File currentPcapFile = this.pcapInputFileQueue.remove();
try {
this.currentPcapHandle = Pcap.openOffline(currentPcapFile.getCanonicalPath(),
errorBuilder);
applyFilter();
switchSuccessful = this.currentPcapHandle != null;
} catch (IOException e) {
log.warn(String.format("Unable to read file %s, trying next file..., reason: %s%n",
currentPcapFile, errorBuilder.toString()));
errorBuilder.setLength(0);
switchSuccessful = false;
}
}
return switchSuccessful;
}
private void applyFilter() {
if (this.pcapFilter != null && this.currentPcapHandle != null) {
PcapBpfProgram bpfProgram = new PcapBpfProgram();
if (Pcap.OK != this.currentPcapHandle.compile(bpfProgram,
pcapFilter.getBpfExpression(), pcapFilter.isOptimize() ? 1 : 0,
pcapFilter.getNetmask())) {
log.warn(this.currentPcapHandle.getErr());
return;
}
if (Pcap.OK != this.currentPcapHandle.setFilter(bpfProgram)) {
log.warn(this.currentPcapHandle.getErr());
}
}
}
@Override
public void close() {
if (this.currentPcapHandle != null) {
this.currentPcapHandle.close();
this.currentPcapHandle = null;
}
}
@Override
public PcapActivity getCurrentPcapActivity() {
return currentPcapActivity;
}
@Override
public void addPcapProcessor(T processor) {
this.fileActivityListeners.add(processor);
}
@Override
public void removePcapProcessor(T processor) {
this.fileActivityListeners.remove(processor);
}
public int getNumberOfInputFiles() {
return numberOfInputFiles;
}
public boolean isSortByPcapTimestamp() {
return sortByPcapTimestamp;
}
}
@@ -0,0 +1,66 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.pcap;
/**
* Represent an intermediary between the native Berkeley Packet Filter compiler and this project external interface.
* It is intended as a mere encapsulation of required and optional parameters before passing it as raw values to
* the JNetPcap engine for native compilation. Error/Syntax checks will not be performed by this class.
*/
public class PcapFilter {
private final String bpfExpression;
private final boolean optimize;
private final int netmask;
/**
* Default constructor
*
* @param bpfExpression a Berkeley Packet Filter expression, may be null or empty
* @param optimize whether or not to try to optimize the provided filter expression.
* @param netmask an integer representing a network mask that will be applied by the native compiler.
*/
public PcapFilter(String bpfExpression, boolean optimize, int netmask) {
this.bpfExpression = bpfExpression;
this.optimize = optimize;
this.netmask = netmask;
}
/**
* @return a Berkeley Packet Filter expression, may be null or empty
*/
public String getBpfExpression() {
return bpfExpression;
}
/**
* JNetPcap expects an integer value, where 0 is equal to false.
*
* @return whether or not to try to optimize the provided filter expression.
*/
public boolean isOptimize() {
return optimize;
}
/**
* @return an integer representing a network mask that will be applied by the native compiler.
*/
public int getNetmask() {
return netmask;
}
}
@@ -0,0 +1,72 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.pcap;
import java.io.Closeable;
import java.io.IOException;
public interface PcapHandler<T extends PcapActivityListener> extends Closeable {
/**
* Read the next Pcap entry from the respective input stream.
* Implementations of this method must ensure that the passed processor parameter is called after all registered
* PcapProcessors had a chance to handle the Pcap-entry, i.e. after the decoding chain has been traversed.
* This constraint ensures that the users pcap processor may access subsequently decoded data.
* The methods signature is still subject to change, i.e. it is planned that the boolean return value will
* be changed to an integer value indicating a kind of error code.
*
* @param processor A Pcap-Entry processor capable of handling the read PcapActivity.
* @return true, iff the entry has been read and parsed successfully, false otherwise (e.g. end of stream).
*/
public boolean readNextPcapEntry(T processor);
/**
* A convenience method that provides a reference to the currently processed PcapActivity, i.e. it returns the
* latest successfully decoded Pcap entry.
*
* @return the latest successfully decoded PcapActivity
*/
public PcapActivity getCurrentPcapActivity();
/**
* Adds a custom listener or processor to the set of already registered listeners.
* This processor will receive a notification of all decoded Pcap entries after the previously registered
* processors have been notified.
*
* @param processor a custom listener to decoded pcap entries and related events
*/
public void addPcapProcessor(T processor);
/**
* Removes a custom listener or processor from the set of already registered listeners.
* This processor will no longer receive a notification of decoded Pcap entries.
*
* @param processor a registered listener
*/
public void removePcapProcessor(T processor);
/**
* Closes the underlying input streams (if not closed already) and releases any system resources associated
* with it. If the streams were already closed then invoking this
* method has no effect.
*
* @throws IOException if an I/O error occurs
*/
@Override
public void close() throws IOException;
}
@@ -0,0 +1,78 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.pcap;
import org.jnetpcap.PcapHeader;
import org.krakenapps.pcap.packet.PacketHeader;
import org.krakenapps.pcap.packet.PcapPacket;
import org.krakenapps.pcap.util.Buffer;
import org.krakenapps.pcap.util.ChainBuffer;
/**
* This class is an adapter for a given {@link org.jnetpcap.packet.PcapPacket}. It may be used as a drop-in replacement
* for a Kraken-specific {@link org.krakenapps.pcap.packet.PcapPacket}, i.e. instances of this class may be passed
* directly to a Kraken decoder pipeline.
* This class is still considered experimental, due to potentially dangerous long->int conversions and
* different naming conventions among the utilized libraries.
*/
public class PcapPacketToKrakenPcapPacketAdapter extends PcapPacket {
private final org.jnetpcap.packet.PcapPacket pcapPacket;
/**
* Takes a JNetPcap specific PcapPacket and creates a Kraken header and payload-buffer.
*
* @param pcapPacket the JNetPcap packet to adapt into a Kraken packet
*/
public PcapPacketToKrakenPcapPacketAdapter(org.jnetpcap.packet.PcapPacket pcapPacket) {
super(createKrakenHeader(pcapPacket), createKrakenPayloadBuffer(pcapPacket));
this.pcapPacket = pcapPacket;
}
/**
* @return the adapted JNetPcap packet
*/
public org.jnetpcap.packet.PcapPacket getPcapPacket() {
return pcapPacket;
}
private static PacketHeader createKrakenHeader(org.jnetpcap.packet.PcapPacket pcapPacket) {
PcapHeader header = pcapPacket.getCaptureHeader();
int origLen = header.wirelen();
int inclLen = header.caplen();
int hdr_sec = checkedInt(header.hdr_sec());
int hdr_usec = header.hdr_usec();
return new PacketHeader(hdr_sec, hdr_usec, inclLen, origLen);
}
private static Buffer createKrakenPayloadBuffer(org.jnetpcap.packet.PcapPacket pcapPacket) {
Buffer krakenBuffer = new ChainBuffer();
byte[] packetArray = pcapPacket.getByteArray(0, pcapPacket.size());
krakenBuffer.addFirst(packetArray);
return krakenBuffer;
}
private static int checkedInt(long l) {
if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
throw new IllegalArgumentException(String.format("Cannot cast %d to int without data loss", l));
}
return (int) l;
}
}
@@ -0,0 +1,60 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol;
import at.jku.fim.rubanetra.config.model.ProtocolId;
/**
* This is the main interface that currently all provided protocol decoders have to implement.
* The currently employed binding mechanism is up to the implementations, e.g. a {@link at.jku.fim.rubanetra.protocol.KrakenBaseProtocol}
* based implementation relies on reflection in order to provide multiple {@link #bind(BaseProtocol)} overrides.
* In order to provide an internal registry mechanism for this framework every protocol decoder must advertise a
* unique protocol identifier that it is able to parse and decode.
*/
public interface BaseProtocol {
/**
* An arbitrary identifier for the protocol(s) this decoder should be able to parse and decode.
* This identifier must be unique and may not be reused multiple times inside this framework for multiple decoders,
* even if the same protocol can be decoded by multiple implementations.
*
* @return a unique identifier used to represent the decoding capabilities of this decoder
*/
public abstract ProtocolId getProtocolId();
/**
* A protocol decoder should be able to pass its decoded data to another parser. Therefore it should be possible
* to bind multiple decoders to form a decoding pipeline, similar to the Java I/O stream processing pipeline.
* One way to implement the network stack would be to statically define which decoder is able to decode which
* data on a certain layer. However, this approach seems quite error prone and must be adjusted regularly.
* The currently utilized mechanism relies on the protocol decoder implementation advertisements and the user
* specifications instead. This way, the protocol parsers can extend their capabilities easily by providing a new
* implementation of this bind-method and the user may directly specify the decoding pipeline.
* Usually, protocol decoders bind-methods are called depending on user input, therefore every implementing class
* should handle errors gracefully.
*
* @param baseProtocol the protocol decoder to bind this decoder to, e.g. if base protocol refers to a decoder
* capable of parsing IPv4 data and this class is able to decode Ethernet, and the user
* specified a binding from Ethernet to IPv4, then Ethernet-decoder.bind(IPv4.decoder) will be
* called once. All necessary steps must be taken by the caller and callee to ensure
* successful data exchange. Currently the data exchange should be kept one way only,
* i.e. data will flow from a lower layer to an upper layer only and not vice versa (unless
* the user specified a binding from Ipv4 to Ethernet).
*/
public abstract void bind(BaseProtocol baseProtocol);
}
@@ -0,0 +1,191 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.pcap.PcapActivityListener;
import at.jku.fim.rubanetra.pcap.PcapHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* An abstract definition of a possible Kraken protocol parser and decoder wrapper.
* It provides an experimental implementation of the basic {@link at.jku.fim.rubanetra.protocol.BaseProtocol#bind(BaseProtocol)}}
* specification which relies on reflection to provide a convenient way for subclasses to provide multiple bindings:
* While it is possible for subclasses to override this mechanism, subclasses should instead define several bind-method
* signatures directly, i.e. it should advertise possible bindings directly via the available bind-methods.
* In order to rely on this feature is is mandatory for implementing classes to:
* <ul>
* <li>define one or multiple 'bind' methods with differing signatures</li>
* <li>adhere to the naming convention, i.e. all methods must be called 'bind'</li>
* <li>should not call 'bind' methods directly (bew aware of calling loops/recursion!),
* as the input handler usually performs this step using a registry of available decoders</li>
* </ul>
* <p>
* Alternatively, a subclass may disregard this feature and implement a suitable {@link #bind(BaseProtocol)} method
* on its own.
*/
public abstract class KrakenBaseProtocol implements BaseProtocol {
public static final String BINDING_METHOD_NAME = "bind";
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
protected KrakenProtocolConfiguration protocolSetting;
private boolean settingIsInitialized = false;
private PcapHandler<PcapActivityListener> pcapHandler;
/**
* Tries to find a suitable bind-method for two concrete implementations of
* {@link at.jku.fim.rubanetra.protocol.BaseProtocol} using reflection tools.
* Both classes must share a common naming convention for bind methods, usually 'bind'.
*
* @param bindFrom bind from one concrete protocol decoder ...
* @param bindTo ... to another protocol decoder
* @param bindingMethodName the name that indicates the binding method
* @return a Method to call for binding the two base protocol decoders together
* @throws NoSuchMethodException if no such method was discovered or defined
*/
public static Method findProtocolBindingMethod(BaseProtocol bindFrom, BaseProtocol bindTo,
String bindingMethodName) throws NoSuchMethodException {
Class<?> visitable = bindTo.getClass();
while (BaseProtocol.class.isAssignableFrom(visitable)) {
Class<?> visitor = bindFrom.getClass(); // getClass();
if (visitor.isInterface()) {
// a binding implementation has to be found, but an interface
// (and all super-interfaces) can not provide an implementation,
// therefore no appropriate binding method was found
throw new NoSuchMethodException(String.format(
"Unable to find a suitable binding method (from %s to %s)",
bindFrom.getClass(), bindTo.getClass()));
}
while (BaseProtocol.class.isAssignableFrom(visitor)) {
try {
return visitor.getDeclaredMethod(bindingMethodName, visitable);
} catch (NoSuchMethodException e) {
visitor = visitor.getSuperclass();
}
}
visitable = visitable.getSuperclass();
}
throw new NoSuchMethodException(String.format(
"Unable to find a suitable binding method (from %s to %s)", bindFrom.getClass(),
bindTo.getClass()));
}
/**
* Initializes this protocol decoder with the information provided by the user.
* This method will be called once by {@link at.jku.fim.rubanetra.config.model.impl.KrakenProtocolConfigurationBuilderImpl}
* before the bindings are processed.
* This method should be called exactly once, multiple calls will result in an exception being thrown.
*
* @param krakenProtocolConfigurationImpl the protocol configuration provided by the user
*/
public void initialize(KrakenProtocolConfiguration krakenProtocolConfigurationImpl) {
if (settingIsInitialized) {
throw new IllegalStateException(String.format("The Kraken Protocol %s was already initialized - multiple initializations are prohibited.",
getClass().getName()));
}
this.protocolSetting = krakenProtocolConfigurationImpl;
this.pcapHandler = krakenProtocolConfigurationImpl.getPcapHandler();
this.settingIsInitialized = true;
}
/**
* @return the protocol configuration object obtained through initialization
*/
public KrakenProtocolConfiguration getProtocolConfiguration() {
return protocolSetting;
}
/**
* @return the latest dispatched PcapActivity that may or may not be processed by this decoder
*/
public PcapActivity getCurrentPcapActivity() {
return pcapHandler.getCurrentPcapActivity();
}
/**
* @return the utilized Pcap input stream
*/
protected PcapHandler<PcapActivityListener> getPcapHandler() {
return this.pcapHandler;
}
@Override
public void bind(BaseProtocol baseProtocol) {
// this implementation relies on reflection
try {
Method m = findProtocolBindingMethod(this, baseProtocol, BINDING_METHOD_NAME);
m.invoke(this, baseProtocol);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException e) {
log.warn("Unable to perform protocol decoder binding.", e);
}
}
/**
* Adds a listener instance to this object, notifications and property names are subject to the individual
* subclass implementations.
*
* @param l the listener to add
*/
public void addPropertyChangeListener(PropertyChangeListener l) {
this.propertyChangeSupport.addPropertyChangeListener(l);
}
/**
* Adds a listener instance to this object, notifications and property names are subject to the individual
* subclass implementations.
*
* @param propertyName the name of the property to listen for events
* @param l the listener to add
*/
public void addPropertyChangeListener(String propertyName, PropertyChangeListener l) {
this.propertyChangeSupport.addPropertyChangeListener(propertyName, l);
}
/**
* Remove a property change listener instance from this objects, i.e. it will no longer receive any notifications
* from this object.
*
* @param l the listener to remove
*/
public void removePropertyChangeListener(PropertyChangeListener l) {
this.propertyChangeSupport.removePropertyChangeListener(l);
}
/**
* Remove a property change listener instance from this objects, i.e. it will no longer receive any notifications
* from this object.
*
* @param propertyName the name of the property that was used to register this listener in the first place
* @param l the listener to remove
*/
public void removePropertyChangeListener(String propertyName, PropertyChangeListener l) {
this.propertyChangeSupport.removePropertyChangeListener(propertyName, l);
}
}
@@ -0,0 +1,277 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity;
import at.jku.fim.rubanetra.output.CsvRecordOutputWriter;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import org.apache.commons.csv.CSVPrinter;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.time.Instant;
import java.util.*;
/**
* A template for a non-replaceable {@link at.jku.fim.rubanetra.protocol.activity.Activity}.
* It defines an "addFrameNumbers" property that will be used to link two activities regarding the addition
* of frame numbers during the reasoning process.
* Additionally an activity extension method is provided, see {@link #extendActivity(Activity)}.
* If a replaceable activity is required, refer to {@link at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity}.
*/
@JsonIgnoreProperties({"source", "changeSupport", "startTimestamp", "endTimestamp", "excludedFromOutput", "csvSchemaDefinition"})
@JsonPropertyOrder({"activityType", "startInstant", "endInstant", "sourceAddress", "destinationAddress", "description", "compoundFrameNumbers", "optionalFields"})
public abstract class AbstractActivity extends EventObject implements Activity, CsvRecordOutputWriter {
public static final String PROPERTY_ADD_FRAME_NUMBERS = "addFrameNumbers";
public static final String PROPERTY_NEW_START_INSTANT = "newStartInstant";
public static final String PROPERTY_NEW_END_INSTANT = "newEndInstant";
private final PropertyChangeListener frameNumberPropertyListener = evt -> {
if (evt.getPropertyName().equals(PROPERTY_ADD_FRAME_NUMBERS) && evt.getNewValue() instanceof Long[]) {
addFrameNumbers((Long[]) evt.getNewValue());
}
};
private final PropertyChangeListener startInstantListener = evt -> {
if (evt.getPropertyName().equals(PROPERTY_NEW_START_INSTANT) && evt.getNewValue() instanceof Instant) {
Instant newStartInstant = (Instant) evt.getNewValue();
if (getStartInstant() == null || newStartInstant.isBefore(getStartInstant())) {
setStartInstant(newStartInstant);
}
}
};
private final PropertyChangeListener endInstantListener = evt -> {
if (evt.getPropertyName().equals(PROPERTY_NEW_END_INSTANT) && evt.getNewValue() instanceof Instant) {
Instant newEndInstant = (Instant) evt.getNewValue();
if (getEndInstant() == null || newEndInstant.isAfter(getEndInstant())) {
setEndInstant(newEndInstant);
}
}
};
private static final long serialVersionUID = 3477186587937656979L;
private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
private final Collection<Long> compoundFrameNumbers = new TreeSet<>();
private final Collection<OptionalField> optionalFields = new HashSet<>();
private String sourceAddress, destinationAddress;
private Instant startInstant, endInstant;
private String description;
private boolean excludedFromOutput;
/**
* Constructs a prototypical Activity event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public AbstractActivity(Object source) {
super(source);
}
@Override
public String getActivityType() {
return getClass().getSimpleName();
}
@Override
public Date getStartTimestamp() {
return Date.from(startInstant);
}
@Override
public Instant getStartInstant() {
return startInstant;
}
public void setStartInstant(Instant startInstant) {
Instant oldInstant = this.startInstant;
this.startInstant = startInstant;
this.changeSupport.firePropertyChange(PROPERTY_NEW_START_INSTANT, oldInstant, this.startInstant);
}
@Override
public Date getEndTimestamp() {
return Date.from(endInstant);
}
@Override
public Instant getEndInstant() {
return endInstant;
}
public void setEndInstant(Instant endInstant) {
Instant oldInstant = this.endInstant;
this.endInstant = endInstant;
this.changeSupport.firePropertyChange(PROPERTY_NEW_END_INSTANT, oldInstant, this.endInstant);
}
@Override
public String getSourceAddressAsString() {
return sourceAddress;
}
@Override
public String getDestinationAddressAsString() {
return destinationAddress;
}
@Override
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
/**
* Extends this activity by act. The following actions are performed:
* <ul>
* <li>all frame numbers from act are copied into this set of frame numbers</li>
* <li>if act is chronologically before this activity, the start Instant of this activity is overwritten</li>
* <li>if act is chronologically after this activity, the end Instant of this activity is overwritten</li>
* <li>if act is chronologically during this activity, no instants are modified</li>
* <li>an ADD_FRAME_NUMBER property listener is registered in act, in order to receive all future updates to the
* frame number set</li>
* <li>a NEW_START_INSTANT property listener is registered in act, in order to receive all future updates to the
* start instant</li>
* <li>a NEW_END_INSTANT property listener is registered in act, in order to receive all future updates to the
* end instant</li>
* </ul>
* Unintended consequences may be encountered in the following scenario:
* {@code act1.extendActivity(act2); act2.extendActivity(act1);}
*
* @param act the activity to be merged with this activity
*/
public void extendActivity(Activity act) {
Collection<Long> frameNumbersToAdd = act.getCompoundFrameNumbers();
addFrameNumbers(frameNumbersToAdd.toArray(new Long[frameNumbersToAdd.size()]));
if (act.getStartInstant() != null && (getStartInstant() == null || act.getStartInstant().isBefore(getStartInstant()))) {
setStartInstant(act.getStartInstant());
}
if (act.getEndInstant() != null && (getEndInstant() == null || act.getEndInstant().isAfter(getEndInstant()))) {
setEndInstant(act.getEndInstant());
}
act.addPropertyChangeListener(PROPERTY_ADD_FRAME_NUMBERS, frameNumberPropertyListener);
act.addPropertyChangeListener(PROPERTY_NEW_START_INSTANT, startInstantListener);
act.addPropertyChangeListener(PROPERTY_NEW_END_INSTANT, endInstantListener);
}
private void addFrameNumbers(Long... frameNumbers) {
Collection<Long> oldFrames = new TreeSet<>(getCompoundFrameNumbers());
for (long frameNumber : frameNumbers)
this.compoundFrameNumbers.add(frameNumber);
if (oldFrames.size() != getCompoundFrameNumbers().size()) {
this.changeSupport.firePropertyChange(PROPERTY_ADD_FRAME_NUMBERS, oldFrames, frameNumbers);
}
}
/**
* Convenience method to add an optional field to the collection of all optional fields.
*
* @param field the optional field to add, may not be null
*/
protected void addOptionalField(OptionalField field) {
if (field == null) {
throw new IllegalArgumentException();
}
this.optionalFields.add(field);
}
@Override
public Collection<Long> getCompoundFrameNumbers() {
return Collections.unmodifiableCollection(compoundFrameNumbers);
}
@Override
public Collection<OptionalField> getOptionalFields() {
return Collections.unmodifiableCollection(optionalFields);
}
public void setSourceAddress(String sourceAddress) {
this.sourceAddress = sourceAddress;
}
public void setDestinationAddress(String destinationAddress) {
this.destinationAddress = destinationAddress;
}
public void removeReplacedActivity(ReplaceableActivity act) {
this.compoundFrameNumbers.removeAll(act.getCompoundFrameNumbers());
}
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
this.changeSupport.addPropertyChangeListener(l);
}
@Override
public void addPropertyChangeListener(String propertyName, PropertyChangeListener l) {
this.changeSupport.addPropertyChangeListener(propertyName, l);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
this.changeSupport.removePropertyChangeListener(l);
}
@Override
public void removePropertyChangeListener(String propertyName, PropertyChangeListener l) {
this.changeSupport.removePropertyChangeListener(propertyName, l);
}
@Override
public boolean isExcludedFromOutput() {
return excludedFromOutput;
}
@Override
public void setExcludedFromOutput(boolean isExcludedFromOutput) {
this.excludedFromOutput = isExcludedFromOutput;
}
/**
* Writes a single record to the provided csvPrinter. This record currently contains the following fields:
* <pre>
* activityType, startInstant, endInstant, sourceAddress, destinationAddress, description, frameNumbers, optionalFields
* </pre>
*
* @param csvPrinter An initialized csvPrinter, shared among all activities
* @throws IOException If the csvPrinter reaches an invalid state
*/
@Override
public void writeCsvRecord(CSVPrinter csvPrinter) throws IOException {
csvPrinter.printRecord(getActivityType(), getStartInstant().toString(), getEndInstant().toString(),
getSourceAddressAsString(), getDestinationAddressAsString(), getDescription(),
getCompoundFrameNumbers(), getOptionalFields());
}
@Override
public String toString() {
return "AbstractActivity{" +
"compoundFrameNumbers=" + compoundFrameNumbers +
", optionalFields=" + optionalFields +
", sourceAddress='" + sourceAddress + '\'' +
", destinationAddress='" + destinationAddress + '\'' +
", startInstant=" + startInstant +
", endInstant=" + endInstant +
", description='" + description + '\'' +
", excludedFromOutput=" + excludedFromOutput +
'}';
}
}
@@ -0,0 +1,54 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity;
/**
* Extension of {@link at.jku.fim.rubanetra.protocol.activity.AbstractActivity}, added abstraction of
* a {@link at.jku.fim.rubanetra.protocol.activity.ReplaceableActivity}, i.e. implementations may
* replace another activity in addition to a mere extension.
*/
public abstract class AbstractReplaceableActivity extends AbstractActivity implements ReplaceableActivity {
private static final long serialVersionUID = 3930231956209393878L;
private boolean replaced;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public AbstractReplaceableActivity(Object source) {
super(source);
}
@Override
public boolean isReplaced() {
return replaced;
}
@Override
public void setReplaced(boolean isReplaced) {
replaced = isReplaced;
}
public void replaceActivity(ReplaceableActivity activity) {
activity.setReplaced(true);
extendActivity(activity);
}
}
@@ -0,0 +1,204 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
import java.time.Instant;
import java.util.Collection;
import java.util.Date;
/**
* This interface constitutes the basis of all concrete activity implementations, i.e. it defines those fields that are
* common across all activities and are by default available in the final output unless
* {@link #setExcludedFromOutput(boolean)} has been called on an object to object basis.
* However, implementations of this interface cannot be replaced by other activities by simple means, therefore the use
* of {@link at.jku.fim.rubanetra.protocol.activity.ReplaceableActivity} is recommended instead.
* Furthermore, the values returned by the getter methods are the only values that will be included in the CVS output
* while the Json parser will include all fields of all subclasses.
* Generally it is advisable to establish a meaningful implementation of all defined methods, nevertheless a template
* is provided by {@link at.jku.fim.rubanetra.protocol.activity.AbstractActivity}.
*/
public interface Activity extends Serializable {
/**
* Defines the type of the activity - this value should be unique in the sense that it should be possible to
* identify the class of the object by using this String exclusively among all activities.
* This value will be used by a python json parser to setup the equivalent of the Java class/activity structure.
*
* @return a unique identifier of the implemented activity
*/
String getActivityType();
/**
* Returns a fixed point in time that fixates the start of this activity's time-line, i.e. it returns a point
* in time that fulfills any one of the following conditions:
* <ul>
* <li>if the activity is defined as an event that occurs at exactly one time without a duration/interval
* then the returned value corresponds to exactly that time and
* {@link #getEndInstant()} must return the same reference</li>
* <li>if the activity spans over a period of time or includes multiple intervals,
* then the returned value corresponds to the point in time that lies furthest in the past and starts
* the first interval of the activity</li>
* </ul>
* Calling this method multiple times may return different instants, due to the reasoning process that may find
* a prior starting point in time. Therefore the returned object should generally not be used as an identifier
* for the activity.
*
* @return the point in time that lies furthest in the past and indicates the start of the activity
*/
Instant getStartInstant();
/**
* Returns a fixed point in time that fixates the end of this activity's time-line, i.e. it returns a point
* in time that fulfills any one of the following conditions:
* <ul>
* <li>if the activity is defined as an event that occurs at exactly one time without a duration/interval
* then the returned value corresponds to exactly that time and
* {@link #getStartInstant()} must return the same reference</li>
* <li>if the activity spans over a period of time or includes multiple intervals,
* then the returned value corresponds to the point in time that lies closest to the current time and ends
* the last interval of the activity</li>
* </ul>
* Calling this method multiple times may return different instants, due to the reasoning process that may find
* a later starting point in time. Therefore the returned object should generally not be used as an identifier
* for the activity.
*
* @return the point in time that lies closest to the current time indicates the end of the activity
*/
Instant getEndInstant();
/**
* An address that can be interpreted as the source address for this activity.
* Usually this address should correspond to a human readable value.
* Because different protocols may be defining different address formats and types, the returned value
* is highly dependant on the actual implementation and may be empty or null if no source address exists at all.
*
* @return a human-readable protocol-dependant source address or null if no such address exists for this activity
*/
String getSourceAddressAsString();
/**
* An address that can be interpreted as the destination address for this activity.
* Usually this address should correspond to a human readable value.
* Because different protocols may be defining different address formats and types, the returned value
* is highly dependant on the actual implementation and may be empty or null if no destination address exists at all.
*
* @return a human-readable protocol-dependant destination address or null if no such address exists for this activity
*/
String getDestinationAddressAsString();
/**
* @return an arbitrary String that describes this activity, may be null or empty
*/
String getDescription();
/**
* This method's return value indicates whether or not this activity will be included in the final output.
* If this method returns false, the activity will be included in the final output.
*
* @return true, iff this activity should be excluded from the final output
*/
boolean isExcludedFromOutput();
/**
* Setter for {@link #isExcludedFromOutput()}.
*
* @param excludedFromOutput whether or not to exclude this activity from the final output
*/
void setExcludedFromOutput(boolean excludedFromOutput);
/**
* This method returns a sorted collection of all frame numbers in consecutive order and greater than zero, that
* define this activity. It serves as basis for a manual review of the packets that were used during the reasoning
* process, i.e. a future use case could consist of using these numbers to extract all frames corresponding to
* a single activity individually from a potentially large PCAP file.
*
* @return a sorted collection of all frame numbers in consecutive order and greater than zero, that
* define this activity
*/
Collection<Long> getCompoundFrameNumbers();
/**
* This method returns a collection of attribute/value pairs that have to be included in the final output of all
* output processors including the CVS encoder and the JSON encoder. This serves as an opportunity to include
* information that would usually not be available due to the fixed nature of CVS columns specification.
*
* @return a collection of attribute/value pairs that have to be included in the final output of all
* output processors including the CVS encoder and the JSON encoder.
*/
Collection<OptionalField> getOptionalFields();
/**
* Adds a property change listener for all properties of this activity.
*
* @param l the listener to add
*/
void addPropertyChangeListener(PropertyChangeListener l);
/**
* Adds a property change listener for one exactly defined property of this activity.
*
* @param propertyName the name of property to receive notifications for
* @param l the listener to add
*/
void addPropertyChangeListener(String propertyName, PropertyChangeListener l);
/**
* Removes the specified listener.
*
* @param l the listener to remove
*/
void removePropertyChangeListener(PropertyChangeListener l);
/**
* The specified listener will no longer receive notifications for propertyName.
*
* @param propertyName the name of property to stop receiving notifications for
* @param l the listener to remove
*/
void removePropertyChangeListener(String propertyName, PropertyChangeListener l);
/**
* This method should only be used within Drools rule-files and serve as timestamp indicator. This is currently
* required due to the general incompatibility of {@link java.time.Instant} caused by the Drools engine pseudo-clock implementation.
*
* @return a timestamp in MILLISECOND precision (backed by an {@link java.time.Instant}), representing
* the starting point in time for this {@link at.jku.fim.rubanetra.protocol.activity.Activity}.
* Please note that this method returns an object which may not be persistent, i.e. the start timestamp
* object may be overwritten by implementations.
* @see #getStartInstant()
* @see at.jku.fim.rubanetra.protocol.activity.AbstractActivity#setStartInstant(java.time.Instant)
* @see at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity#setStartInstant(java.time.Instant)
*/
Date getStartTimestamp();
/**
* This method should only be used within Drools rule-files and serve as timestamp indicator. This is currently
* required due to the general incompatibility of {@link java.time.Instant} caused by the Drools engine pseudo-clock implementation.
*
* @return a timestamp in MILLISECOND precision (backed by an {@link java.time.Instant}), representing
* the ending point in time for this {@link at.jku.fim.rubanetra.protocol.activity.Activity}.
* Please note that this method returns an object which may not be persistent, i.e. the end timestamp
* object may be overwritten by implementations.
* @see #getEndInstant()
* @see at.jku.fim.rubanetra.protocol.activity.AbstractActivity#setEndInstant(java.time.Instant)
* @see at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity#setEndInstant(java.time.Instant)
*/
Date getEndTimestamp();
}
@@ -0,0 +1,48 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity;
import java.util.UUID;
/**
* This class serves as the base class for custom class extension inside the Drools rules files, i.e. any declared
* classes using the Drools rule language may extend this class in order to qualify as valid Activity which
* is ultimately supposed to be written to the final OutputStream.
* As soon as it is possible to extend an abstract class (e.g. the AbstractActivity or the AbstractReplaceableActivity)
* inside the Drools rule definition this class can be considered obsolete. However, currently it appears that
* the base class requires a default constructor. This class defines a default constructor, however, the source
* object is currently substituted by a random UUID.
*/
public class DroolsBaseActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = -5807104716376365130L;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public DroolsBaseActivity(Object source) {
super(source);
}
public DroolsBaseActivity() {
super(UUID.randomUUID());
}
}
@@ -0,0 +1,64 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity;
import java.io.Serializable;
/**
* An optional field consists of an identifier and an arbitrary value and may be added to the final OutputStream.
*/
public class OptionalField implements Serializable {
private static final long serialVersionUID = -6391001123309282371L;
private String identifier;
private Object value;
/**
* Default constructor, all values are initialized as null pointers.
*/
public OptionalField() {
this.identifier = null;
this.value = null;
}
/**
* Construct a new optional field
* @param identifier the unique identifier among all optional field to be included for a single activity-
* @param value an arbitrary object
*/
public OptionalField(String identifier, Object value) {
this.identifier = identifier;
this.value = value;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
}
@@ -0,0 +1,46 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity;
import java.io.Serializable;
import java.util.EventObject;
/**
* This event may be fired by any custom rule definition and it directs the output handler to write an Activity to
* the output stream.
*/
public class OutputActivityEvent extends EventObject implements Serializable {
private static final long serialVersionUID = -4878319387952597370L;
private final Activity toOutput;
/**
* Constructs a prototypical Event.
*
* @param toOutput The activity that should be written to the output stream.
* @throws IllegalArgumentException if source is null.
*/
public OutputActivityEvent(Activity toOutput) {
super(toOutput);
this.toOutput = toOutput;
}
public Activity getToOutput() {
return toOutput;
}
}
@@ -0,0 +1,37 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity;
/**
* Extension of the {@link at.jku.fim.rubanetra.protocol.activity.Activity} interface, it provides
* methods to replace, in addition to extend, an activity.
*/
public interface ReplaceableActivity extends Activity {
/**
* @return true, iff this activity has been replaced by at least one other activity.
*/
public boolean isReplaced();
/**
* Indicate, that this activity has been replaced.
*
* @param isReplaced whether or not this activity will be replaced by the caller.
*/
public void setReplaced(boolean isReplaced);
}
@@ -0,0 +1,181 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.arp;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.protocol.network.Arp;
import org.kie.api.definition.type.Role;
import org.kie.api.definition.type.Role.Type;
import org.krakenapps.pcap.decoder.arp.ArpPacket;
import org.krakenapps.pcap.decoder.ethernet.MacAddress;
import java.net.InetAddress;
/**
* An encapsulation of an {@link org.krakenapps.pcap.decoder.arp.ArpPacket} including IP information if available.
* This class also provides the JNetPcap specific Arp packet representation (if available)
* {@link org.jnetpcap.protocol.network.Arp}.
*/
@Role(Type.EVENT)
public class ArpActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = -6124545150681044570L;
private final PcapActivity pcapActivity;
private final int hardwareType;
private final int protocolType;
private final int hardwareAddressLength;
private final int protocolAddressLength;
private final int operation;
private final MacAddress senderHardwareAddress;
private final InetAddress senderProtocolAddress;
private final MacAddress targetHardwareAddress;
private final InetAddress targetProtocolAddress;
private final Arp arp;
/**
* Default constructor for this class, the source pcap activity will be replaced.
*
* @param source on which the ArpActivity occurred or the final PcapActivity that led to the fully decoded packet.
* @param p the parsed Arp-packet
*/
public ArpActivity(PcapActivity source, ArpPacket p) {
super(source);
this.pcapActivity = source;
hardwareType = p.getHardwareType();
protocolType = p.getProtocolType();
hardwareAddressLength = p.getHardwareSize();
protocolAddressLength = p.getProtocolSize();
operation = p.getOpcode();
senderHardwareAddress = p.getSenderMac();
senderProtocolAddress = p.getSenderIp();
targetHardwareAddress = p.getTargetMac();
targetProtocolAddress = p.getTargetIp();
arp = new Arp();
PcapPacket packet = pcapActivity.getPcapPacket();
packet.hasHeader(getArp());
replaceActivity(source);
}
/**
* @return the source on which the ArpActivity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
/**
* @see org.krakenapps.pcap.decoder.arp.ArpPacket#getHardwareType()
*/
public int getHardwareType() {
return hardwareType;
}
/**
* @see org.krakenapps.pcap.decoder.arp.ArpPacket#getProtocolType()
*/
public int getProtocolType() {
return protocolType;
}
/**
* @see org.krakenapps.pcap.decoder.arp.ArpPacket#getHardwareSize()
*/
public int getHardwareAddressLength() {
return hardwareAddressLength;
}
/**
* @see org.krakenapps.pcap.decoder.arp.ArpPacket#getProtocolSize()
*/
public int getProtocolAddressLength() {
return protocolAddressLength;
}
/**
* @see org.krakenapps.pcap.decoder.arp.ArpPacket#getOpcode()
*/
public int getOperation() {
return operation;
}
/**
* @see org.krakenapps.pcap.decoder.arp.ArpPacket#getSenderMac()
*/
public MacAddress getSenderHardwareAddress() {
return senderHardwareAddress;
}
/**
* @see org.krakenapps.pcap.decoder.arp.ArpPacket#getSenderIp()
*/
public InetAddress getSenderProtocolAddress() {
return senderProtocolAddress;
}
/**
* @see org.krakenapps.pcap.decoder.arp.ArpPacket#getTargetMac()
*/
public MacAddress getTargetHardwareAddress() {
return targetHardwareAddress;
}
/**
* @see org.krakenapps.pcap.decoder.arp.ArpPacket#getTargetIp()
*/
public InetAddress getTargetProtocolAddress() {
return targetProtocolAddress;
}
/**
* @return the JNetPcap specific Arp packet representation
*/
public Arp getArp() {
return arp;
}
@Override
public String getSourceAddressAsString() {
return String.valueOf(getSenderHardwareAddress());
}
@Override
public String getDestinationAddressAsString() {
return String.valueOf(getTargetHardwareAddress());
}
@Override
public String toString() {
return "ArpActivity{" +
"pcapActivity=" + pcapActivity +
", hardwareType=" + hardwareType +
", protocolType=" + protocolType +
", hardwareAddressLength=" + hardwareAddressLength +
", protocolAddressLength=" + protocolAddressLength +
", operation=" + operation +
", senderHardwareAddress=" + senderHardwareAddress +
", senderProtocolAddress=" + senderProtocolAddress +
", targetHardwareAddress=" + targetHardwareAddress +
", targetProtocolAddress=" + targetProtocolAddress +
", arp=" + arp +
'}';
}
}
@@ -0,0 +1,89 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.dhcp;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.kie.api.definition.type.Role;
import org.krakenapps.pcap.decoder.dhcp.DhcpMessage;
/**
* An encapsulation of a {@link org.krakenapps.pcap.decoder.dhcp.DhcpMessage}.
*/
@Role(Role.Type.EVENT)
public class DhcpActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = -3031909630697934242L;
private final PcapActivity pcapActivity;
private final DhcpMessage dhcpMessage;
/**
* Constructs an activity containing the passed Dhcp message, the source pcap activity will be replaced.
*
* @param source The object on which the Activity occurred.
* @param dhcpMessage the decoded Dhcp message
* @throws IllegalArgumentException if source is null.
*/
public DhcpActivity(PcapActivity source, DhcpMessage dhcpMessage) {
super(source);
pcapActivity = source;
this.dhcpMessage = dhcpMessage;
replaceActivity(source);
}
/**
* @return the source on which the ArpActivity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
/**
* @return the decoded Dhcp content
*/
public DhcpMessage getDhcpMessage() {
return dhcpMessage;
}
@Override
public String getSourceAddressAsString() {
if (dhcpMessage != null) {
return String.format("%s - %s", String.valueOf(dhcpMessage.getClientAddress()), String.valueOf(dhcpMessage.getClientMac()));
} else {
return null;
}
}
@Override
public String getDestinationAddressAsString() {
if (dhcpMessage != null) {
return String.valueOf(dhcpMessage.getNextServerAddress());
} else {
return null;
}
}
@Override
public String toString() {
return "DhcpActivity{" +
"pcapActivity=" + pcapActivity +
", dhcpMessage=" + dhcpMessage +
'}';
}
}
@@ -0,0 +1,143 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.dns;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.kie.api.definition.type.Role;
import org.kie.api.definition.type.Role.Type;
import org.xbill.DNS.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* An encapsulation of a decoded {@link org.xbill.DNS.Message}.
* The default output stream handler {@link at.jku.fim.rubanetra.output.impl.JsonOutputWriterStrategy}
* will not include the original {@link org.xbill.DNS.Message} object due to space conservation issues. Instead, the
* available question, answer, authority and additional {@link org.xbill.DNS.Record}s are included as lists and the
* {@link org.xbill.DNS.Header} as it was encountered.
*/
@Role(Type.EVENT)
public class DnsActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = -476178927847593388L;
private final Message dnsMessage;
private final List<Record> questionRecords;
private final List<Record> answerRecords;
private final List<Record> authorityRecords;
private final List<Record> additionalRecords;
private final Header dnsMessageHeader;
private final boolean isResponse;
private final PcapActivity pcapActivity;
/**
* Constructs a new DnsActivity, while replacing the source pcap activity and wrapping the decoded DNS message
*
* @param source on which the DnsActivity occurred or the final PcapActivity that led to the fully decoded packet.
* @param dnsMessage the fully decoded DNS content
*/
public DnsActivity(PcapActivity source, Message dnsMessage) {
super(source);
this.dnsMessage = dnsMessage;
questionRecords = Arrays.asList(dnsMessage.getSectionArray(Section.QUESTION));
answerRecords = Arrays.asList(dnsMessage.getSectionArray(Section.ANSWER));
authorityRecords = Arrays.asList(dnsMessage.getSectionArray(Section.AUTHORITY));
additionalRecords = Arrays.asList(dnsMessage.getSectionArray(Section.ADDITIONAL));
dnsMessageHeader = dnsMessage.getHeader();
isResponse = dnsMessage.getHeader().getFlag(Flags.QR);
this.pcapActivity = source;
replaceActivity(source);
}
/**
* @return the decoded DNS content as is
*/
@JsonIgnore
public Message getDnsMessage() {
return dnsMessage;
}
/**
* @return an unmodifiable list of the available question records
*/
public List<Record> getQuestionRecords() {
return Collections.unmodifiableList(questionRecords);
}
/**
* @return an unmodifiable list of the available answer records
*/
public List<Record> getAnswerRecords() {
return Collections.unmodifiableList(answerRecords);
}
/**
* @return an unmodifiable list of the available authority records
*/
public List<Record> getAuthorityRecords() {
return Collections.unmodifiableList(authorityRecords);
}
/**
* @return an unmodifiable list of the available additional records
*/
public List<Record> getAdditionalRecords() {
return Collections.unmodifiableList(additionalRecords);
}
/**
* @return the DNS message header
*/
public Header getDnsMessageHeader() {
return dnsMessageHeader;
}
/**
* @return flag, indicating whether or not this message is a response to a query
*/
public boolean isResponse() {
return isResponse;
}
/**
* @return the source on which the DnsActivity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
@Override
public String toString() {
return "DnsActivity{" +
"dnsMessage=" + dnsMessage +
", questionRecords=" + questionRecords +
", answerRecords=" + answerRecords +
", authorityRecords=" + authorityRecords +
", additionalRecords=" + additionalRecords +
", dnsMessageHeader=" + dnsMessageHeader +
", isResponse=" + isResponse +
", pcapActivity=" + pcapActivity +
'}';
}
}
@@ -0,0 +1,140 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.ethernet;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.protocol.lan.Ethernet;
import org.jnetpcap.protocol.lan.Ethernet.EthernetType;
import org.kie.api.definition.type.Role;
import org.kie.api.definition.type.Role.Type;
import org.krakenapps.pcap.decoder.ethernet.EthernetFrame;
import org.krakenapps.pcap.decoder.ethernet.MacAddress;
/**
* This class encapsulates an {@link org.jnetpcap.protocol.lan.Ethernet} object, which should provide a vast amount
* of metadata including the content buffer itself. If this activity is created before it has been processed by
* subsequent protocol parsers, the content buffer should not be modified in any way, i.e. it should be treated as
* read-only medium.
* ENHANCEMENT: encapsulate all metadata fields and prohibit access to the content buffer itself
*/
@Role(Type.EVENT)
public class EthernetActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = -8495525878251669060L;
private final PcapActivity pcapActivity;
private final MacAddress destinationMacAddress, sourceMacAddress;
private final int ethernetType;
private final EthernetType ethernetTypeEnum;
private final Ethernet ethernet;
/**
* Constructs a new ethernet activity, replaces the source pcap activity on which the event occurred.
*
* @param source the Pcap activity on which the event occurred. This activity will be excluded from the
* output, unless {@link #setExcludedFromOutput(boolean)} is called with 'false'.
* @param frame the decoded Ethernet frame (by Kraken)
*/
public EthernetActivity(PcapActivity source, EthernetFrame frame) {
super(source);
this.pcapActivity = source;
this.destinationMacAddress = frame.getDestination();
this.sourceMacAddress = frame.getSource();
this.ethernetType = frame.getType();
this.ethernetTypeEnum = EthernetType.valueOf(getEthernetType());
this.ethernet = new Ethernet();
scanAndFillEthernet();
replaceActivity(source);
setExcludedFromOutput(true);
}
private void scanAndFillEthernet() {
PcapPacket pcapPacket = this.pcapActivity.getPcapPacket();
pcapPacket.hasHeader(ethernet);
}
/**
* @return the Ethernet frame as decoded by JNetPcap
*/
public Ethernet getEthernet() {
return this.ethernet;
}
/**
* @return the physical Ethernet address (source)
*/
public MacAddress getSourceMacAddress() {
return this.sourceMacAddress;
}
/**
* @return the physical Ethernet address (destination)
*/
public MacAddress getDestinationMacAddress() {
return this.destinationMacAddress;
}
/**
* @return The payload type as integer
* @see #getEthernetTypeEnum()
*/
public int getEthernetType() {
return this.ethernetType;
}
/**
* @return the payload type as JNetPcap enumeration object, however, be aware that this enumeration may not be
* complete
* @see #getEthernetType()
*/
public EthernetType getEthernetTypeEnum() {
return ethernetTypeEnum;
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded frame.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
@Override
public String getSourceAddressAsString() {
return String.valueOf(getSourceMacAddress());
}
@Override
public String getDestinationAddressAsString() {
return String.valueOf(getDestinationMacAddress());
}
@Override
public String toString() {
return "EthernetActivity{" +
"pcapActivity=" + pcapActivity +
", destinationMacAddress=" + destinationMacAddress +
", sourceMacAddress=" + sourceMacAddress +
", ethernetType=" + ethernetType +
", ethernetTypeEnum=" + ethernetTypeEnum +
", ethernet=" + ethernet +
'}';
}
}
@@ -0,0 +1,133 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.ftp;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import java.util.Arrays;
/**
* TODO: this class represent a partial ftp activity, it must be reassembled by Drools rules based on the tcp socket addresses to represent a FTPSession
*/
public class FtpActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = 8828875279114236211L;
private final PcapActivity pcapActivity;
private final Type ftpActivityType;
private String command, reply;
private byte[] list;
/**
* Constructs a prototypical activity, replaces the source Pcap activity and stores the type of the FTP message..
*
* @param source The object on which the activity initially occurred.
* @param type the FTP message type leading to a decoded String, i.e. either COMMAND or REPLY
* @throws IllegalArgumentException if source is null or an unsupported type was encountered.
*/
public FtpActivity(PcapActivity source, Type type, String decodedMessage) {
super(source);
pcapActivity = source;
ftpActivityType = type;
switch (ftpActivityType) {
case COMMAND:
this.command = decodedMessage;
break;
case REPLY:
this.reply = decodedMessage;
break;
default:
throw new IllegalArgumentException(String.format("Unsupported FTP message type: %s", type));
}
replaceActivity(source);
}
/**
* Constructs a prototypical activity, replaces the source Pcap activity and stores the type of the FTP message..
*
* @param source The object on which the activity initially occurred.
* @param type the FTP message type leading to a decoded byte [], i.e. VIEW_LIST.
* @throws IllegalArgumentException if source is null or an unsupported type was encountered.
*/
public FtpActivity(PcapActivity source, Type type, byte [] decodedMessage) {
super(source);
pcapActivity = source;
ftpActivityType = type;
switch (ftpActivityType) {
case VIEW_LIST:
this.list = decodedMessage;
break;
default:
throw new IllegalArgumentException(String.format("Unsupported FTP message type: %s", type));
}
replaceActivity(source);
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded frame.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
/**
* @return the FTP message type as enumeration
*/
public Type getFtpActivityTypeEnum() {
return ftpActivityType;
}
/**
* @return the command string, if the type of this activity is equal to COMMAND or null otherwise.
*/
public String getCommand() {
return command;
}
/**
* @return the decoded reply string, if the type of this activity is equal to REPLY or null otherwise
*/
public String getReply() {
return reply;
}
/**
* @return the result of the VIEW_LIST command or null
*/
public byte[] getList() {
return list;
}
public enum Type {
COMMAND, REPLY, VIEW_LIST
}
@Override
public String toString() {
return "FtpActivity{" +
"pcapActivity=" + pcapActivity +
", ftpActivityType=" + ftpActivityType +
", command='" + command + '\'' +
", reply='" + reply + '\'' +
", list=" + Arrays.toString(list) +
'}';
}
}
@@ -0,0 +1,149 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.http;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import at.jku.fim.rubanetra.protocol.activity.dns.DnsActivity;
import org.kie.api.definition.type.Role;
import org.kie.api.definition.type.Role.Type;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* A high level aggregation of a {@link at.jku.fim.rubanetra.protocol.activity.http.HttpRequestActivity}
* and a {@link at.jku.fim.rubanetra.protocol.activity.http.HttpResponseActivity}.
* Usually instances of this class are created directly by Drools rules, i.e. a request is matched to a response
* by one or several rules.
* Additionally, this class defines several optional fields, e.g. a potential preceding Dns-Match to this Http-Activity
* and multiple {@link at.jku.fim.rubanetra.protocol.activity.http.HttpImageActivity} objects may be
* aggregated as well.
*/
public class HttpActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = 2723756798789149547L;
private final HttpRequestActivity request;
private final HttpResponseActivity response;
private final Set<HttpImageActivity> imageActivities;
private DnsActivity dnsMatch;
/**
* Constructs a new Http-Activity and replaces both, the passed request and the response.
*
* @param req the request that is followed by ...
* @param resp ... the response for the request
*/
public HttpActivity(HttpRequestActivity req, HttpResponseActivity resp) {
super(req);
this.request = req;
this.response = resp;
this.dnsMatch = null;
this.imageActivities = new HashSet<>();
replaceActivity(req);
replaceActivity(resp);
}
/**
* @return the Http response for the {@link #getRequest()}
*/
public HttpResponseActivity getResponse() {
return response;
}
/**
* @return the Http request preceding the {@link #getResponse()}
*/
public HttpRequestActivity getRequest() {
return request;
}
/**
* The interpretation of this field depends on the actual specification of the rule that filled this variable in the
* first place.
* @return a potential DNS-based query leading to the represented Http request/response
*/
public DnsActivity getDnsMatch() {
return dnsMatch;
}
/**
* Calling this method results in the following actions:
* <ul>
* <li>if this method was called previously, the old dnsMatch object is will no longer considered to be a
* replaced activity and the assigned frames are removed from the frame set</li>
* <li>the dnsMatch reference is saved and the DNS activity will be replaced</li>
* </ul>
* @param dnsMatch a DNS activity that potentially led to this HTTP activity (speculative)
*/
public void setDnsMatch(DnsActivity dnsMatch) {
if (this.dnsMatch != null) {
super.removeReplacedActivity(this.dnsMatch);
}
this.dnsMatch = dnsMatch;
replaceActivity(dnsMatch);
}
/**
* The interpretation of this field depends on the actual definition of the Drools rules that will fill this set.
* @return a collection of activities that potentially originated from this HTTP activity.
*/
public Set<HttpImageActivity> getImageActivities() {
return Collections.unmodifiableSet(imageActivities);
}
/**
* Adds and replaces a Http image activity which origins may be considered to lie within this HTTP activity.
* The image activities are currently excluded from output, unless this is overridden manually.
* @param imgAct a image activity that was caused by this HTTP activity
*/
public void addImageActivity(HttpImageActivity imgAct) {
if (this.imageActivities.add(imgAct)) {
replaceActivity(imgAct);
imgAct.setExcludedFromOutput(true);
}
}
@Override
public String getSourceAddressAsString() {
if(getRequest() != null) {
return String.valueOf(getRequest().getClientAddress());
} else {
return null;
}
}
@Override
public String getDestinationAddressAsString() {
if(getRequest() != null) {
return String.valueOf(getRequest().getServerAddress());
} else {
return null;
}
}
@Override
public String toString() {
return "HttpActivity{" +
"request=" + request +
", response=" + response +
", imageActivities=" + imageActivities +
", dnsMatch=" + dnsMatch +
'}';
}
}
@@ -0,0 +1,339 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.http;
import org.apache.http.HttpHeaders;
/**
* The following header definitions were taken from the {@link org.apache.http.HttpHeaders} (released
* under the Apache 2.0 license) class and wrapped into an enumeration to
* provide a simple canonicalization method which constitutes a workaround
* for the missing normalization of Kraken HTTP header fields (see
* {@link org.krakenapps.pcap.decoder.http.HttpHeaders}).
* This enum is also used directly inside Drools rule definitions.
*/
public enum HttpHeader {
/**
* RFC 2616 (HTTP/1.1) Section 14.1
*/
ACCEPT(HttpHeaders.ACCEPT),
/**
* RFC 2616 (HTTP/1.1) Section 14.2
*/
ACCEPT_CHARSET(HttpHeaders.ACCEPT_CHARSET),
/**
* RFC 2616 (HTTP/1.1) Section 14.3
*/
ACCEPT_ENCODING(HttpHeaders.ACCEPT_ENCODING),
/**
* RFC 2616 (HTTP/1.1) Section 14.4
*/
ACCEPT_LANGUAGE(HttpHeaders.ACCEPT_LANGUAGE),
/**
* RFC 2616 (HTTP/1.1) Section 14.5
*/
ACCEPT_RANGES(HttpHeaders.ACCEPT_RANGES),
/**
* RFC 2616 (HTTP/1.1) Section 14.6
*/
AGE(HttpHeaders.AGE),
/**
* RFC 1945 (HTTP/1.0) Section 10.1, RFC 2616 (HTTP/1.1) Section 14.7
*/
ALLOW(HttpHeaders.ALLOW),
/**
* RFC 1945 (HTTP/1.0) Section 10.2, RFC 2616 (HTTP/1.1) Section 14.8
*/
AUTHORIZATION(HttpHeaders.AUTHORIZATION),
/**
* RFC 2616 (HTTP/1.1) Section 14.9
*/
CACHE_CONTROL(HttpHeaders.CACHE_CONTROL),
/**
* RFC 2616 (HTTP/1.1) Section 14.10
*/
CONNECTION(HttpHeaders.CONNECTION),
/**
* RFC 1945 (HTTP/1.0) Section 10.3, RFC 2616 (HTTP/1.1) Section 14.11
*/
CONTENT_ENCODING(HttpHeaders.CONTENT_ENCODING),
/**
* RFC 2616 (HTTP/1.1) Section 14.12
*/
CONTENT_LANGUAGE(HttpHeaders.CONTENT_LANGUAGE),
/**
* RFC 1945 (HTTP/1.0) Section 10.4, RFC 2616 (HTTP/1.1) Section 14.13
*/
CONTENT_LENGTH(HttpHeaders.CONTENT_LENGTH),
/**
* RFC 2616 (HTTP/1.1) Section 14.14
*/
CONTENT_LOCATION(HttpHeaders.CONTENT_LOCATION),
/**
* RFC 2616 (HTTP/1.1) Section 14.15
*/
CONTENT_MD5(HttpHeaders.CONTENT_MD5),
/**
* RFC 2616 (HTTP/1.1) Section 14.16
*/
CONTENT_RANGE(HttpHeaders.CONTENT_RANGE),
/**
* RFC 1945 (HTTP/1.0) Section 10.5, RFC 2616 (HTTP/1.1) Section 14.17
*/
CONTENT_TYPE(HttpHeaders.CONTENT_TYPE),
/**
* RFC 1945 (HTTP/1.0) Section 10.6, RFC 2616 (HTTP/1.1) Section 14.18
*/
DATE(HttpHeaders.DATE),
/**
* RFC 2518 (WevDAV) Section 9.1
*/
DAV(HttpHeaders.DAV),
/**
* RFC 2518 (WevDAV) Section 9.2
*/
DEPTH(HttpHeaders.DEPTH),
/**
* RFC 2518 (WevDAV) Section 9.3
*/
DESTINATION(HttpHeaders.DESTINATION),
/**
* RFC 2616 (HTTP/1.1) Section 14.19
*/
ETAG(HttpHeaders.ETAG),
/**
* RFC 2616 (HTTP/1.1) Section 14.20
*/
EXPECT(HttpHeaders.EXPECT),
/**
* RFC 1945 (HTTP/1.0) Section 10.7, RFC 2616 (HTTP/1.1) Section 14.21
*/
EXPIRES(HttpHeaders.EXPIRES),
/**
* RFC 1945 (HTTP/1.0) Section 10.8, RFC 2616 (HTTP/1.1) Section 14.22
*/
FROM(HttpHeaders.FROM),
/**
* RFC 2616 (HTTP/1.1) Section 14.23
*/
HOST(HttpHeaders.HOST),
/**
* RFC 2518 (WevDAV) Section 9.4
*/
IF(HttpHeaders.IF),
/**
* RFC 2616 (HTTP/1.1) Section 14.24
*/
IF_MATCH(HttpHeaders.IF_MATCH),
/**
* RFC 1945 (HTTP/1.0) Section 10.9, RFC 2616 (HTTP/1.1) Section 14.25
*/
IF_MODIFIED_SINCE(HttpHeaders.IF_MODIFIED_SINCE),
/**
* RFC 2616 (HTTP/1.1) Section 14.26
*/
IF_NONE_MATCH(HttpHeaders.IF_NONE_MATCH),
/**
* RFC 2616 (HTTP/1.1) Section 14.27
*/
IF_RANGE(HttpHeaders.IF_RANGE),
/**
* RFC 2616 (HTTP/1.1) Section 14.28
*/
IF_UNMODIFIED_SINCE(HttpHeaders.IF_UNMODIFIED_SINCE),
/**
* RFC 1945 (HTTP/1.0) Section 10.10, RFC 2616 (HTTP/1.1) Section 14.29
*/
LAST_MODIFIED(HttpHeaders.LAST_MODIFIED),
/**
* RFC 1945 (HTTP/1.0) Section 10.11, RFC 2616 (HTTP/1.1) Section 14.30
*/
LOCATION(HttpHeaders.LOCATION),
/**
* RFC 2518 (WevDAV) Section 9.5
*/
LOCK_TOKEN(HttpHeaders.LOCK_TOKEN),
/**
* RFC 2616 (HTTP/1.1) Section 14.31
*/
MAX_FORWARDS(HttpHeaders.MAX_FORWARDS),
/**
* RFC 2518 (WevDAV) Section 9.6
*/
OVERWRITE(HttpHeaders.OVERWRITE),
/**
* RFC 1945 (HTTP/1.0) Section 10.12, RFC 2616 (HTTP/1.1) Section 14.32
*/
PRAGMA(HttpHeaders.PRAGMA),
/**
* RFC 2616 (HTTP/1.1) Section 14.33
*/
PROXY_AUTHENTICATE(HttpHeaders.PROXY_AUTHENTICATE),
/**
* RFC 2616 (HTTP/1.1) Section 14.34
*/
PROXY_AUTHORIZATION(HttpHeaders.PROXY_AUTHORIZATION),
/**
* RFC 2616 (HTTP/1.1) Section 14.35
*/
RANGE(HttpHeaders.RANGE),
/**
* RFC 1945 (HTTP/1.0) Section 10.13, RFC 2616 (HTTP/1.1) Section 14.36
*/
REFERER(HttpHeaders.REFERER),
/**
* RFC 2616 (HTTP/1.1) Section 14.37
*/
RETRY_AFTER(HttpHeaders.RETRY_AFTER),
/**
* RFC 1945 (HTTP/1.0) Section 10.14, RFC 2616 (HTTP/1.1) Section 14.38
*/
SERVER(HttpHeaders.SERVER),
/**
* RFC 2518 (WevDAV) Section 9.7
*/
STATUS_URI(HttpHeaders.STATUS_URI),
/**
* RFC 2616 (HTTP/1.1) Section 14.39
*/
TE(HttpHeaders.TE),
/**
* RFC 2518 (WevDAV) Section 9.8
*/
TIMEOUT(HttpHeaders.TIMEOUT),
/**
* RFC 2616 (HTTP/1.1) Section 14.40
*/
TRAILER(HttpHeaders.TRAILER),
/**
* RFC 2616 (HTTP/1.1) Section 14.41
*/
TRANSFER_ENCODING(HttpHeaders.TRANSFER_ENCODING),
/**
* RFC 2616 (HTTP/1.1) Section 14.42
*/
UPGRADE(HttpHeaders.UPGRADE),
/**
* RFC 1945 (HTTP/1.0) Section 10.15, RFC 2616 (HTTP/1.1) Section 14.43
*/
USER_AGENT(HttpHeaders.USER_AGENT),
/**
* RFC 2616 (HTTP/1.1) Section 14.44
*/
VARY(HttpHeaders.VARY),
/**
* RFC 2616 (HTTP/1.1) Section 14.45
*/
VIA(HttpHeaders.VIA),
/**
* RFC 2616 (HTTP/1.1) Section 14.46
*/
WARNING(HttpHeaders.WARNING), WWW_AUTHENTICATE(HttpHeaders.WWW_AUTHENTICATE);
private final String name;
HttpHeader(String httpHeader) {
this.name = httpHeader;
}
/**
* Canonicalizes a header as encountered by various decoders, i.e. it takes a String, representative for a header,
* and tries to return an enumeration object instead.
* This method should be considered both, inefficient and potentially unreliable, because it does not remove any
* whitespace nor does handle encoding issues.
* A simple equalsIgnoreCase is performed against the header enum and the passed String.
*
* @param wildHeader a non-null String that represent a HTTP header field key
* @return a enum object representing the header
* @throws java.lang.IllegalArgumentException if no header was found for wildHeader or wildHeader is a null pointer
*/
public static HttpHeader canonicalizeHeader(String wildHeader) {
if (wildHeader != null) {
for (HttpHeader h : HttpHeader.values()) {
if (wildHeader.equalsIgnoreCase(h.name)) {
return h;
}
}
}
throw new IllegalArgumentException();
}
@Override
public String toString() {
return name;
}
public String getName() {
return name;
}
}
@@ -0,0 +1,77 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.http;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
/**
* Represents a deduction that was performed by rule-based reasoning that resulted in the fact that the encountered
* a {@link at.jku.fim.rubanetra.protocol.activity.http.HttpActivity}-headers contained image-related
* metadata.
* This class should be considered a convenience class that tries to simplify the collection of derived information
* by the Drools rule specifications.
*/
public class HttpImageActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = -6487431475099739380L;
private String imageType;
private String imagePath;
public HttpImageActivity(HttpActivity httpActivity) {
super(httpActivity);
imagePath = null;
imageType = null;
extendActivity(httpActivity);
}
/**
* @return the type or format of the image as encountered or derived
*/
public String getImageType() {
return imageType;
}
/**
* @param imageType the type or format of the image to set
*/
public void setImageType(String imageType) {
this.imageType = imageType;
}
/**
* @return the absolute or relative path of the image as encountered or derived
*/
public String getImagePath() {
return imagePath;
}
/**
* @param imagePath the path of the image to set
*/
public void setImagePath(String imagePath) {
this.imagePath = imagePath;
}
@Override
public String toString() {
return "HttpImageActivity{" +
"imageType='" + imageType + '\'' +
", imagePath='" + imagePath + '\'' +
'}';
}
}
@@ -0,0 +1,202 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.http;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.protocol.tcpip.Http;
import org.kie.api.definition.type.Role;
import org.kie.api.definition.type.Role.Type;
import org.krakenapps.pcap.decoder.http.HttpMethod;
import org.krakenapps.pcap.decoder.http.HttpRequest;
import org.krakenapps.pcap.decoder.http.HttpVersion;
import java.net.InetSocketAddress;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Encapsulates a {@link org.krakenapps.pcap.decoder.http.HttpRequest}, that was decoded by the Kraken library.
* The goal of this class is to provide a read-only interface to the encountered Http-related information, however,
* currently the actual content buffer that was used by the decoder can both be read from and written to.
* Furthermore, a JNetPcap representation is provided in addition to the Kraken-representation.
*/
@Role(Type.EVENT)
public class HttpRequestActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = -8446692870521248194L;
private final HttpVersion httpVersion;
private final InetSocketAddress serverAddress;
private final InetSocketAddress clientAddress;
private final HttpMethod httpMethod;
private final String httpQueryString;
private final List<NameValuePair> httpQueryParameters;
private final Map<String, String> requestHeaderMap;
private final PcapActivity pcapActivity;
private final URL url;
private final Http httpRequest;
/**
* Constructs a new request activity and replaces the pcap source activity on which this event occurred.
* It decodes the query parameters using the default Charset and {@link org.apache.http.client.utils.URLEncodedUtils}.
*
* @param source the Pcap activity on which this activity occurred
* @param request the decoded Http request
*/
public HttpRequestActivity(PcapActivity source, HttpRequest request) {
super(source);
this.requestHeaderMap = new HashMap<>();
for (String headerKey : request.getHeaderKeys()) {
try {
HttpHeader canonicalHeader = HttpHeader.canonicalizeHeader(headerKey);
requestHeaderMap.put(canonicalHeader.toString(), request.getHeader(headerKey));
} catch (IllegalArgumentException e) {
requestHeaderMap.put(headerKey, request.getHeader(headerKey));
}
}
httpVersion = request.getHttpVersion();
serverAddress = request.getLocalAddress();
clientAddress = request.getRemoteAddress();
httpMethod = request.getMethod();
httpQueryString = request.getQueryString();
httpQueryParameters = URLEncodedUtils.parse(httpQueryString, Charset.defaultCharset());
pcapActivity = source;
url = request.getURL();
httpRequest = new Http();
scanAndFillHttpRequest();
replaceActivity(source);
}
private void scanAndFillHttpRequest() {
PcapPacket packet = pcapActivity.getPcapPacket();
packet.hasHeader(getHttpRequest());
}
/**
* @return the Kraken-specific representation of the version field
*/
public HttpVersion getHttpVersion() {
return httpVersion;
}
/**
* @return the destination address (Layer 3 and 4)
*/
public InetSocketAddress getServerAddress() {
return serverAddress;
}
/**
* @return the source address (Layer 3 and 4)
*/
public InetSocketAddress getClientAddress() {
return clientAddress;
}
/**
* @return the Kraken-specific representation of the method field
*/
public HttpMethod getHttpMethod() {
return httpMethod;
}
/**
* @return the unmodified query string
*/
public String getHttpQueryString() {
return httpQueryString;
}
/**
* @return a collection of the decoded query-parameters (using the default charset)
*/
public List<NameValuePair> getHttpQueryParameters() {
return Collections.unmodifiableList(httpQueryParameters);
}
/**
* @param key the header key
* @return the value for the key, or null if the key was not defined
*/
public String getRequestHeader(String key) {
return this.requestHeaderMap.get(key);
}
/**
* @return an unmodifiable collection containing all header keys and values
*/
public Map<String, String> getRequestHeaderMap() {
return Collections.unmodifiableMap(requestHeaderMap);
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
/**
* @return an URL representation of the destination resource
*/
public URL getUrl() {
return url;
}
/**
* @return the JNetPcap specific representation of the request, if available
*/
public Http getHttpRequest() {
return httpRequest;
}
@Override
public String getSourceAddressAsString() {
return String.valueOf(getClientAddress());
}
@Override
public String getDestinationAddressAsString() {
return String.valueOf(getServerAddress());
}
@Override
public String toString() {
return "HttpRequestActivity{" +
"httpVersion=" + httpVersion +
", serverAddress=" + serverAddress +
", clientAddress=" + clientAddress +
", httpMethod=" + httpMethod +
", httpQueryString='" + httpQueryString + '\'' +
", httpQueryParameters=" + httpQueryParameters +
", requestHeaderMap=" + requestHeaderMap +
", pcapActivity=" + pcapActivity +
", url=" + url +
", httpRequest=" + httpRequest +
'}';
}
}
@@ -0,0 +1,138 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.http;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.protocol.tcpip.Http;
import org.kie.api.definition.type.Role;
import org.krakenapps.pcap.decoder.http.HttpResponse;
import org.krakenapps.pcap.decoder.http.HttpVersion;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Encapsulates a {@link org.krakenapps.pcap.decoder.http.HttpResponse}, that was decoded by the Kraken library.
* The goal of this class is to provide a read-only interface to the encountered Http-related information, however,
* currently the actual content buffer that was used by the decoder can both be read from and written to.
* Furthermore, a JNetPcap representation is provided in addition to the Kraken-representation.
*/
@Role(Role.Type.EVENT)
public class HttpResponseActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = 8779177553506884946L;
private final HttpVersion httpVersion;
private final int responseStatusCode;
private final String responseStatusLine;
private final Map<String, String> responseHeaderMap;
private final PcapActivity pcapActivity;
private final Http httpResponse;
/**
* Constructs a new response activity and replaces the source pcap activity on which this event occurred.
* Tries to canonicalize the encountered headers using
* {@link at.jku.fim.rubanetra.protocol.activity.http.HttpHeader}, if it fails
* the failed header is stored as encountered.
*
* @param source the pcap activity on which this event occurred
* @param response the Http response, decoded by the Kraken library
*/
public HttpResponseActivity(PcapActivity source, HttpResponse response) {
super(source);
this.responseHeaderMap = new HashMap<>();
for (String headerKey : response.getHeaderKeys()) {
try {
HttpHeader canonicalHeader = HttpHeader.canonicalizeHeader(headerKey);
responseHeaderMap.put(canonicalHeader.toString(), response.getHeader(headerKey));
} catch (IllegalArgumentException e) {
responseHeaderMap.put(headerKey, response.getHeader(headerKey));
}
}
httpVersion = response.getHttpVersion();
responseStatusCode = response.getStatusCode();
responseStatusLine = response.getStatusLine();
pcapActivity = source;
httpResponse = new Http();
scanAndFillHttpResponse();
replaceActivity(source);
}
private void scanAndFillHttpResponse() {
PcapPacket packet = pcapActivity.getPcapPacket();
packet.hasHeader(getHttpResponse());
}
/**
* @return the Kraken specific representation of the Http version field
*/
public HttpVersion getHttpVersion() {
return httpVersion;
}
/**
* @return the HTTP response code as encountered
*/
public int getResponseStatusCode() {
return responseStatusCode;
}
/**
* @return Http response status line as encountered
*/
public String getResponseStatusLine() {
return responseStatusLine;
}
/**
* @return an unmodifiable collection containing the decoded headers
*/
public Map<String, String> getResponseHeaderMap() {
return Collections.unmodifiableMap(responseHeaderMap);
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
/**
* @return the JNetPcap specific representation of the Http response
*/
public Http getHttpResponse() {
return httpResponse;
}
@Override
public String toString() {
return "HttpResponseActivity{" +
"httpVersion=" + httpVersion +
", responseStatusCode=" + responseStatusCode +
", responseStatusLine='" + responseStatusLine + '\'' +
", responseHeaderMap=" + responseHeaderMap +
", pcapActivity=" + pcapActivity +
", httpResponse=" + httpResponse +
'}';
}
}
@@ -0,0 +1,187 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.icmp;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.protocol.network.Icmp;
import org.jnetpcap.protocol.network.Icmp.IcmpCode;
import org.jnetpcap.protocol.network.Icmp.IcmpType;
import org.krakenapps.pcap.decoder.icmp.IcmpMessage;
import org.krakenapps.pcap.decoder.icmp.IcmpPacket;
import java.net.InetAddress;
/**
* Encapsulates an {@link org.krakenapps.pcap.decoder.icmp.IcmpPacket}.
*/
public class Icmpv4Activity extends AbstractReplaceableActivity {
private static final long serialVersionUID = -2557140540329408425L;
private final int icmpSubType;
private final IcmpPacket icmpPacket;
private final String icmpMessage;
private final PcapActivity pcapActivity;
private final IcmpType icmpType;
private final IcmpCode icmpCode;
private final InetAddress sourceAddress;
private final InetAddress destinationAddress;
private final int identifier;
private final int sequence;
private final Icmp icmp;
/**
* Constructs a new ICMP activity (IPv4 specific) and replaces the source Pcap activity on which this activity occurred
*
* @param source the Pcap activity on which this event occurred
* @param icmpPacket the Kraken specific representation of an ICMP packet
*/
public Icmpv4Activity(PcapActivity source, IcmpPacket icmpPacket) {
super(source);
icmpSubType = icmpPacket.getCode();
icmpMessage = IcmpMessage.getMessage(icmpPacket.getType(), icmpSubType);
sourceAddress = icmpPacket.getSource();
destinationAddress = icmpPacket.getDestination();
identifier = icmpPacket.getId();
sequence = icmpPacket.getSeq();
this.icmpPacket = icmpPacket;
pcapActivity = source;
icmpCode = IcmpCode.valueOf(icmpPacket.getType(), icmpSubType);
icmpType = IcmpType.valueOf(icmpPacket.getType());
icmp = new Icmp();
scanAndFillIcmp();
replaceActivity(source);
setExcludedFromOutput(true);
}
private void scanAndFillIcmp() {
PcapPacket packet = pcapActivity.getPcapPacket();
packet.hasHeader(getIcmp());
}
/**
* @return The Kraken specific representation including content buffer
*/
public IcmpPacket getIcmpPacket() {
return icmpPacket;
}
/**
* @return the ICMP message String unaltered
*/
public String getIcmpMessage() {
return icmpMessage;
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
/**
* @return the JNetPcap specific enumeration for the ICMP code
*/
public IcmpCode getIcmpCode() {
return icmpCode;
}
/**
* @return the JNetPcap specific ICMP Type
*/
public IcmpType getIcmpType() {
return icmpType;
}
/**
* @return the ICMP Sub-Type or Code as encountered
*/
public int getIcmpSubType() {
return icmpSubType;
}
/**
* @return L3/L4 source address
*/
public InetAddress getSourceAddress() {
return sourceAddress;
}
/**
* @return L3/L4 destination address
*/
public InetAddress getDestinationAddress() {
return destinationAddress;
}
/**
* @return the message identifier as is
*/
public int getIdentifier() {
return identifier;
}
/**
* @return the sequence number as is
*/
public int getSequence() {
return sequence;
}
/**
* @return the JNetPcap specific ICMP representation if available
*/
public Icmp getIcmp() {
return icmp;
}
@Override
public String getSourceAddressAsString() {
return String.valueOf(getSourceAddress());
}
@Override
public String getDestinationAddressAsString() {
return String.valueOf(getDestinationAddress());
}
@Override
public String toString() {
return "Icmpv4Activity{" +
"icmpSubType=" + icmpSubType +
", icmpPacket=" + icmpPacket +
", icmpMessage='" + icmpMessage + '\'' +
", pcapActivity=" + pcapActivity +
", icmpType=" + icmpType +
", icmpCode=" + icmpCode +
", sourceAddress=" + sourceAddress +
", destinationAddress=" + destinationAddress +
", identifier=" + identifier +
", sequence=" + sequence +
", icmp=" + icmp +
'}';
}
}
@@ -0,0 +1,154 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.icmp;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.protocol.network.Icmp;
import org.krakenapps.pcap.decoder.icmpv6.Icmpv6Message;
import org.krakenapps.pcap.decoder.icmpv6.Icmpv6Packet;
import java.net.InetAddress;
/**
* IPv6 specific class similar to {@link at.jku.fim.rubanetra.protocol.activity.icmp.Icmpv4Activity},
* but encapsulates an {@link org.krakenapps.pcap.decoder.icmpv6.Icmpv6Packet} instead.
*/
public class Icmpv6Activity extends AbstractReplaceableActivity {
private static final long serialVersionUID = 5042112889089462354L;
private final int icmpType;
private final int icmpSubType;
private final Icmpv6Packet icmpPacket;
private final String icmpMessage;
private final PcapActivity pcapActivity;
private final Icmp icmp;
private final InetAddress destinationAddress;
private final InetAddress sourceAddress;
/**
* Constructs a new ICMPv6 activity, replacing the source Pcap activity and providing a JNetPcap representation of
* the packet.
* @param source the event that led to the decoded icmpPacket
* @param icmpPacket the ICMP message decoded by Kraken
*/
public Icmpv6Activity(PcapActivity source, Icmpv6Packet icmpPacket) {
super(source);
icmpType = icmpPacket.getType();
icmpSubType = icmpPacket.getCode();
icmpMessage = Icmpv6Message.getMessage(icmpType);
sourceAddress = icmpPacket.getSource();
destinationAddress = icmpPacket.getDestination();
this.icmpPacket = icmpPacket;
pcapActivity = source;
icmp = new Icmp();
scanAndFillIcmp();
replaceActivity(source);
}
private void scanAndFillIcmp() {
PcapPacket packet = pcapActivity.getPcapPacket();
packet.hasHeader(getIcmp());
}
/**
* @return the type of the ICMP message
*/
public int getIcmpType() {
return icmpType;
}
/**
* @return the subtype of the ICMP message
*/
public int getIcmpSubType() {
return icmpSubType;
}
/**
* @return the related Kraken object, as passed to constructor
*/
public Icmpv6Packet getIcmpPacket() {
return icmpPacket;
}
/**
* @return a String representation for various ICMP messages
* @see org.krakenapps.pcap.decoder.icmpv6.Icmpv6Message
*/
public String getIcmpMessage() {
return icmpMessage;
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
/**
* @return JNetPcap's representation of an ICMP packet
*/
public Icmp getIcmp() {
return icmp;
}
/**
* @return L3/L4 source address
*/
public InetAddress getSourceAddress() {
return sourceAddress;
}
/**
* @return L3/L4 destination address
*/
public InetAddress getDestinationAddress() {
return destinationAddress;
}
@Override
public String getSourceAddressAsString() {
return String.valueOf(getSourceAddress());
}
@Override
public String getDestinationAddressAsString() {
return String.valueOf(getDestinationAddress());
}
@Override
public String toString() {
return "Icmpv6Activity{" +
"icmpType=" + icmpType +
", icmpSubType=" + icmpSubType +
", icmpPacket=" + icmpPacket +
", icmpMessage='" + icmpMessage + '\'' +
", pcapActivity=" + pcapActivity +
", icmp=" + icmp +
", destinationAddress=" + destinationAddress +
", sourceAddress=" + sourceAddress +
'}';
}
}
@@ -0,0 +1,80 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.icmp;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.kie.api.definition.type.Role;
import org.kie.api.definition.type.Role.Type;
/**
* Represents the derived fact, that both an Echo request and a related Echo reply were encountered.
* This activity will be constructed by rule evaluations.
* ENHANCEMENT: ICMPv6
*/
@Role(Type.FACT)
public class PingActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = -7127689333196188386L;
private final Icmpv4Activity request;
private final Icmpv4Activity reply;
/**
* Constructs a new Ping activity and replaces both the echo request/reply.
* @param request the echo request
* @param reply the corresponding and matched echo reply
*/
public PingActivity(Icmpv4Activity request, Icmpv4Activity reply) {
super(request);
this.request = request;
this.reply = reply;
replaceActivity(request);
replaceActivity(reply);
}
/**
* @return the encountered echo request
*/
public Icmpv4Activity getRequest() {
return request;
}
/**
* @return the matched echo reply
*/
public Icmpv4Activity getReply() {
return reply;
}
@Override
public String getSourceAddressAsString() {
return getRequest().getSourceAddressAsString();
}
@Override
public String getDestinationAddressAsString() {
return getReply().getDestinationAddressAsString();
}
@Override
public String toString() {
return "PingActivity{" +
"request=" + request +
", reply=" + reply +
'}';
}
}
@@ -0,0 +1,145 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.ip;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.kie.api.definition.type.Role;
import org.kie.api.definition.type.Role.Type;
import org.krakenapps.pcap.decoder.ip.Ipv4Packet;
import org.krakenapps.pcap.decoder.ipv6.Ipv6Packet;
import java.net.InetAddress;
/**
* This class represents the abstraction of {@link at.jku.fim.rubanetra.protocol.activity.ip.Ipv4Activity}
* and {@link at.jku.fim.rubanetra.protocol.activity.ip.Ipv6Activity}. Several common attributes
* are available through instances of this class, in particular:
* <ul>
* <li>the protocol version</li>
* <li>the protocol type</li>
* <li>the source L3 address</li>
* <li>the destination L3 address</li>
* <li>the source Pcap activity from which this IP packet was derived from</li>
* </ul>
*/
@Role(Type.EVENT)
public abstract class IpActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = 8337666572553550945L;
private final int version;
private final int protocol;
private final InetAddress sourceAddress, destinationAddress;
private final PcapActivity pcapActivity;
/**
* IPv4 constructor to be called by subclasses. The source activity will be replaced and this instance will be
* excluded from output.
*
* @param source the source event on which the IPv4 packet was found
* @param ipv4Packet the decoded IPv4 packet (Kraken)
*/
public IpActivity(PcapActivity source, Ipv4Packet ipv4Packet) {
super(source);
this.version = ipv4Packet.getVersion();
this.protocol = ipv4Packet.getProtocol();
this.sourceAddress = ipv4Packet.getSourceAddress();
this.destinationAddress = ipv4Packet.getDestinationAddress();
this.pcapActivity = source;
replaceActivity(source);
setExcludedFromOutput(true);
}
/**
* IPv6 constructor to be called by subclasses. The source activity will be replaced and this instance will be
* excluded from output.
*
* @param source the source event on which the IPv6 packet was found
* @param ipv6Packet the decoded IPv6 packet (Kraken)
*/
public IpActivity(PcapActivity source, Ipv6Packet ipv6Packet) {
super(source);
this.version = ipv6Packet.getVersion();
protocol = ipv6Packet.getNextHeader();
sourceAddress = ipv6Packet.getSourceAddress();
destinationAddress = ipv6Packet.getDestinationAddress();
this.pcapActivity = source;
replaceActivity(source);
setExcludedFromOutput(true);
}
/**
* @return the internet protocol version
*/
public int getVersion() {
return version;
}
/**
* @return the protocol type (IPv4) or the next header value (IPv6)
*/
public int getProtocol() {
return protocol;
}
/**
* @return the IPv4/IPv6 source address
*/
public InetAddress getSourceAddress() {
return sourceAddress;
}
/**
* @return the IPv4/IPv6 destination address
*/
public InetAddress getDestinationAddress() {
return destinationAddress;
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
@Override
public String getSourceAddressAsString() {
return String.valueOf(getSourceAddress());
}
@Override
public String getDestinationAddressAsString() {
return String.valueOf(getDestinationAddress());
}
@Override
public String toString() {
return "IpActivity{" +
"version=" + version +
", protocol=" + protocol +
", sourceAddress=" + sourceAddress +
", destinationAddress=" + destinationAddress +
", pcapActivity=" + pcapActivity +
'}';
}
}
@@ -0,0 +1,171 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.ip;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.protocol.network.Ip4;
import org.kie.api.definition.type.Role;
import org.kie.api.definition.type.Role.Type;
import org.krakenapps.pcap.decoder.ip.Ipv4Packet;
import java.util.Arrays;
/**
* Encapsulates all available/decoded IPv4 packet fields and provides a compatible JNetPcap
* {@link org.jnetpcap.protocol.network.Ip4} object.
* Instances of this class are excluded from output by default in order to decrease verbosity,
* {@link #setExcludedFromOutput(boolean)} may be called to change this behaviour.
*/
@Role(Type.EVENT)
public class Ipv4Activity extends IpActivity {
private static final long serialVersionUID = -7231432195268189132L;
private final int internetHeaderLength;
private final int differentiatedServicesCodePoint;
private final int totalLength;
private final int identification;
private final int flags;
private final int fragmentOffset;
private final int timeToLive;
private final int headerChecksum;
private final byte[] options;
private final Ip4 ipv4;
/**
* Constructs a new IPv4 activity that will not be included in the final output. The source pcap activity will be
* replaced and the packet fields encapsulated.
* @param source the Pcap activity from which this packet was decoded (final one, if fragmented).
* @param ipv4Packet the decoded IPv4 packet (Kraken)
*/
public Ipv4Activity(PcapActivity source, Ipv4Packet ipv4Packet) {
super(source, ipv4Packet);
this.internetHeaderLength = ipv4Packet.getIhl();
this.differentiatedServicesCodePoint = ipv4Packet.getTos();
this.totalLength = ipv4Packet.getTotalLength();
this.identification = ipv4Packet.getId();
this.flags = ipv4Packet.getFlags();
this.fragmentOffset = ipv4Packet.getFragmentOffset();
this.timeToLive = ipv4Packet.getTtl();
this.headerChecksum = ipv4Packet.getHeaderChecksum();
this.options = ipv4Packet.getOptions();
PcapPacket pcap = source.getPcapPacket();
ipv4 = new Ip4();
pcap.hasHeader(ipv4);
replaceActivity(source);
setExcludedFromOutput(true);
}
/**
* @throws java.lang.IllegalStateException since it is not yet implemented
*/
public int getExplicitCongestionNotification() {
throw new IllegalStateException("Not yet implemented");
}
/**
* @return the length of the IPv4 packet header in bytes
*/
public int getInternetHeaderLength() {
return internetHeaderLength;
}
/**
* @return the differentiated services code point value
*/
public int getDifferentiatedServicesCodePoint() {
return differentiatedServicesCodePoint;
}
/**
* @return the total packet size in bytes
*/
public int getTotalLength() {
return totalLength;
}
/**
* @return the identification value
*/
public int getIdentification() {
return identification;
}
/**
* @return the flags combined in one integer
*/
public int getFlags() {
return flags;
}
/**
* @return the fragment offset value
*/
public int getFragmentOffset() {
return fragmentOffset;
}
/**
* @return the TTL value
*/
public int getTimeToLive() {
return timeToLive;
}
/**
* @return the checksum
*/
public int getHeaderChecksum() {
return headerChecksum;
}
/**
* @return the options as encountered
*/
public byte[] getOptions() {
return options;
}
/**
* @return the JNetPcap representation of the IPv4 packet iff available, null otherwise (e.g. if the packet is
* fragmented)
*/
public Ip4 getIpv4() {
return ipv4;
}
@Override
public String toString() {
return "Ipv4Activity{" +
"internetHeaderLength=" + internetHeaderLength +
", differentiatedServicesCodePoint=" + differentiatedServicesCodePoint +
", totalLength=" + totalLength +
", identification=" + identification +
", flags=" + flags +
", fragmentOffset=" + fragmentOffset +
", timeToLive=" + timeToLive +
", headerChecksum=" + headerChecksum +
", options=" + Arrays.toString(options) +
", ipv4=" + ipv4 +
'}';
}
}
@@ -0,0 +1,130 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.ip;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.protocol.network.Ip6;
import org.kie.api.definition.type.Role;
import org.kie.api.definition.type.Role.Type;
import org.krakenapps.pcap.decoder.ipv6.Ipv6Packet;
/**
* Encapsulates certain IPv6 packet fields, and provides a compatible JNetPcap representation
* ({@link org.jnetpcap.protocol.network.Ip6}) of the packet.
* By default, instances of this class will be excluded from the final output due to verbosity,
* {@link #setExcludedFromOutput(boolean)} may be called to change this behaviour.
*/
@Role(Type.EVENT)
public class Ipv6Activity extends IpActivity {
private static final long serialVersionUID = 6406883861304202335L;
private final byte trafficClass;
private final int flowLabel;
private final int payloadLength;
private final byte nextHeader;
private final int hopLimit;
private final Ip6 ipv6;
private final Ipv6Packet ipv6Packet;
/**
* Construct a new IPv6 activity and replaces the source Pcap activity on which this activity occurred,
*
* @param source the Pcap activity on which this event occurred.
* @param ipv6Packet the decoded IPv6 packet (Kraken)
*/
public Ipv6Activity(PcapActivity source, Ipv6Packet ipv6Packet) {
super(source, ipv6Packet);
this.ipv6Packet = ipv6Packet;
trafficClass = ipv6Packet.getTrafficClass();
flowLabel = ipv6Packet.getFlowLabel();
payloadLength = ipv6Packet.getPayloadLength();
nextHeader = ipv6Packet.getNextHeader();
hopLimit = ipv6Packet.getHopLimit();
PcapPacket pcap = source.getPcapPacket();
ipv6 = new Ip6();
pcap.hasHeader(ipv6);
replaceActivity(source);
setExcludedFromOutput(true);
}
/**
* @return the traffic class value
*/
public byte getTrafficClass() {
return trafficClass;
}
/**
* @return the flow label value
*/
public int getFlowLabel() {
return flowLabel;
}
/**
* @return the size of the payload in bytes
*/
public int getPayloadLength() {
return payloadLength;
}
/**
* @return the next header value, redundant to {@link #getProtocol()}
*/
public byte getNextHeader() {
return nextHeader;
}
/**
* @return the Hop limit value
*/
public int getHopLimit() {
return hopLimit;
}
/**
* @return the JNetPcap representation of an IPv6 packet, if available. null otherwise
*/
public Ip6 getIpv6() {
return ipv6;
}
/**
* @return the original Kraken IPv6 packet representation
*/
public Ipv6Packet getIpv6Packet() {
return ipv6Packet;
}
@Override
public String toString() {
return "Ipv6Activity{" +
"trafficClass=" + trafficClass +
", flowLabel=" + flowLabel +
", payloadLength=" + payloadLength +
", nextHeader=" + nextHeader +
", hopLimit=" + hopLimit +
", ipv6=" + ipv6 +
", ipv6Packet=" + ipv6Packet +
'}';
}
}
@@ -0,0 +1,85 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.msn;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.kie.api.definition.type.Role;
import org.kie.api.definition.type.Role.Type;
/**
* Encapsulates MSN chat messages.
*/
@Role(Type.EVENT)
public class MsnActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = -4422657387707609350L;
private final PcapActivity pcapActivity;
private final String account;
private final String chat;
/**
* Constructs a new MSN chat activity, replaces the source pcap activity on which this event occurred.
* @param source the source event on which this activity occurred
* @param account the account name that was used for the chat
* @param chat the chat message
*/
public MsnActivity(PcapActivity source, String account, String chat) {
super(source);
pcapActivity = source;
this.account = account;
this.chat = chat;
replaceActivity(source);
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
/**
* @return the account name used for chatting
*/
public String getAccount() {
return account;
}
/**
* @return the chat message
*/
public String getChat() {
return chat;
}
@Override
public String getSourceAddressAsString() {
return getAccount();
}
@Override
public String toString() {
return "MsnActivity{" +
"pcapActivity=" + pcapActivity +
", account='" + account + '\'' +
", chat='" + chat + '\'' +
'}';
}
}
@@ -0,0 +1,93 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.netbios;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.krakenapps.pcap.decoder.netbios.NetBiosDatagramPacket;
import org.krakenapps.pcap.decoder.netbios.NetBiosNamePacket;
/**
* Encapsulates {@link org.krakenapps.pcap.decoder.netbios.NetBiosDatagramPacket}s.
*/
public class NetbiosActivity extends AbstractReplaceableActivity {
private final PcapActivity pcapActivity;
private final NetBiosNamePacket namePacket;
private final NetBiosDatagramPacket datagramPacket;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred (will be replaced).
* @throws IllegalArgumentException if source is null.
*/
public NetbiosActivity(PcapActivity source, NetBiosDatagramPacket datagramPacket) {
super(source);
pcapActivity = source;
this.datagramPacket = datagramPacket;
this.namePacket = null;
replaceActivity(source);
}
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred (will be replaced).
* @throws IllegalArgumentException if source is null.
*/
public NetbiosActivity(PcapActivity source, NetBiosNamePacket namePacket) {
super(source);
pcapActivity = source;
this.namePacket = namePacket;
this.datagramPacket = null;
replaceActivity(source);
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
/**
* @return the decoded packet (by Kraken)
*/
public NetBiosNamePacket getNamePacket() {
return namePacket;
}
/**
* @return the original decoded datagram.
*/
public NetBiosDatagramPacket getDatagramPacket() {
return datagramPacket;
}
@Override
public String toString() {
return "NetbiosActivity{" +
"pcapActivity=" + pcapActivity +
", namePacket=" + namePacket +
", datagramPacket=" + datagramPacket +
'}';
}
}
@@ -0,0 +1,163 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.pop3;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.krakenapps.mime.MimeHeader;
import org.krakenapps.pcap.decoder.pop3.Pop3Data;
/**
* TODO: this class represents a partial Pop3-Activity, i.e. it should be reassembled by drools into a single session
* TODO: based on the source/destination socket addresses
*/
public class Pop3Activity extends AbstractReplaceableActivity {
private static final long serialVersionUID = 5803080617646591389L;
private final PcapActivity pcapActivity;
private final Type subType;
/**
* RECEIVE partial Type fields: header, data
*/
private MimeHeader header;
private Pop3Data data;
/**
* COMMAND partial Type field: command
*/
private String command;
/**
* RESPONSE partial Type field: response
*/
private String response;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @throws IllegalArgumentException if source is null.
*/
public Pop3Activity(PcapActivity source, Type partialActivityType) {
super(source);
pcapActivity = source;
this.subType = partialActivityType;
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
/**
* @return the subtype of this POP activity
*/
public Type getSubType() {
return subType;
}
/**
* @return the decoded {@link org.krakenapps.mime.MimeHeader} if available, null otherwise
*/
public MimeHeader getHeader() {
return header;
}
/**
* This method will be called by {@link at.jku.fim.rubanetra.protocol.handler.KrakenPop3Handler}
* in order to build a partial POP session.
*
* @param header the decoded MIME header.
*/
public void setHeader(MimeHeader header) {
this.header = header;
}
/**
* @return the decoded {@link org.krakenapps.pcap.decoder.pop3.Pop3Data} if available, null otherwise
*/
public Pop3Data getData() {
return data;
}
/**
* This method will be called by {@link at.jku.fim.rubanetra.protocol.handler.KrakenPop3Handler}
* during the decoding process.
*
* @param data the decoded POP data
*/
public void setData(Pop3Data data) {
this.data = data;
}
/**
* @return the decoded command string if available, null otherwise
*/
public String getCommand() {
return command;
}
/**
* This method will be called by {@link at.jku.fim.rubanetra.protocol.handler.KrakenPop3Handler} in
* order to build a partial session during the encoding process.
*
* @param command the decoded command.
*/
public void setCommand(String command) {
this.command = command;
}
/**
* @return the decoded response message
*/
public String getResponse() {
return response;
}
/**
* This method will be called by {@link at.jku.fim.rubanetra.protocol.handler.KrakenPop3Handler} in
* order to build a partial session during the encoding process.
*
* @param response the decoded response string
*/
public void setResponse(String response) {
this.response = response;
}
/**
* POP3 Activity type of the surrounding object
*/
public enum Type {
RECEIVE, COMMAND, RESPONSE
}
@Override
public String toString() {
return "Pop3Activity{" +
"pcapActivity=" + pcapActivity +
", subType=" + subType +
", header=" + header +
", data=" + data +
", command='" + command + '\'' +
", response='" + response + '\'' +
'}';
}
}
@@ -0,0 +1,90 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.skype;
import org.jnetpcap.protocol.tcpip.Udp;
/**
* This class does not represent an activity but provides some useful static helper methods. Particularly it provides
* quite basic byte-based checks whether or not a certain UDP-Payload could indicate the presence of Skype related
* traffic.
* Additionally, the Skype object ID can be extracted as well.
* Those checks are based on the OpenSkype project, see https://github.com/matthiasbock/OpenSkype
*/
public class SkypeActivityHelper {
/**
* Returns the Skype object ID, however, since the object ID could be arbitrary this method
* does not check whether the payload corresponds to Skype traffic.
*
* @param udp the JNetPcap UDP packet representation
* @return the skype object id
* @see #hasSkypePayload(org.jnetpcap.protocol.tcpip.Udp)
*/
public static int objectId(Udp udp) {
if (udp != null && udp.hasPayload() && udp.getPayloadLength() > 3) {
return udp.getPacket().getUShort(udp.getPayloadOffset());
}
return -1;
}
/**
* Calls {@link #hasSkypeType(org.jnetpcap.protocol.tcpip.Udp, at.jku.fim.rubanetra.protocol.activity.skype.SkypeActivityHelper.SkypeUdpType)}
* with the PAYLOAD flag internally (convenience method).
*
* @param udp the JNetPcap UDP packet representation
* @return true, if this UDP packet could contain Skype related traffic
*/
public static boolean hasSkypePayload(Udp udp) {
return hasSkypeType(udp, SkypeUdpType.PAYLOAD);
}
/**
* Tries to determine whether or not the provided UDP packet contains data that corresponds to some known
* Skype flags. Note however, that these flags may be invalid at any future point in time.
*
* @param udp the JNetPcap UDP packet representation
* @param type the type of the data to check against
* @return true, if udp potentially contains data corresponding to type
*/
public static boolean hasSkypeType(Udp udp, SkypeUdpType type) {
if (udp != null && type != null && udp.hasPayload() && udp.getPayloadLength() > 3) {
int thirdByte = udp.getPacket().getUByte(udp.getPayloadOffset() + 2);
return (thirdByte & 0x0F) == type.getVal();
}
return false;
}
/**
* Several known Skype payload types including their flag value,
* based on the OpenSkype project, see https://github.com/matthiasbock/OpenSkype
*/
public enum SkypeUdpType {
PAYLOAD(0x02), RESEND(0x03), CONFIRM(0x5), ERROR(0x7), AUDIO(0xd), FRAGMENT(0xf);
private final int val;
SkypeUdpType(int val) {
this.val = val;
}
public int getVal() {
return val;
}
}
}
@@ -0,0 +1,62 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.smtp;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.kie.api.definition.type.Role;
import org.kie.api.definition.type.Role.Type;
/**
* Represents an abstraction for {@link at.jku.fim.rubanetra.protocol.activity.smtp.SmtpCommandActivity},
* {@link at.jku.fim.rubanetra.protocol.activity.smtp.SmtpReplyActivity} and
* {@link at.jku.fim.rubanetra.protocol.activity.smtp.SmtpSendActivity} that may be used by Drools rules
* in order to determine that some SMTP related traffic occurred for a given {@link at.jku.fim.rubanetra.pcap.PcapActivity}.
*/
@Role(Type.EVENT)
public abstract class SmtpActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = 6159462782844297508L;
private final PcapActivity pcapActivity;
/**
* Constructs a SMTP activity and replaced the source pcap activity on which this event occurred.
*
* @param source the pcap activity on which this event occurred
*/
public SmtpActivity(PcapActivity source) {
super(source);
pcapActivity = source;
replaceActivity(source);
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
@Override
public String toString() {
return "SmtpActivity{" +
"pcapActivity=" + pcapActivity +
'}';
}
}
@@ -0,0 +1,65 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.smtp;
import at.jku.fim.rubanetra.pcap.PcapActivity;
/**
* Encapsulates the SMTP command and parameter values.
*/
public class SmtpCommandActivity extends SmtpActivity {
private static final long serialVersionUID = 1579865560446923831L;
private final String command;
private final String parameter;
/**
* Constructs a new command activity and replaces the source pcap activity.
*
* @param source the pcap activity on which this event occurred
* @param command the command string
* @param parameter the parameter value
*/
public SmtpCommandActivity(PcapActivity source, String command, String parameter) {
super(source);
this.command = command;
this.parameter = parameter;
}
/**
* @return the command value
*/
public String getCommand() {
return command;
}
/**
* @return the parameter value
*/
public String getParameter() {
return parameter;
}
@Override
public String toString() {
return "SmtpCommandActivity{" +
"command='" + command + '\'' +
", parameter='" + parameter + '\'' +
'}';
}
}
@@ -0,0 +1,65 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.smtp;
import at.jku.fim.rubanetra.pcap.PcapActivity;
/**
* Encapsulates the SMTP reply code number and the SMTP message.
*/
public class SmtpReplyActivity extends SmtpActivity {
private static final long serialVersionUID = 7746759264444813246L;
private final int code;
private final String message;
/**
* Constructs a new SMTP reply activity and replaces the source pcap activity on which this event occurred initially.
*
* @param source the pcap activity on which this event occurred.
* @param code the code number of the reply
* @param message the decoded message itself
*/
public SmtpReplyActivity(PcapActivity source, int code, String message) {
super(source);
this.code = code;
this.message = message;
}
/**
* @return the reply code number
*/
public int getCode() {
return code;
}
/**
* @return the decoded message
*/
public String getMessage() {
return message;
}
@Override
public String toString() {
return "SmtpReplyActivity{" +
"code=" + code +
", message='" + message + '\'' +
'}';
}
}
@@ -0,0 +1,68 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.smtp;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import org.krakenapps.mime.MimeHeader;
import org.krakenapps.pcap.decoder.smtp.SmtpData;
/**
* Encapsulates the {@link org.krakenapps.mime.MimeHeader} and the {@link org.krakenapps.pcap.decoder.smtp.SmtpData} for
* SMTP.
*/
public class SmtpSendActivity extends SmtpActivity {
private static final long serialVersionUID = -5050757703933049654L;
private final MimeHeader header;
private final SmtpData data;
/**
* Constructs a new Smtp send activity that replaces the source pcap activity on which this event occurred.
*
* @param source the pcap activity on which the SMTP send activity occurred.
* @param header the decoded MIME header
* @param data the SMTP data
*/
public SmtpSendActivity(PcapActivity source, MimeHeader header, SmtpData data) {
super(source);
this.header = header;
this.data = data;
}
/**
* @return the decoded MIME header (Kraken)
*/
public MimeHeader getHeader() {
return header;
}
/**
* @return the decoded SMTP data (Kraken)
*/
public SmtpData getData() {
return data;
}
@Override
public String toString() {
return "SmtpSendActivity{" +
"header=" + header +
", data=" + data +
'}';
}
}
@@ -0,0 +1,102 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.snmp;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.krakenapps.pcap.decoder.snmp.v1.Pdu;
import java.net.InetSocketAddress;
/**
* Encapsulates a {@link org.krakenapps.pcap.decoder.snmp.v1.Pdu} object including source and destination
* L3/L4 addresses.
*/
public class Snmpv1Activity extends AbstractReplaceableActivity {
private static final long serialVersionUID = 398825166451021814L;
private final PcapActivity pcapActivity;
private final Pdu pdu;
private final InetSocketAddress sourceSocketAddress, destinationSocketAddress;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @param pdu the decoded {@link org.krakenapps.pcap.decoder.snmp.v1.Pdu}
* @param sourceAddress the L3/L4 source address
* @param destinationAddress the L3/L4 destination address
*/
public Snmpv1Activity(PcapActivity source, Pdu pdu, InetSocketAddress sourceAddress, InetSocketAddress destinationAddress) {
super(source);
pcapActivity = source;
this.pdu = pdu;
this.sourceSocketAddress = sourceAddress;
this.destinationSocketAddress = destinationAddress;
replaceActivity(source);
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
/**
* @return the decoded PDU
*/
public Pdu getPdu() {
return pdu;
}
/**
* @return the L3/L4 source address
*/
public InetSocketAddress getSourceSocketAddress() {
return sourceSocketAddress;
}
/**
* @return the L3/L4 destination address
*/
public InetSocketAddress getDestinationSocketAddress() {
return destinationSocketAddress;
}
@Override
public String getSourceAddressAsString() {
return String.valueOf(getSourceSocketAddress());
}
@Override
public String getDestinationAddressAsString() {
return String.valueOf(getDestinationSocketAddress());
}
@Override
public String toString() {
return "Snmpv1Activity{" +
"pcapActivity=" + pcapActivity +
", pdu=" + pdu +
", sourceSocketAddress=" + sourceSocketAddress +
", destinationSocketAddress=" + destinationSocketAddress +
'}';
}
}
@@ -0,0 +1,101 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.snmp;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.krakenapps.pcap.decoder.snmp.v2.Pdu;
import java.net.InetSocketAddress;
/**
* Created by stefan on 3/19/14.
*/
public class Snmpv2Activity extends AbstractReplaceableActivity {
private static final long serialVersionUID = -5230001535702929422L;
private final PcapActivity pcapActivity;
private final Pdu pdu;
private final InetSocketAddress sourceSocketAddress, destinationSocketAddress;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred.
* @param pdu the decoded {@link org.krakenapps.pcap.decoder.snmp.v2.Pdu}
* @param sourceAddress the L3/L4 source address
* @param destinationAddress the L3/L4 destination address
*/
public Snmpv2Activity(PcapActivity source, Pdu pdu, InetSocketAddress sourceAddress, InetSocketAddress destinationAddress) {
super(source);
pcapActivity = source;
this.pdu = pdu;
this.sourceSocketAddress = sourceAddress;
this.destinationSocketAddress = destinationAddress;
replaceActivity(source);
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
/**
* @return the decoded PDU
*/
public Pdu getPdu() {
return pdu;
}
/**
* @return the L3/L4 source address
*/
public InetSocketAddress getSourceSocketAddress() {
return sourceSocketAddress;
}
/**
* @return the L3/L4 destination address
*/
public InetSocketAddress getDestinationSocketAddress() {
return destinationSocketAddress;
}
@Override
public String getSourceAddressAsString() {
return String.valueOf(getSourceSocketAddress());
}
@Override
public String getDestinationAddressAsString() {
return String.valueOf(getDestinationSocketAddress());
}
@Override
public String toString() {
return "Snmpv2Activity{" +
"pcapActivity=" + pcapActivity +
", pdu=" + pdu +
", sourceSocketAddress=" + sourceSocketAddress +
", destinationSocketAddress=" + destinationSocketAddress +
'}';
}
}
@@ -0,0 +1,442 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.tcp;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import at.jku.fim.rubanetra.protocol.activity.ip.IpActivity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.packet.format.FormatUtils;
import org.jnetpcap.protocol.tcpip.Tcp;
import org.kie.api.definition.type.Role;
import org.krakenapps.pcap.decoder.tcp.TcpDirection;
import org.krakenapps.pcap.decoder.tcp.TcpPacket;
import org.krakenapps.pcap.decoder.tcp.TcpSession;
import org.krakenapps.pcap.decoder.tcp.TcpState;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Arrays;
/**
* Encapsulates a vast amount of TCP flags/options/values and derived information, however, some fields have not yet
* been implemented by the Kraken TCP-Decoder and/or the JNetPcap-decoder. In these cases either a value of -1 or null
* is returned instead, e.g. for relative seq/ack numbers or the client/server state or the traffic direction.
* Two constructors are provided, one is intended for use by decoded Kraken TCP packets {@link org.krakenapps.pcap.decoder.tcp.TcpPacket}
* the other one for the JNetPcap equivalent {@link org.jnetpcap.protocol.tcpip.Tcp}.
* However, independent of the used constructor the JNetPcap representation will always be made available if possible.
* Instances of this class will not be included in the final output, {@link #setExcludedFromOutput(boolean)} should be
* called appropriately to override this behaviour.
*/
@Role(Role.Type.EVENT)
public class TcpActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = -2422097413536740312L;
private final int sourcePort, destinationPort;
private final InetAddress sourceAddress, destinationAddress;
private final long sequenceNumber;
private final long acknowledgeNumber;
private final int relativeSequenceNumber;
private final int relativeAcknowledgeNumber;
private final int dataOffset;
private final int controlBits;
private final int windowSize, checksum, urgentPointer, tcpLength;
private final byte[] options, padding;
private final boolean syn, ack, psh, fin, rst, urg;
private final TcpDirection direction;
private final TcpState clientState, serverState;
private final PcapActivity pcapActivity;
private final Tcp tcp;
private final InetSocketAddress sourceSocketAddress, destinationSocketAddress;
/**
* Constructs a new Tcp activity using the decoded Kraken data. The source pcap activity on which this TCP-activity
* occurred will be replaced.
*
* @param source the final pacp activity that led to a successful decode by the Kraken TCP decoder.
* @param tcpPacket the decoded TCP packet data
* @param tcpSession the maintained TCP session information (client & server state)
*/
public TcpActivity(PcapActivity source, TcpPacket tcpPacket, TcpSession tcpSession) {
super(source);
this.sourcePort = tcpPacket.getSourcePort();
this.destinationPort = tcpPacket.getDestinationPort();
this.sourceAddress = tcpPacket.getSourceAddress();
this.sourceSocketAddress = tcpPacket.getSource();
this.destinationAddress = tcpPacket.getDestinationAddress();
this.destinationSocketAddress = tcpPacket.getDestination();
this.sequenceNumber = tcpPacket.getSeq();
this.acknowledgeNumber = tcpPacket.getAck();
this.relativeSequenceNumber = tcpPacket.getRelativeSeq();
this.relativeAcknowledgeNumber = tcpPacket.getRelativeAck();
this.dataOffset = tcpPacket.getDataOffset();
this.controlBits = tcpPacket.getFlags();
this.windowSize = tcpPacket.getWindow();
this.checksum = tcpPacket.getChecksum();
this.urgentPointer = tcpPacket.getUrgentPointer();
this.options = tcpPacket.getOptions();
this.padding = tcpPacket.getPadding();
this.tcpLength = tcpPacket.getTotalLength();
this.direction = tcpPacket.getDirection();
this.syn = tcpPacket.isSyn();
this.ack = tcpPacket.isAck();
this.psh = tcpPacket.isPsh();
this.fin = tcpPacket.isFin();
this.rst = tcpPacket.isRst();
this.urg = tcpPacket.isUrg();
this.clientState = tcpSession.getClientState();
this.serverState = tcpSession.getServerState();
this.pcapActivity = source;
this.tcp = new Tcp();
PcapPacket packet = pcapActivity.getPcapPacket();
packet.hasHeader(tcp);
replaceActivity(source);
setExcludedFromOutput(true);
}
public TcpActivity(PcapActivity source, Tcp tcp, IpActivity ip) {
super(source);
this.sourcePort = tcp.source();
this.destinationPort = tcp.destination();
this.sourceAddress = ip.getSourceAddress();
this.sourceSocketAddress = new InetSocketAddress(sourceAddress, sourcePort);
this.destinationAddress = ip.getDestinationAddress();
this.destinationSocketAddress = new InetSocketAddress(destinationAddress, destinationPort);
this.sequenceNumber = tcp.seq();
this.acknowledgeNumber = tcp.ack();
this.relativeSequenceNumber = -1; // TODO: not yet implemented
this.relativeAcknowledgeNumber = -1; //TODO: not yet implemented
this.dataOffset = tcp.getOffset();
this.controlBits = tcp.flags();
this.windowSize = tcp.window();
this.checksum = tcp.checksum();
this.urgentPointer = tcp.urgent();
this.tcpLength = tcp.getLength();
this.options = null; // TODO: not yet implemented
this.padding = null; // TODO: not yet implemented
this.syn = tcp.flags_SYN();
this.ack = tcp.flags_ACK();
this.psh = tcp.flags_PSH();
this.fin = tcp.flags_FIN();
this.rst = tcp.flags_RST();
this.urg = tcp.flags_URG();
this.direction = null; // TODO: not yet implemented
this.clientState = null; // TODO: not yet implemented
this.serverState = null; // TODO: not yet implemented
this.pcapActivity = source;
this.tcp = tcp;
replaceActivity(source);
setExcludedFromOutput(true);
}
/**
* @return the L4 source address information
* @see #getSourceSocketAddress()
*/
public int getSourcePort() {
return sourcePort;
}
/**
* @return the L4 destination address information
* @see #getDestinationSocketAddress()
*/
public int getDestinationPort() {
return destinationPort;
}
/**
* @return the L3 source address information
* @see #getSourceSocketAddress()
*/
public InetAddress getSourceAddress() {
return sourceAddress;
}
/**
* @return the L3/L4 source address information
*/
public InetSocketAddress getSourceSocketAddress() {
return sourceSocketAddress;
}
/**
* @return the L3 destination address information
* @see #getDestinationSocketAddress()
*/
public InetAddress getDestinationAddress() {
return destinationAddress;
}
/**
* @return the L3/L4 destination address information
*/
public InetSocketAddress getDestinationSocketAddress() {
return destinationSocketAddress;
}
/**
* @return the decoded sequence number
*/
public long getSequenceNumber() {
return sequenceNumber;
}
/**
* @return the decoded ACK number
*/
public long getAcknowledgeNumber() {
return acknowledgeNumber;
}
/**
* @return the decoded data offset
*/
public int getDataOffset() {
return dataOffset;
}
/**
* @return the control flags
*/
public int getControlBits() {
return controlBits;
}
/**
* @return the parsed window size
*/
public int getWindowSize() {
return windowSize;
}
/**
* @return the parsed checksum
*/
public int getChecksum() {
return checksum;
}
/**
* @return the parsed urgent pointer value
*/
public int getUrgentPointer() {
return urgentPointer;
}
/**
* @return the content of the TCP length field
*/
public int getTcpLength() {
return tcpLength;
}
/**
* @return the options as array or null (if the JNetPcap constructor was used)
*/
public byte[] getOptions() {
return options;
}
/**
* @return the padding bytes or null (if the JNetPcap constructor was used)
*/
public byte[] getPadding() {
return padding;
}
/**
* @return true, if the syn flag is set
*/
public boolean isSyn() {
return syn;
}
/**
* @return true, if the ack flag is set
*/
public boolean isAck() {
return ack;
}
/**
* @return true, if the psh flag is set
*/
public boolean isPsh() {
return psh;
}
/**
* @return true, if the fin flag is set
*/
public boolean isFin() {
return fin;
}
/**
* @return true, if the rst flag is set
*/
public boolean isRst() {
return rst;
}
/**
* @return true, if the urg flag is set
*/
public boolean isUrg() {
return urg;
}
/**
* @return the direction of this activity, e.g. client->server. may be null if the JNetPcap constructor was used.
*/
public TcpDirection getDirection() {
return direction;
}
/**
* @return the client state. may be null if the JNetPcap constructor was used.
*/
public TcpState getClientState() {
return this.clientState;
}
/**
* @return the server state. may be null if the JNetPcap constructor was used.
*/
public TcpState getServerState() {
return this.serverState;
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
/**
* @return the relative sequence number (calculated by the Kraken decoder, may be -1 if the JNetPcap constructor
* was used)
*/
public int getRelativeSequenceNumber() {
return relativeSequenceNumber;
}
/**
* @return the relative ack number (calculated by the Kraken decoder, may be -1 if the JNetPcap constructor
* was used)
*/
public int getRelativeAcknowledgeNumber() {
return relativeAcknowledgeNumber;
}
/**
* @return the JNetPcap representation of a decoded TCP packet or null, if not available
*/
@JsonIgnore
public Tcp getTcp() {
return tcp;
}
/**
* @return the payload as a String, or null if the payload is not available or empty
*/
public String payloadString() {
byte[] payload = tcp.getPayload();
return payload != null && payload.length > 0 ? new String(payload) : null;
}
/**
* @return a formatted Hex dump of the payload or null if the payload is not available or empty
* @see org.jnetpcap.packet.format.FormatUtils#hexdump(byte[])
*/
public String payloadHexFormattedDump() {
byte[] payload = tcp.getPayload();
return payload != null && payload.length > 0 ? FormatUtils.hexdump(payload) : null;
}
/**
* @return a Hex dump of the payload or null if the payload is not available or empty
* @see org.jnetpcap.packet.format.FormatUtils#hexdump(byte[])
*/
public String payloadHexString() {
byte[] payload = tcp.getPayload();
return payload != null && payload.length > 0 ? FormatUtils.asString(payload) : null;
}
/**
* ENHANCEMENT: Crude check based on the source activity should be replaced by a more detailed comparison
* since the decoders may not be deterministic.
*/
@Override
public boolean equals(Object obj) {
return obj instanceof TcpActivity && ((TcpActivity) obj).getPcapActivity().equals(getPcapActivity());
}
@Override
public String getSourceAddressAsString() {
return String.valueOf(getSourceAddress());
}
@Override
public String getDestinationAddressAsString() {
return String.valueOf(getDestinationAddress());
}
@Override
public String toString() {
return "TcpActivity{" +
"sourcePort=" + sourcePort +
", destinationPort=" + destinationPort +
", sourceAddress=" + sourceAddress +
", destinationAddress=" + destinationAddress +
", sequenceNumber=" + sequenceNumber +
", acknowledgeNumber=" + acknowledgeNumber +
", relativeSequenceNumber=" + relativeSequenceNumber +
", relativeAcknowledgeNumber=" + relativeAcknowledgeNumber +
", dataOffset=" + dataOffset +
", controlBits=" + controlBits +
", windowSize=" + windowSize +
", checksum=" + checksum +
", urgentPointer=" + urgentPointer +
", tcpLength=" + tcpLength +
", options=" + Arrays.toString(options) +
", padding=" + Arrays.toString(padding) +
", syn=" + syn +
", ack=" + ack +
", psh=" + psh +
", fin=" + fin +
", rst=" + rst +
", urg=" + urg +
", direction=" + direction +
", clientState=" + clientState +
", serverState=" + serverState +
", pcapActivity=" + pcapActivity +
", tcp=" + tcp +
", sourceSocketAddress=" + sourceSocketAddress +
", destinationSocketAddress=" + destinationSocketAddress +
'}';
}
}
@@ -0,0 +1,217 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.telnet;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import org.krakenapps.pcap.decoder.telnet.AnsiMode;
import org.krakenapps.pcap.decoder.telnet.TelnetCommand;
import org.krakenapps.pcap.decoder.telnet.TelnetOption;
import java.util.Arrays;
/**
* TODO: partial types -> drools reassembly
*/
public class TelnetActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = -1020544168748948881L;
private final PcapActivity pcapActivity;
private final Type subType;
/**
* (CLIENT|SERVER)_(COMMAND|ANSICONTROL)
*/
private TelnetCommand command;
/**
* (CLIENT|SERVER)_(COMMAND)
*/
private TelnetOption option;
private byte[] data;
/**
* (CLIENT|SERVER)_ANSICONTROL
*/
private AnsiMode ansiMode;
private int[] arguments;
/**
* (CLIENT|SERVER)_DATA
*/
private String text;
/**
* (CLIENT|SERVER)_TITLE
*/
private String title;
/**
* Constructs a prototypical Event.
*
* @param source The object on which the Event initially occurred (will be replaced).
* @param partialActivityType the subtype of this activity (to be reassembled)
* @throws IllegalArgumentException if source is null.
*/
public TelnetActivity(PcapActivity source, Type partialActivityType) {
super(source);
pcapActivity = source;
this.subType = partialActivityType;
replaceActivity(source);
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
/**
* @return the subtype of this activity for reassembly
*/
public Type getSubType() {
return subType;
}
/**
* @return the decoded {@link org.krakenapps.pcap.decoder.telnet.TelnetCommand}
*/
public TelnetCommand getCommand() {
return command;
}
/**
* Set by {@link at.jku.fim.rubanetra.protocol.handler.KrakenTelnetHandler}
* @param command the decoded {@link org.krakenapps.pcap.decoder.telnet.TelnetCommand}
*/
public void setCommand(TelnetCommand command) {
this.command = command;
}
/**
* @return the decoded {@link org.krakenapps.pcap.decoder.telnet.TelnetOption}
*/
public TelnetOption getOption() {
return option;
}
/**
* Set by {@link at.jku.fim.rubanetra.protocol.handler.KrakenTelnetHandler}
* @param option the decoded {@link org.krakenapps.pcap.decoder.telnet.TelnetOption}
*/
public void setOption(TelnetOption option) {
this.option = option;
}
/**
* @return the available telnet data (provided by the decoder)
*/
public byte[] getData() {
return data;
}
/**
* Set by {@link at.jku.fim.rubanetra.protocol.handler.KrakenTelnetHandler}
* @param data the telnet data
*/
public void setData(byte[] data) {
this.data = data;
}
/**
* @return the decoded {@link org.krakenapps.pcap.decoder.telnet.AnsiMode}
*/
public AnsiMode getAnsiMode() {
return ansiMode;
}
/**
* Set by {@link at.jku.fim.rubanetra.protocol.handler.KrakenTelnetHandler}
* @param ansiMode the decoded {@link org.krakenapps.pcap.decoder.telnet.AnsiMode}
*/
public void setAnsiMode(AnsiMode ansiMode) {
this.ansiMode = ansiMode;
}
/**
* @return the available telnet arguments
* @see org.krakenapps.pcap.decoder.telnet.TelnetDecoder
*/
public int[] getArguments() {
return arguments;
}
/**
* Set by {@link at.jku.fim.rubanetra.protocol.handler.KrakenTelnetHandler}
* @param arguments the available telnet arguments
*/
public void setArguments(int[] arguments) {
this.arguments = arguments;
}
/**
* @return the decoded telnet text
*/
public String getText() {
return text;
}
/**
* Set by {@link at.jku.fim.rubanetra.protocol.handler.KrakenTelnetHandler}
* @param text the decoded telnet text
*/
public void setText(String text) {
this.text = text;
}
/**
* @return the decoded title
*/
public String getTitle() {
return title;
}
/**
* Set by {@link at.jku.fim.rubanetra.protocol.handler.KrakenTelnetHandler}
* @param title the decoded title
*/
public void setTitle(String title) {
this.title = title;
}
public enum Type {
CLIENT_COMMAND, SERVER_COMMAND, CLIENT_ANSICONTROL, SERVER_ANSICONTROL,
CLIENT_DATA, SERVER_DATA, CLIENT_TITLE, SERVER_TITLE
}
@Override
public String toString() {
return "TelnetActivity{" +
"pcapActivity=" + pcapActivity +
", subType=" + subType +
", command=" + command +
", option=" + option +
", data=" + Arrays.toString(data) +
", ansiMode=" + ansiMode +
", arguments=" + Arrays.toString(arguments) +
", text='" + text + '\'' +
", title='" + title + '\'' +
'}';
}
}
@@ -0,0 +1,154 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.tls;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import at.jku.fim.rubanetra.protocol.activity.tcp.TcpActivity;
import org.kie.api.definition.type.Role;
import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import static org.kie.api.definition.type.Role.Type;
/**
* Encapsulates various {@link at.jku.fim.rubanetra.protocol.activity.tcp.TcpActivity} objects
* that are specific to SSL/TLS streams, e.g. typical "Client Hello", "Server Hello" and "ChangeCipherSpec"
* messages are stored in this activity - therefore it should be considered a derived fact next to constituting an event.
* {@link at.jku.fim.rubanetra.protocol.activity.tls.TlsActivityHelper} may be used to decide whether
* or not a TCP payload may contain SSL/TLS protocol messages.
*/
@Role(Type.EVENT)
public class TlsActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = -7664259518283613758L;
private final TcpActivity clientHello;
private final TcpActivity serverHello;
private final SortedSet<Long> clientToServerTraffic, serverToClientTraffic;
private TcpActivity changeCipherSpec;
/**
* Constructs a prototypical TLS Event using a "Client Hello" and a "Server Hello" TCP messages.
* Both messages are extended instead of replaced due to potential wrongful classifications.
*/
public TlsActivity(TcpActivity clientHello, TcpActivity serverHello) {
super(clientHello);
this.clientHello = clientHello;
this.serverHello = serverHello;
serverToClientTraffic = new TreeSet<>();
clientToServerTraffic = new TreeSet<>();
extendActivity(clientHello);
extendActivity(serverHello);
}
/**
* @return the tcp activity resembling the change cipher spec protocol message or null if it has not been set
*/
public TcpActivity getChangeCipherSpec() {
return changeCipherSpec;
}
/**
* Designates the passed TCP activity to containing a "Change Cipher Spec" protocol message.
*
* @param changeCipherSpec the TCP activity resembling a "Change Cipher Spec" (will be extended)
*/
public void setChangeCipherSpec(TcpActivity changeCipherSpec) {
if (this.changeCipherSpec != null) {
removeReplacedActivity(this.changeCipherSpec);
}
this.changeCipherSpec = changeCipherSpec;
extendActivity(changeCipherSpec);
}
/**
* @return the "Client Hello" protocol message containing TcpActivity
*/
public TcpActivity getClientHello() {
return clientHello;
}
/**
* @return the "Server Hello" protocol message containing TcpActivity
*/
public TcpActivity getServerHello() {
return serverHello;
}
/**
* @return a sorted set containing individual frame numbers that are part of the TCP session (client -> server)
* ENHANCEMENT: this is quite inefficient, introduction of ranges?
*/
public SortedSet<Long> getClientToServerTraffic() {
return Collections.unmodifiableSortedSet(clientToServerTraffic);
}
/**
* @return a sorted set containing individual frame numbers that are part of the TCP session (server -> client)
* ENHANCEMENT: this is quite inefficient, introduction of ranges?
*/
public SortedSet<Long> getServerToClientTraffic() {
return Collections.unmodifiableSortedSet(serverToClientTraffic);
}
/**
* Assigns and extends a tcp activity to be part of this TLS/SSL stream
*
* @param tcp the tcp activity of the same TCP session, client to server direction
*/
public void addClientToServerTcpActivity(TcpActivity tcp) {
this.clientToServerTraffic.addAll(tcp.getCompoundFrameNumbers());
extendActivity(tcp);
}
/**
* Assigns and extends a tcp activity to be part of this TLS/SSL stream
*
* @param tcp the tcp activity of the same TCP session, server to client direction
*/
public void addServerToClientTcpActivity(TcpActivity tcp) {
this.serverToClientTraffic.addAll(tcp.getCompoundFrameNumbers());
extendActivity(tcp);
}
@Override
public String getSourceAddressAsString() {
return getClientHello().getSourceAddressAsString();
}
@Override
public String getDestinationAddressAsString() {
return getClientHello().getDestinationAddressAsString();
}
@Override
public String toString() {
return "TlsActivity{" +
"clientHello=" + clientHello +
", serverHello=" + serverHello +
", clientToServerTraffic=" + clientToServerTraffic +
", serverToClientTraffic=" + serverToClientTraffic +
", changeCipherSpec=" + changeCipherSpec +
'}';
}
}
@@ -0,0 +1,167 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.tls;
import org.jnetpcap.protocol.tcpip.Tcp;
/**
* Contains various static methods as indicators of TLS/SSL protocol messages in
* {@link at.jku.fim.rubanetra.protocol.activity.tcp.TcpActivity} payloads.
* These methods are basically unverified and marginally tested - therefore false positives/negatives may be
* possible.
*/
public class TlsActivityHelper {
/**
* Returns true if the TCP payload contains a "Handshake" control flag.
*
* @param tcp the JNetPcap TCP representation containing a payload.
* @return true if the TCP payload contains a "Handshake" control flag.
*/
public static boolean isTlsHandshake(Tcp tcp) {
return TlsContentType.HANDSHAKE.matches(tcp, TlsRecord.CONTENT_TYPE)
|| TlsContentType.SSL_HANDSHAKE.matches(tcp, TlsRecord.CONTENT_TYPE);
}
/**
* Returns true if the TCP payload contains a "Client Hello" control message.
*
* @param tcp the JNetPcap TCP representation containing a payload.
* @return true if the TCP payload contains a "Client Hello" control message.
*/
public static boolean isClientHello(Tcp tcp) {
if (TlsContentType.HANDSHAKE.matches(tcp, TlsRecord.CONTENT_TYPE)) {
return TlsContentType.HANDSHAKE_STATE_CLIENT_HELLO.matches(tcp, TlsRecord.HANDSHAKE_STATE);
} else if (TlsContentType.SSL_HANDSHAKE.matches(tcp, TlsRecord.CONTENT_TYPE)) {
return TlsContentType.SSL_HANDSHAKE_STATE_CLIENT_HELLO.matches(tcp, TlsRecord.SSL_HANDSHAKE_STATE);
}
return false;
}
/**
* Returns true if the TCP payload contains a "Server Hello" control message.
*
* @param tcp the JNetPcap TCP representation containing a payload.
* @return true if the TCP payload contains a "Server Hello" control message.
*/
public static boolean isServerHello(Tcp tcp) {
if (TlsContentType.HANDSHAKE.matches(tcp, TlsRecord.CONTENT_TYPE)) {
return TlsContentType.HANDSHAKE_STATE_SERVER_HELLO.matches(tcp, TlsRecord.HANDSHAKE_STATE);
} else if (TlsContentType.SSL_HANDSHAKE.matches(tcp, TlsRecord.CONTENT_TYPE)) {
return TlsContentType.SSL_HANDSHAKE_STATE_SERVER_HELLO.matches(tcp, TlsRecord.SSL_HANDSHAKE_STATE);
}
return false;
}
/**
* Returns true if the TCP payload contains a "ChangeCipherSpec" control message.
*
* @param tcp the JNetPcap TCP representation containing a payload.
* @return true if the TCP payload contains a "ChangeCipherSpec" control message.
*/
public static boolean isChangeCipherSpec(Tcp tcp) {
return TlsContentType.CHANGE_CIPHER_SPEC.matches(tcp, TlsRecord.CONTENT_TYPE);
}
/**
* Returns true if the TCP payload contains an "Application" control message.
*
* @param tcp the JNetPcap TCP representation containing a payload.
* @return true if the TCP payload contains an "Application" control message.
*/
public static boolean isApplication(Tcp tcp) {
return TlsContentType.APPLICATION.matches(tcp, TlsRecord.CONTENT_TYPE);
}
/**
* Enumeration of possible SSL/TLS records including byte offset into the payload and length values.
*/
public enum TlsRecord {
CONTENT_TYPE(0, 1),
VERSION(1, 2),
LENGTH(3, 2),
// for handshake
HANDSHAKE_STATE(5, 1),
SSL_HANDSHAKE_STATE(2, 1);
private final int byteOffset;
private final int length;
TlsRecord(int byteOffset, int length) {
this.byteOffset = byteOffset;
this.length = length;
}
}
/**
* Enumeration of possible SSL/TLS content types including flag values.
*/
public enum TlsContentType {
CHANGE_CIPHER_SPEC(0x14),
ALERT(0x15),
HANDSHAKE(0x16),
APPLICATION(0x17),
// special purpose handshake protocol
HANDSHAKE_STATE_CLIENT_HELLO(0x01),
HANDSHAKE_STATE_SERVER_HELLO(0x02),
//SSL v2 exception
SSL_HANDSHAKE(0x80),
// special purpose handshake protocol
SSL_HANDSHAKE_STATE_CLIENT_HELLO(0x01),
SSL_HANDSHAKE_STATE_SERVER_HELLO(0x02);
private final int val;
TlsContentType(int val) {
this.val = val;
}
/**
* Returns true if the payload of tcp contains this flag in its TlsRecord record.
*
* @param tcp the the JNetPcap TCP representation containing a payload.
* @param record the TLSRecord of interest
* @return true if the payload of tcp contains this flag in its TlsRecord record.
*/
public boolean matches(Tcp tcp, TlsRecord record) {
if (tcp == null || !tcp.hasPayload()) return false;
if (tcp.getPayloadLength() >= record.byteOffset + record.length) {
int off = tcp.getPayloadOffset() + record.byteOffset;
switch (record.length) {
case 1:
return tcp.getPacket().getUByte(off) == val;
case 2:
return tcp.getPacket().getUShort(off) == val;
default:
return false;
}
}
return false;
}
}
}
@@ -0,0 +1,156 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.activity.udp;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.protocol.activity.AbstractReplaceableActivity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.protocol.tcpip.Udp;
import org.kie.api.definition.type.Role;
import org.kie.api.definition.type.Role.Type;
import org.krakenapps.pcap.decoder.udp.UdpPacket;
import java.net.InetSocketAddress;
/**
* Encapsulates various UDP packet specific values (decoded by Kraken) and
* provides a JNetPcap representation of the same UDP data.
*/
@Role(Type.EVENT)
public class UdpActivity extends AbstractReplaceableActivity {
private static final long serialVersionUID = 351329488071596679L;
private final int sourcePort;
private final int destinationPort;
private final int length;
private final int checksum;
private final PcapActivity pcapActivity;
private final Udp udp;
private final InetSocketAddress sourceSocketAddress;
private final InetSocketAddress destinationSocketAddress;
/**
* Constructs a new Udp activity, replaces the source pcap activity on which this event occurred and excludes it
* from the final output due to verbosity.
*
* @param source the pcap activity on which this activity occurred
* @param packet the decoded UDP data (Kraken)
*/
public UdpActivity(PcapActivity source, UdpPacket packet) {
super(source);
sourcePort = packet.getSourcePort();
destinationPort = packet.getDestinationPort();
length = packet.getLength();
checksum = packet.getChecksum();
sourceSocketAddress = packet.getSource();
destinationSocketAddress = packet.getDestination();
pcapActivity = source;
this.udp = new Udp();
PcapPacket p = pcapActivity.getPcapPacket();
p.hasHeader(udp);
replaceActivity(source);
setExcludedFromOutput(true);
}
/**
* @return the JNetPcap representation of the decoded UDP data
*/
@JsonIgnore
public Udp getUdp() {
return udp;
}
/**
* @return the L4 source address information
*/
public int getSourcePort() {
return sourcePort;
}
/**
* @return the L4 destination address information
*/
public int getDestinationPort() {
return destinationPort;
}
/**
* @return the L3/L4 source address
*/
public InetSocketAddress getSourceSocketAddress() {
return sourceSocketAddress;
}
/**
* @return the L3/L4 destination address
*/
public InetSocketAddress getDestinationSocketAddress() {
return destinationSocketAddress;
}
/**
* @return payload length in bytes
*/
public int getLength() {
return length;
}
/**
* @return packet checksum as encountered
*/
public int getChecksum() {
return checksum;
}
/**
* @return the source on which this activity occurred or the final PcapActivity that led to the fully
* decoded packet.
*/
public PcapActivity getPcapActivity() {
return pcapActivity;
}
@Override
public String getSourceAddressAsString() {
return String.valueOf(getSourceSocketAddress());
}
@Override
public String getDestinationAddressAsString() {
return String.valueOf(getDestinationSocketAddress());
}
@Override
public String toString() {
return "UdpActivity{" +
"sourcePort=" + sourcePort +
", destinationPort=" + destinationPort +
", length=" + length +
", checksum=" + checksum +
", pcapActivity=" + pcapActivity +
", udp=" + udp +
", sourceSocketAddress=" + sourceSocketAddress +
", destinationSocketAddress=" + destinationSocketAddress +
'}';
}
}
@@ -0,0 +1,92 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.arp.ArpActivity;
import org.krakenapps.pcap.decoder.arp.ArpDecoder;
import org.krakenapps.pcap.decoder.arp.ArpPacket;
/**
* Represents a link to the Kraken library, i.e. it receives decoded ARP data, wraps it into suitable
* activities ({@link at.jku.fim.rubanetra.protocol.activity.arp.ArpActivity}) and
* passes it on to interested objects.
* The Kraken library expects an Ethernet-Decoder that transfers decoded frames to an
* {@link org.krakenapps.pcap.decoder.arp.ArpDecoder}, therefore this Ethernet decoder must be bound from
* a {@link at.jku.fim.rubanetra.protocol.handler.KrakenEthernetProtocolHandler} to this Arp Decoder
* ({@link #getKrakenArpDecoder()}).
* This class does not provide a binding method implementation, i.e. it is not possible to bind this handler
* to another one.
*/
public class KrakenArpHandler extends KrakenBaseProtocol {
public static final String NEW_ARP_ACTIVITY_PROPERTY_NAME = "newArpActivity";
public static final ProtocolId krakenArpProtocolId = new ProtocolId() {
private final String id = "Arp";
@Override
public String getProtocolId() {
return id;
}
@Override
public String getName() {
return id;
}
@Override
public String toString() {
return getName();
}
};
private final ArpDecoder krakenArpDecoder;
/**
* Constructs a new Arp handler by creating an {@link org.krakenapps.pcap.decoder.arp.ArpDecoder}, a
* {@link at.jku.fim.rubanetra.protocol.handler.KrakenEthernetProtocolHandler} and binding to it.
*/
public KrakenArpHandler() {
super();
krakenArpDecoder = new ArpDecoder();
krakenArpDecoder.register(this::fireNewArpActivity);
}
@Override
public ProtocolId getProtocolId() {
return krakenArpProtocolId;
}
@Override
public void initialize(KrakenProtocolConfiguration setting) {
super.initialize(setting);
}
private void fireNewArpActivity(ArpPacket p) {
ArpActivity act = new ArpActivity(getCurrentPcapActivity(), p);
propertyChangeSupport.firePropertyChange(NEW_ARP_ACTIVITY_PROPERTY_NAME, null, act);
}
public ArpDecoder getKrakenArpDecoder() {
return krakenArpDecoder;
}
}
@@ -0,0 +1,81 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.dhcp.DhcpActivity;
import org.krakenapps.pcap.Protocol;
import org.krakenapps.pcap.decoder.dhcp.DhcpDecoder;
import org.krakenapps.pcap.decoder.dhcp.DhcpMessage;
/**
* Utilizes a {@link org.krakenapps.pcap.decoder.dhcp.DhcpDecoder} in order to receive decoded DHCP data, which
* is in turn distributed as {@link at.jku.fim.rubanetra.protocol.activity.dhcp.DhcpActivity} to
* interested observers.
* This class does not define any concrete bindings.
*/
public class KrakenDhcpHandler extends KrakenBaseProtocol {
public static final String PROPERTY_NEW_DHCP_ACTIVITY = "newDhcpActivity";
private final DhcpDecoder krakenDhcpDecoder;
public KrakenDhcpHandler() {
krakenDhcpDecoder = new DhcpDecoder();
krakenDhcpDecoder.register(this::fireNewDhcpActivity);
}
private void fireNewDhcpActivity(DhcpMessage dhcpMessage) {
DhcpActivity dhcpActivity = new DhcpActivity(getCurrentPcapActivity(), dhcpMessage);
propertyChangeSupport.firePropertyChange(PROPERTY_NEW_DHCP_ACTIVITY, null, dhcpActivity);
}
public static final KrakenApplicationProtocolId krakenDhcpProtocolId = new KrakenApplicationProtocolId() {
private final String protocolId = Protocol.DHCP.name();
@Override
public Protocol getKrakenApplicationProtocol() {
return Protocol.DHCP;
}
@Override
public String getProtocolId() {
return protocolId;
}
@Override
public String getName() {
return "Dhcp";
}
@Override
public String toString() {
return getName();
}
};
@Override
public ProtocolId getProtocolId() {
return krakenDhcpProtocolId;
}
public DhcpDecoder getKrakenDhcpDecoder() {
return krakenDhcpDecoder;
}
}
@@ -0,0 +1,217 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.dns.DnsActivity;
import org.krakenapps.pcap.Protocol;
import org.krakenapps.pcap.decoder.tcp.TcpProcessor;
import org.krakenapps.pcap.decoder.tcp.TcpSessionKey;
import org.krakenapps.pcap.decoder.udp.UdpPacket;
import org.krakenapps.pcap.decoder.udp.UdpProcessor;
import org.krakenapps.pcap.util.Buffer;
import org.krakenapps.pcap.util.ChainBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xbill.DNS.Message;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
/**
* This class does not utilize a Kraken decoder directly, instead it represents the decoding processors as
* an implementation of {@link org.krakenapps.pcap.decoder.udp.UdpProcessor} and
* {@link org.krakenapps.pcap.decoder.tcp.TcpProcessor}.
* The org.xbill.dns Java library (see {@link org.xbill.DNS.Message}) is used as primary payload decoder.
* Several modifications were required to process both, UDP- and TCP-based payloads, therefore this implementation
* is still considered experimental and may (silently) fail to decode valid DNS messages.
*/
public class KrakenDnsProtocolHandler extends KrakenBaseProtocol implements UdpProcessor, TcpProcessor {
public static final String NEW_DNS_ACTIVITY_PROPERTY_NAME = "newDnsActivity";
public static final KrakenApplicationProtocolId krakenDnsProtocolId = new KrakenApplicationProtocolId() {
private final String protocolId = Protocol.DNS.name();
@Override
public Protocol getKrakenApplicationProtocol() {
return Protocol.DNS;
}
@Override
public String getProtocolId() {
return protocolId;
}
@Override
public String getName() {
return "Dns";
}
@Override
public String toString() {
return getName();
}
};
private final Map<TcpSessionKey, DnsSession> sessionMap = new HashMap<>();
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
public ProtocolId getProtocolId() {
return krakenDnsProtocolId;
}
@Override
public void process(UdpPacket p) {
Buffer udpData = p.getData();
int readableBytes = udpData.readableBytes();
byte[] dnsData = new byte[readableBytes];
udpData.gets(dnsData);
try {
Message dnsMessage = new Message(dnsData);
DnsActivity dnsActivity = new DnsActivity(getCurrentPcapActivity(), dnsMessage);
fireNewDnsActivity(dnsActivity);
} catch (IOException e) {
log.debug("Failed to decode UdpPacket {} {}", getCurrentPcapActivity().getFrameNumber(), e);
}
}
private void fireNewDnsActivity(DnsActivity dnsActivity) {
propertyChangeSupport.firePropertyChange(NEW_DNS_ACTIVITY_PROPERTY_NAME, null, dnsActivity);
}
@Override
public void onReset(TcpSessionKey key) {
onFinish(key);
}
@Override
public void onEstablish(TcpSessionKey key) {
// take the valuable IP information and create a new potential DNS session
InetAddress clientIp = key.getClientIp();
InetAddress serverIp = key.getServerIp();
InetSocketAddress clientAddr = new InetSocketAddress(clientIp, key.getClientPort());
InetSocketAddress serverAddr = new InetSocketAddress(serverIp, key.getServerPort());
sessionMap.put(key, new DnsSession(clientAddr, serverAddr));
}
@Override
public void onFinish(TcpSessionKey key) {
sessionMap.remove(key);
}
@Override
public void handleTx(TcpSessionKey session, Buffer data) {
DnsSession dnsSession = sessionMap.get(session);
dnsSession.txBuffer.addLast(data);
if (dnsSession.txMessageSize <= 0) {
// DNS over TCP is prefixed by a two byte length field (see RFC 1035)
dnsSession.txMessageSize = dnsSession.txBuffer.getUnsignedShort();
}
dnsSession.txBuffer.mark();
int readableBytes = dnsSession.txBuffer.readableBytes();
if (readableBytes == dnsSession.txMessageSize) {
byte[] dnsData = new byte[readableBytes];
dnsSession.txBuffer.gets(dnsData);
try {
Message dnsMessage = new Message(dnsData);
DnsActivity dnsActivity = new DnsActivity(getCurrentPcapActivity(), dnsMessage);
fireNewDnsActivity(dnsActivity);
} catch (IOException e) {
log.debug("Failed to decode DNS message over tcp {} {}", getCurrentPcapActivity().getFrameNumber(), e);
dnsSession.txBuffer.reset();
}
}
}
@Override
public void handleRx(TcpSessionKey session, Buffer data) {
DnsSession dnsSession = sessionMap.get(session);
dnsSession.rxBuffer.addLast(data);
if (dnsSession.rxMessageSize <= 0) {
// DNS over TCP is prefixed by a two byte length field (see RFC 1035)
dnsSession.rxMessageSize = dnsSession.rxBuffer.getUnsignedShort();
}
//dnsSession.rxBuffer.skip(2); // skip DNS Message size??
dnsSession.rxBuffer.mark();
int readableBytes = dnsSession.rxBuffer.readableBytes();
if (readableBytes == dnsSession.rxMessageSize) {
byte[] dnsData = new byte[readableBytes];
dnsSession.rxBuffer.gets(dnsData);
try {
Message dnsMessage = new Message(dnsData);
DnsActivity dnsActivity = new DnsActivity(getCurrentPcapActivity(), dnsMessage);
fireNewDnsActivity(dnsActivity);
} catch (IOException e) {
log.debug("Failed to decode DNS message over tcp {} {}", getCurrentPcapActivity().getFrameNumber(), e);
dnsSession.rxBuffer.reset();
}
}
}
/**
* Encapsulates RX/TX buffers for DNS messages exchanged between a client and a server
*/
private class DnsSession {
private final InetSocketAddress client;
private final InetSocketAddress server;
private final ChainBuffer txBuffer;
private final ChainBuffer rxBuffer;
private int txMessageSize, rxMessageSize;
DnsSession(InetSocketAddress client, InetSocketAddress server) {
this.client = client;
this.server = server;
txBuffer = new ChainBuffer();
rxBuffer = new ChainBuffer();
this.txMessageSize = this.rxMessageSize = -1;
}
public InetSocketAddress getClient() {
return client;
}
public InetSocketAddress getServer() {
return server;
}
public ChainBuffer getTxBuffer() {
return txBuffer;
}
public ChainBuffer getRxBuffer() {
return rxBuffer;
}
public int getTxMessageSize() {
return txMessageSize;
}
public int getRxMessageSize() {
return rxMessageSize;
}
}
}
@@ -0,0 +1,149 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.pcap.PcapActivity;
import at.jku.fim.rubanetra.pcap.PcapPacketToKrakenPcapPacketAdapter;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.ethernet.EthernetActivity;
import org.krakenapps.pcap.decoder.ethernet.EthernetDecoder;
import org.krakenapps.pcap.decoder.ethernet.EthernetFrame;
import org.krakenapps.pcap.decoder.ethernet.EthernetType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Kraken network layer 2 decoder {@link org.krakenapps.pcap.decoder.ethernet.EthernetDecoder} is used by this
* handler in order to create and deliver {@link at.jku.fim.rubanetra.protocol.activity.ethernet.EthernetActivity}
* instances. Furthermore, several bindings have been implemented, i.e.:
* <ul>
* <li>-> {@link at.jku.fim.rubanetra.protocol.handler.KrakenIpv4Handler}</li>
* <li>-> {@link at.jku.fim.rubanetra.protocol.handler.KrakenIpv6Handler}</li>
* <li>-> {@link at.jku.fim.rubanetra.protocol.handler.KrakenArpHandler}</li>
* </ul>
*/
public class KrakenEthernetProtocolHandler extends KrakenBaseProtocol {
public static final ProtocolId krakenEthernetProtocolId = new ProtocolId() {
private final String protocolId = "Ethernet";
@Override
public String getProtocolId() {
return protocolId;
}
@Override
public String getName() {
return protocolId;
}
@Override
public String toString() {
return getName();
}
};
public static final String PROPERTY_NEW_ETHERNET_ACTIVITY = "newEthernetActivity";
private final EthernetDecoder krakenEthernetDecoder;
private final Logger log = LoggerFactory.getLogger(getClass());
public KrakenEthernetProtocolHandler() {
super();
krakenEthernetDecoder = new EthernetDecoder();
krakenEthernetDecoder.register(this::fireNewEthernetActivity);
}
@Override
public ProtocolId getProtocolId() {
return krakenEthernetProtocolId;
}
@Override
public void initialize(KrakenProtocolConfiguration setting) {
super.initialize(setting);
getPcapHandler().addPcapProcessor(this::decodePcapPacket);
}
private void fireNewEthernetActivity(EthernetFrame frame) {
EthernetActivity ethernetActivity = new EthernetActivity(getCurrentPcapActivity(), frame);
propertyChangeSupport.firePropertyChange(PROPERTY_NEW_ETHERNET_ACTIVITY, null, ethernetActivity);
}
/**
* Bind the internal {@link org.krakenapps.pcap.decoder.ethernet.EthernetDecoder} to an IPv4 decoder, i.e.
* a decoder that shall receive all decoded Ethernet frames.
*
* @param ipv4Protocol the IPv4 decoder to bind to
* @throws java.lang.IllegalArgumentException if the parameter is a null pointer
*/
public void bind(KrakenIpv4Handler ipv4Protocol) {
if (ipv4Protocol == null) {
throw new IllegalArgumentException();
}
krakenEthernetDecoder.register(EthernetType.IPV4, ipv4Protocol.getKrakenIpv4Decoder());
}
/**
* Bind the internal {@link org.krakenapps.pcap.decoder.ethernet.EthernetDecoder} to an IPv6 decoder, i.e.
* a decoder that shall receive all decoded Ethernet frames.
*
* @param ipv6Handler the IPv6 decoder to bind to
* @throws java.lang.IllegalArgumentException if the parameter is a null pointer
*/
public void bind(KrakenIpv6Handler ipv6Handler) {
krakenEthernetDecoder.register(EthernetType.IPV6, ipv6Handler.getKrakenIpv6Decoder());
}
/**
* Bind the internal {@link org.krakenapps.pcap.decoder.ethernet.EthernetDecoder} to an ARP decoder, i.e.
* a decoder that shall receive all decoded Ethernet frames.
*
* @param arpHandler the ARP decoder to bind to
* @throws java.lang.IllegalArgumentException if the parameter is a null pointer
*/
public void bind(KrakenArpHandler arpHandler) {
krakenEthernetDecoder.register(EthernetType.ARP, arpHandler.getKrakenArpDecoder());
}
/**
* Takes a Pcap entry and tries to decode the payload as an Ethernet frame using a
* {@link org.krakenapps.pcap.decoder.ethernet.EthernetDecoder} and a suitable Pcap wrapper
* {@link at.jku.fim.rubanetra.pcap.PcapPacketToKrakenPcapPacketAdapter}.
*
* @param pcapActivity the Pcap entry whose payload is to be decoded as an Ethernet frame
* @throws java.lang.NullPointerException if pcapActivity is a null pointer
*/
public void decodePcapPacket(PcapActivity pcapActivity) {
if (pcapActivity != null && pcapActivity.getPcapPacket() != null) {
krakenEthernetDecoder.decode(new PcapPacketToKrakenPcapPacketAdapter(pcapActivity.getPcapPacket()));
} else {
log.warn("Unable to decode a null pointer Pcap entry");
throw new NullPointerException();
}
}
/**
* @return the Ethernet decoder used internally
*/
public EthernetDecoder getKrakenEthernetDecoder() {
return krakenEthernetDecoder;
}
}
@@ -0,0 +1,108 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.ftp.FtpActivity;
import org.krakenapps.pcap.Protocol;
import org.krakenapps.pcap.decoder.ftp.FtpDecoder;
import org.krakenapps.pcap.decoder.ftp.FtpProcessor;
import org.krakenapps.pcap.decoder.tcp.TcpPortProtocolMapper;
import java.io.InputStream;
/**
* This is an Kraken application layer decoder handler, i.e. it does not provide any forward bindings.
* Internally, a {@link org.krakenapps.pcap.decoder.ftp.FtpDecoder} is used to build partial
* {@link at.jku.fim.rubanetra.protocol.activity.ftp.FtpActivity} instances.
*/
public class KrakenFtpHandler extends KrakenBaseProtocol {
public static final String PROPERTY_NEW_PARTIAL_FTP_ACTIVITY = "newPartialFtpActivity";
public static final KrakenApplicationProtocolId krakenFtpProtocolId = new KrakenApplicationProtocolId() {
private final String protocolId = Protocol.FTP.name();
@Override
public Protocol getKrakenApplicationProtocol() {
return Protocol.FTP;
}
@Override
public String getProtocolId() {
return protocolId;
}
@Override
public String getName() {
return "Ftp";
}
@Override
public String toString() {
return getName();
}
};
private final FtpDecoder krakenFtpDecoder;
private final FtpProcessor ftpProcessor = new FtpProcessor() {
@Override
public void onCommand(String command) {
FtpActivity activity = new FtpActivity(getCurrentPcapActivity(), FtpActivity.Type.COMMAND, command);
fireNewPartialFtpActivity(activity);
}
@Override
public void onReply(String reply) {
FtpActivity activity = new FtpActivity(getCurrentPcapActivity(), FtpActivity.Type.REPLY, reply);
fireNewPartialFtpActivity(activity);
}
@Override
public void viewList(byte[] list) {
FtpActivity activity = new FtpActivity(getCurrentPcapActivity(), FtpActivity.Type.VIEW_LIST, list);
fireNewPartialFtpActivity(activity);
}
@Override
public void onExtractFile(InputStream is, String fileName) {
}
};
public KrakenFtpHandler() {
krakenFtpDecoder = new FtpDecoder(new TcpPortProtocolMapper());
krakenFtpDecoder.register(ftpProcessor);
}
private void fireNewPartialFtpActivity(FtpActivity partialFtpActivity) {
propertyChangeSupport.firePropertyChange(PROPERTY_NEW_PARTIAL_FTP_ACTIVITY, null, partialFtpActivity);
}
@Override
public ProtocolId getProtocolId() {
return krakenFtpProtocolId;
}
/**
* @return the FTP decoder that is used internally
*/
public FtpDecoder getKrakenFtpDecoder() {
return krakenFtpDecoder;
}
}
@@ -0,0 +1,109 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.http.HttpRequestActivity;
import at.jku.fim.rubanetra.protocol.activity.http.HttpResponseActivity;
import org.krakenapps.pcap.Protocol;
import org.krakenapps.pcap.decoder.http.HttpDecoder;
import org.krakenapps.pcap.decoder.http.HttpProcessor;
import org.krakenapps.pcap.decoder.http.HttpRequest;
import org.krakenapps.pcap.decoder.http.HttpResponse;
import org.krakenapps.pcap.util.Buffer;
/**
* Represents an application layer Kraken decoder handler, i.e. it does not provide any forward bindings.
* A {@link org.krakenapps.pcap.decoder.http.HttpDecoder} is used internally to decode HTTP data which is used to
* build {@link at.jku.fim.rubanetra.protocol.activity.http.HttpRequestActivity} and
* {@link at.jku.fim.rubanetra.protocol.activity.http.HttpResponseActivity} objects.
* However, it does not handle Multipart data (the buffers will be silently discarded).
*/
public class KrakenHttpHandler extends KrakenBaseProtocol {
public static final String NEW_HTTP_REQUEST_ACTIVITY_PROPERTY_NAME = "newHttpRequestActivity";
public static final String NEW_HTTP_RESPONSE_ACTIVITY_PROPERTY_NAME = "newHttpResponseActivity";
public static final KrakenApplicationProtocolId krakenHttpProtocolId = new KrakenApplicationProtocolId() {
private final String protocolIdString = Protocol.HTTP.name();
@Override
public Protocol getKrakenApplicationProtocol() {
return Protocol.HTTP;
}
@Override
public String getProtocolId() {
return protocolIdString;
}
@Override
public String getName() {
return "Http";
}
@Override
public String toString() {
return getName();
}
};
private final HttpDecoder krakenHttpDecoder;
public KrakenHttpHandler() {
krakenHttpDecoder = new HttpDecoder();
krakenHttpDecoder.register(new HttpProcessor() {
@Override
public void onResponse(HttpRequest req, HttpResponse resp) {
fireNewHttpResponseActivity(resp);
}
@Override
public void onRequest(HttpRequest req) {
fireNewHttpRequestActivity(req);
}
@Override
public void onMultipartData(Buffer buffer) {
}
});
}
protected void fireNewHttpRequestActivity(HttpRequest req) {
HttpRequestActivity requestActivity = new HttpRequestActivity(getCurrentPcapActivity(), req);
propertyChangeSupport.firePropertyChange(NEW_HTTP_REQUEST_ACTIVITY_PROPERTY_NAME, null, requestActivity);
}
protected void fireNewHttpResponseActivity(HttpResponse resp) {
HttpResponseActivity responseActivity = new HttpResponseActivity(getCurrentPcapActivity(), resp);
propertyChangeSupport.firePropertyChange(NEW_HTTP_RESPONSE_ACTIVITY_PROPERTY_NAME, null, responseActivity);
}
@Override
public ProtocolId getProtocolId() {
return krakenHttpProtocolId;
}
/**
* @return the HTTP decoder that is used internally
*/
public HttpDecoder getKrakenHttpDecoder() {
return krakenHttpDecoder;
}
}
@@ -0,0 +1,97 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.icmp.Icmpv4Activity;
import org.krakenapps.pcap.decoder.icmp.IcmpDecoder;
import org.krakenapps.pcap.decoder.icmp.IcmpPacket;
import org.krakenapps.pcap.decoder.icmp.IcmpProcessor;
/**
* This ICMP decoder handler relies on {@link org.krakenapps.pcap.decoder.icmp.IcmpDecoder} which is provided by the
* Kraken library and on {@link at.jku.fim.rubanetra.protocol.handler.KrakenIpv4Handler} in order to
* receive the IPv4 payload used to transport the ICMP data.
* No forward bindings are provided by this class.
*/
public class KrakenIcmpv4Handler extends KrakenBaseProtocol {
public static final String NEW_ICMPV_4_ACTIVITY_PROPERTY_NAME = "newIcmpv4Activity";
public static final ProtocolId krakenIcmpv4ProtocolId = new ProtocolId() {
private final String protocolId = "Icmpv4";
@Override
public String getProtocolId() {
return protocolId;
}
@Override
public String getName() {
return protocolId;
}
@Override
public String toString() {
return getName();
}
};
private final IcmpProcessor generalIcmpProcessor = new IcmpProcessor() {
private long lastProcessedFrameNumber = -1;
@Override
public void process(IcmpPacket p) {
// this is a workaround to prevent the redundant call from Kraken's IcmpDecoder of the same event using
// two different objects
if (lastProcessedFrameNumber != getCurrentPcapActivity().getFrameNumber()) {
fireNewIcmpActivity(p);
}
lastProcessedFrameNumber = getCurrentPcapActivity().getFrameNumber();
}
};
private final IcmpDecoder krakenIcmpv4Decoder;
public KrakenIcmpv4Handler() {
krakenIcmpv4Decoder = new IcmpDecoder();
krakenIcmpv4Decoder.register(generalIcmpProcessor);
}
@Override
public ProtocolId getProtocolId() {
return krakenIcmpv4ProtocolId;
}
@Override
public void initialize(KrakenProtocolConfiguration setting) {
super.initialize(setting);
}
protected void fireNewIcmpActivity(IcmpPacket p) {
Icmpv4Activity icmpActivity = new Icmpv4Activity(getCurrentPcapActivity(), p);
propertyChangeSupport.firePropertyChange(NEW_ICMPV_4_ACTIVITY_PROPERTY_NAME, null, icmpActivity);
}
/**
* @return the ICMP decoder that is used internally
*/
public IcmpDecoder getKrakenIcmpv4Decoder() {
return krakenIcmpv4Decoder;
}
}
@@ -0,0 +1,82 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.icmp.Icmpv6Activity;
import org.krakenapps.pcap.decoder.icmpv6.Icmpv6Decoder;
import org.krakenapps.pcap.decoder.icmpv6.Icmpv6Packet;
/**
* Handles decoded ICMPv6 data received from a {@link org.krakenapps.pcap.decoder.icmpv6.Icmpv6Decoder} in order to
* build {@link at.jku.fim.rubanetra.protocol.activity.icmp.Icmpv6Activity} objects.
* This class does not provide any forward bindings.
*/
public class KrakenIcmpv6Handler extends KrakenBaseProtocol {
public static final String NEW_ICMPV_6_ACTIVITY_PROPERTY_NAME = "newIcmpv6Activity";
public static final ProtocolId krakenIcmpv6ProtocolId = new ProtocolId() {
private final String protocolId = "Icmpv6";
@Override
public String getProtocolId() {
return protocolId;
}
@Override
public String getName() {
return protocolId;
}
@Override
public String toString() {
return getName();
}
};
private final Icmpv6Decoder krakenImpv6Decoder;
public KrakenIcmpv6Handler() {
krakenImpv6Decoder = new Icmpv6Decoder();
krakenImpv6Decoder.register(this::fireNewIcmpActivity);
}
@Override
public ProtocolId getProtocolId() {
return krakenIcmpv6ProtocolId;
}
@Override
public void initialize(KrakenProtocolConfiguration setting) {
super.initialize(setting);
}
protected void fireNewIcmpActivity(Icmpv6Packet p) {
Icmpv6Activity icmpActivity = new Icmpv6Activity(getCurrentPcapActivity(), p);
propertyChangeSupport.firePropertyChange(NEW_ICMPV_6_ACTIVITY_PROPERTY_NAME, null, icmpActivity);
}
/**
* @return the ICMPv6 decoder that is used internally
*/
public Icmpv6Decoder getKrakenImpv6Decoder() {
return krakenImpv6Decoder;
}
}
@@ -0,0 +1,135 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.ip.Ipv4Activity;
import org.krakenapps.pcap.decoder.ethernet.EthernetType;
import org.krakenapps.pcap.decoder.ip.InternetProtocol;
import org.krakenapps.pcap.decoder.ip.IpDecoder;
import org.krakenapps.pcap.decoder.ip.IpProcessor;
import org.krakenapps.pcap.decoder.ip.Ipv4Packet;
/**
* This is an inefficient handler for Kraken's {@link org.krakenapps.pcap.decoder.ip.IpDecoder} implementation.
* Since the utilized IP next protocol numbers are not known a priori this class registers all numbers in the
* range [0x01;0xFF[ during initialization.
* Forward bindings are provided for:
* <ul>
* <li>{@link at.jku.fim.rubanetra.protocol.handler.KrakenUdpHandler}</li>
* <li>{@link at.jku.fim.rubanetra.protocol.handler.KrakenTcpHandler}</li>
* <li>{@link at.jku.fim.rubanetra.protocol.handler.KrakenIcmpv4Handler}</li>
* </ul>
*/
public class KrakenIpv4Handler extends KrakenBaseProtocol {
public static final String NEW_IPV_4_ACTIVITY_PROPERTY_NAME = "newIpv4Activity";
public static final ProtocolId krakenIpv4ProtocolId = new ProtocolId() {
private final String protocolId = "Ipv4";
@Override
public String getProtocolId() {
return protocolId;
}
@Override
public String getName() {
return protocolId;
}
@Override
public String toString() {
return getName();
}
};
private final IpProcessor generalIpv4Processor = this::fireNewIpv4Activity;
private final IpDecoder krakenIpv4Decoder;
public KrakenIpv4Handler() {
krakenIpv4Decoder = new IpDecoder();
}
@Override
public ProtocolId getProtocolId() {
return krakenIpv4ProtocolId;
}
@Override
public void initialize(KrakenProtocolConfiguration setting) {
super.initialize(setting);
// the Kraken IPv4 parser always calls back protocol id 0
krakenIpv4Decoder.register(0, generalIpv4Processor);
}
private void fireNewIpv4Activity(Ipv4Packet packet) {
Ipv4Activity ipActivity = new Ipv4Activity(getCurrentPcapActivity(), packet);
propertyChangeSupport.firePropertyChange(NEW_IPV_4_ACTIVITY_PROPERTY_NAME, null, ipActivity);
}
/**
* An experimental backward binding, i.e. it registers ethernetProtocol Kraken decoder to this IPv4 decoder
*
* @param ethernetProtocol the Ethernet decoder to bind to this IPv4 decoder
*/
public void bind(KrakenEthernetProtocolHandler ethernetProtocol) {
ethernetProtocol.getKrakenEthernetDecoder().register(EthernetType.IPV4, krakenIpv4Decoder);
}
/**
* Forward binding to the tcpProtocol internal TCP decoder, i.e. all decoded IPv4 packets are forwarded to the
* TCP handler.
*
* @param tcpProtocol the handler for the decoded IPv4 packets
*/
public void bind(KrakenTcpHandler tcpProtocol) {
krakenIpv4Decoder.register(InternetProtocol.TCP, tcpProtocol.getKrakenTcpDecoder());
}
/**
* Forward binding to the udpProtocol internal UDP decoder, i.e. all decoded IPv4 packets are forwarded to this
* UDP handler.
*
* @param udpProtocol the handler for the decoded IPv4 packets
*/
public void bind(KrakenUdpHandler udpProtocol) {
krakenIpv4Decoder.register(InternetProtocol.UDP, udpProtocol.getKrakenUdpDecoder());
}
/**
* Bind the internal {@link org.krakenapps.pcap.decoder.ip.IpDecoder} to an ICMPv4 decoder, i.e.
* a decoder that shall receive all decoded IP packets.
*
* @param icmpv4Handler the ICMPv4 decoder to bind to
* @throws java.lang.IllegalArgumentException if the parameter is a null pointer
*/
public void bind(KrakenIcmpv4Handler icmpv4Handler) {
krakenIpv4Decoder.register(InternetProtocol.ICMP, icmpv4Handler.getKrakenIcmpv4Decoder());
}
/**
* @return the internal IPv4 decoder
*/
public IpDecoder getKrakenIpv4Decoder() {
return krakenIpv4Decoder;
}
}
@@ -0,0 +1,132 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.ip.Ipv6Activity;
import org.krakenapps.pcap.decoder.ethernet.EthernetType;
import org.krakenapps.pcap.decoder.ip.InternetProtocol;
import org.krakenapps.pcap.decoder.ipv6.Ipv6Decoder;
import org.krakenapps.pcap.decoder.ipv6.Ipv6Packet;
import org.krakenapps.pcap.decoder.ipv6.Ipv6Processor;
/**
* Handles IPv6 packets decoded by {@link org.krakenapps.pcap.decoder.ipv6.Ipv6Decoder} and builds
* {@link at.jku.fim.rubanetra.protocol.activity.ip.Ipv6Activity} objects during the process.
* Since the protocol numbers for the next protocol decoder may be arbitrary it registers to all protocol numbers in the
* range [0x00;0xFF[ using the same processor.
* Forward bindings are provided for:
* <ul>
* <li>{@link at.jku.fim.rubanetra.protocol.handler.KrakenTcpHandler}</li>
* <li>{@link at.jku.fim.rubanetra.protocol.handler.KrakenUdpHandler}</li>
* <li>{@link at.jku.fim.rubanetra.protocol.handler.KrakenIcmpv6Handler}</li>
* </ul>
*/
public class KrakenIpv6Handler extends KrakenBaseProtocol {
public static final String NEW_IPV_6_ACTIVITY_PROPERTY_NAME = "newIpv6Activity";
public static final ProtocolId krakenIpv6ProtocolId = new ProtocolId() {
private final String protocolId = "Ipv6";
@Override
public String getProtocolId() {
return protocolId;
}
@Override
public String getName() {
return protocolId;
}
@Override
public String toString() {
return getName();
}
};
private final Ipv6Processor krakenIpv6Processor = this::fireNewIpv6Activity;
private final Ipv6Decoder krakenIpv6Decoder;
public KrakenIpv6Handler() {
krakenIpv6Decoder = new Ipv6Decoder();
}
@Override
public ProtocolId getProtocolId() {
return krakenIpv6ProtocolId;
}
@Override
public void initialize(KrakenProtocolConfiguration setting) {
super.initialize(setting);
for (byte protocolNumber = Byte.MIN_VALUE; protocolNumber < Byte.MAX_VALUE; protocolNumber++) {
krakenIpv6Decoder.register(protocolNumber, krakenIpv6Processor);
}
}
private void fireNewIpv6Activity(Ipv6Packet packet) {
Ipv6Activity ipActivity = new Ipv6Activity(getCurrentPcapActivity(), packet);
propertyChangeSupport.firePropertyChange(NEW_IPV_6_ACTIVITY_PROPERTY_NAME, null, ipActivity);
}
/**
* Experimental backward binding using an Ethernet decoder that is to be bound to this IPv6 decoder.
*
* @param ethernetProtocol the Ethernet decoder to bind to this IPv6 decoder
*/
public void bind(KrakenEthernetProtocolHandler ethernetProtocol) {
ethernetProtocol.getKrakenEthernetDecoder().register(EthernetType.IPV6, krakenIpv6Decoder);
}
/**
* Forward binding from this IPv6 decoder to a TCP protocol decoder.
*
* @param tcpProtocol the TCP decoder to receive the decoded IPv6 packets
*/
public void bind(KrakenTcpHandler tcpProtocol) {
krakenIpv6Decoder.register(InternetProtocol.TCP, tcpProtocol.getKrakenTcpDecoder());
}
/**
* Forward binding from this IPv6 decoder to an UDP decoder.
*
* @param udpHandler the UDP decoder to receive the decoded IPv6 packets
*/
public void bind(KrakenUdpHandler udpHandler) {
krakenIpv6Decoder.register(InternetProtocol.UDP, udpHandler.getKrakenUdpDecoder());
}
/**
* Bind the internal {@link org.krakenapps.pcap.decoder.ipv6.Ipv6Decoder} to an ICMPv6 decoder, i.e.
* a decoder that shall receive all decoded IP packets.
*
* @param icmpv6Handler the ICMPv6 decoder to bind to
* @throws java.lang.IllegalArgumentException if the parameter is a null pointer
*/
public void bind(KrakenIcmpv6Handler icmpv6Handler) {
krakenIpv6Decoder.register(InternetProtocol.ICMPV6, icmpv6Handler.getKrakenImpv6Decoder());
}
/**
* @return Internally used IPv6 decoder
*/
public Ipv6Decoder getKrakenIpv6Decoder() {
return krakenIpv6Decoder;
}
}
@@ -0,0 +1,87 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.msn.MsnActivity;
import at.jku.fim.rubanetra.protocol.mapper.KrakenTcpProtocolMapperStrategy;
import at.jku.fim.rubanetra.protocol.mapper.KrakenTransportLayerMappingFactory;
import org.krakenapps.pcap.Protocol;
import org.krakenapps.pcap.decoder.msn.MsnDecoder;
/**
* Handles Kraken's {@link org.krakenapps.pcap.decoder.msn.MsnDecoder} output
* and creates suitable {@link at.jku.fim.rubanetra.protocol.activity.msn.MsnActivity} objects
* during the process.
* This class does not provide any bindings.
*/
public class KrakenMsnHandler extends KrakenBaseProtocol {
public static final String NEW_MSN_ACTIVITY_PROPERTY_NAME = "newMsnActivity";
public static final KrakenApplicationProtocolId krakenMsnProtocolId = new KrakenApplicationProtocolId() {
private final String protocolId = Protocol.MSN.name();
@Override
public Protocol getKrakenApplicationProtocol() {
return Protocol.MSN;
}
@Override
public String getProtocolId() {
return protocolId;
}
@Override
public String getName() {
return "Msn";
}
@Override
public String toString() {
return getName();
}
};
private final KrakenTransportLayerMappingFactory mappingFactory = new KrakenTransportLayerMappingFactory();
private KrakenTcpProtocolMapperStrategy tcpMappingStrategy;
private MsnDecoder krakenMsnDecoder;
public KrakenMsnHandler() {
krakenMsnDecoder = new MsnDecoder();
krakenMsnDecoder.register(this::fireNewMsnActivity);
}
private void fireNewMsnActivity(String account, String chat) {
MsnActivity msnAct = new MsnActivity(getCurrentPcapActivity(), account, chat);
propertyChangeSupport.firePropertyChange(NEW_MSN_ACTIVITY_PROPERTY_NAME, null, msnAct);
}
@Override
public ProtocolId getProtocolId() {
return krakenMsnProtocolId;
}
/**
* @return the internally used MSN decoder
*/
public MsnDecoder getKrakenMsnDecoder() {
return krakenMsnDecoder;
}
}
@@ -0,0 +1,82 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.netbios.NetbiosActivity;
import org.krakenapps.pcap.Protocol;
import org.krakenapps.pcap.decoder.netbios.NetBiosDecoder;
/**
* This class tries to handle various Netbios events dispatched by the internally
* used {@link org.krakenapps.pcap.decoder.netbios.NetBiosDecoder}.
* Appropriate {@link at.jku.fim.rubanetra.protocol.activity.netbios.NetbiosActivity}
* objects are constructed for those events, however, be aware that this class
* has not yet been tested at all.
* This class does not provide any bindings.
*/
public class KrakenNetbiosHandler extends KrakenBaseProtocol {
public static final KrakenApplicationProtocolId krakenNetbiosProtocolId = new KrakenApplicationProtocolId() {
@Override
public Protocol getKrakenApplicationProtocol() {
return Protocol.NETBIOS;
}
@Override
public String getProtocolId() {
return Protocol.NETBIOS.name();
}
@Override
public String getName() {
return "Netbios";
}
};
private final NetBiosDecoder krakenNetbiosDecoder;
public KrakenNetbiosHandler() {
krakenNetbiosDecoder = new NetBiosDecoder();
krakenNetbiosDecoder.registerDatagramProcessor(netBiosDatagramPacket -> {
NetbiosActivity act = new NetbiosActivity(getCurrentPcapActivity(), netBiosDatagramPacket);
fireNewNetbiosActivity(act);
});
krakenNetbiosDecoder.registerNameProcessor(netBiosNamePacket -> {
NetbiosActivity act = new NetbiosActivity(getCurrentPcapActivity(), netBiosNamePacket);
fireNewNetbiosActivity(act);
});
}
private void fireNewNetbiosActivity(NetbiosActivity act) {
propertyChangeSupport.firePropertyChange("newNetbiosActivity", null, act);
}
@Override
public ProtocolId getProtocolId() {
return krakenNetbiosProtocolId;
}
/**
* @return the internally used NetBios decoder
*/
public NetBiosDecoder getKrakenNetbiosDecoder() {
return krakenNetbiosDecoder;
}
}
@@ -0,0 +1,98 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.pop3.Pop3Activity;
import org.krakenapps.mime.MimeHeader;
import org.krakenapps.pcap.Protocol;
import org.krakenapps.pcap.decoder.pop3.Pop3Data;
import org.krakenapps.pcap.decoder.pop3.Pop3Decoder;
import org.krakenapps.pcap.decoder.pop3.Pop3Processor;
/**
* This class utilizes a {@link org.krakenapps.pcap.decoder.pop3.Pop3Decoder} in order
* to dispatch {@link at.jku.fim.rubanetra.protocol.activity.pop3.Pop3Activity} objects
* for decoded events.
* It does not provide any bindings.
*/
public class KrakenPop3Handler extends KrakenBaseProtocol {
public static final KrakenApplicationProtocolId krakenPop3ProtocolId = new KrakenApplicationProtocolId() {
@Override
public Protocol getKrakenApplicationProtocol() {
return Protocol.POP3;
}
@Override
public String getProtocolId() {
return Protocol.POP3.name();
}
@Override
public String getName() {
return "Pop3";
}
};
private final Pop3Decoder krakenPop3Decoder;
public KrakenPop3Handler() {
krakenPop3Decoder = new Pop3Decoder();
krakenPop3Decoder.register(new Pop3Processor() {
@Override
public void onReceive(MimeHeader header, Pop3Data data) {
Pop3Activity act = new Pop3Activity(getCurrentPcapActivity(), Pop3Activity.Type.RECEIVE);
act.setHeader(header);
act.setData(data);
fireNewPartialPop3Activity(act);
}
@Override
public void onCommand(String command) {
Pop3Activity act = new Pop3Activity(getCurrentPcapActivity(), Pop3Activity.Type.COMMAND);
act.setCommand(command);
fireNewPartialPop3Activity(act);
}
@Override
public void onResponse(String response) {
Pop3Activity act = new Pop3Activity(getCurrentPcapActivity(), Pop3Activity.Type.RESPONSE);
act.setResponse(response);
fireNewPartialPop3Activity(act);
}
});
}
private void fireNewPartialPop3Activity(Pop3Activity act) {
propertyChangeSupport.firePropertyChange("newPartialPop3Activity", null, act);
}
@Override
public ProtocolId getProtocolId() {
return krakenPop3ProtocolId;
}
/**
* @return the internally used Pop3 decoder
*/
public Pop3Decoder getKrakenPop3Decoder() {
return krakenPop3Decoder;
}
}
@@ -0,0 +1,106 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.smtp.SmtpActivity;
import at.jku.fim.rubanetra.protocol.activity.smtp.SmtpCommandActivity;
import at.jku.fim.rubanetra.protocol.activity.smtp.SmtpReplyActivity;
import at.jku.fim.rubanetra.protocol.activity.smtp.SmtpSendActivity;
import org.krakenapps.mime.MimeHeader;
import org.krakenapps.pcap.Protocol;
import org.krakenapps.pcap.decoder.smtp.SmtpData;
import org.krakenapps.pcap.decoder.smtp.SmtpDecoder;
import org.krakenapps.pcap.decoder.smtp.SmtpProcessor;
/**
* Uses a {@link org.krakenapps.pcap.decoder.smtp.SmtpDecoder} to receive decoded
* SMTP data that is wrapped into appropriate {@link at.jku.fim.rubanetra.protocol.activity.smtp.SmtpActivity}
* extension instances.
* This class does not provide any bindings.
*/
public class KrakenSmtpHandler extends KrakenBaseProtocol {
public static final String NEW_SMTP_SEND_ACTIVITY_PROPERTY_NAME = "newSmtpSendActivity";
public static final String NEW_SMTP_COMMAND_ACTIVITY_PROPERTY_NAME = "newSmtpCommandActivity";
public static final String NEW_SMTP_REPLY_ACTIVITY_PROPERTY_NAME = "newSmtpReplyActivity";
public static final KrakenApplicationProtocolId krakenSmtpProtocolId = new KrakenApplicationProtocolId() {
private final String protocolId = Protocol.SMTP.name();
@Override
public Protocol getKrakenApplicationProtocol() {
return Protocol.SMTP;
}
@Override
public String getProtocolId() {
return protocolId;
}
@Override
public String getName() {
return "Smtp";
}
@Override
public String toString() {
return getName();
}
};
private final SmtpDecoder krakenSmtpDecoder;
public KrakenSmtpHandler() {
krakenSmtpDecoder = new SmtpDecoder();
krakenSmtpDecoder.register(new SmtpProcessor() {
@Override
public void onSend(MimeHeader header, SmtpData data) {
SmtpActivity act = new SmtpSendActivity(getCurrentPcapActivity(), header, data);
propertyChangeSupport.firePropertyChange(NEW_SMTP_SEND_ACTIVITY_PROPERTY_NAME, null, act);
}
@Override
public void onCommand(String command, String parameter) {
SmtpActivity act = new SmtpCommandActivity(getCurrentPcapActivity(), command, parameter);
propertyChangeSupport.firePropertyChange(NEW_SMTP_COMMAND_ACTIVITY_PROPERTY_NAME, null, act);
}
@Override
public void onReply(int code, String message) {
SmtpActivity act = new SmtpReplyActivity(getCurrentPcapActivity(), code, message);
propertyChangeSupport.firePropertyChange(NEW_SMTP_REPLY_ACTIVITY_PROPERTY_NAME, null, act);
}
});
}
@Override
public ProtocolId getProtocolId() {
return krakenSmtpProtocolId;
}
/**
* @return the internally used SMTP decoder
*/
public SmtpDecoder getKrakenSmtpDecoder() {
return krakenSmtpDecoder;
}
}
@@ -0,0 +1,85 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.snmp.Snmpv1Activity;
import at.jku.fim.rubanetra.protocol.activity.snmp.Snmpv2Activity;
import org.krakenapps.pcap.Protocol;
import org.krakenapps.pcap.decoder.snmp.SnmpDecoder;
import org.krakenapps.pcap.decoder.snmp.v1.Pdu;
import java.net.InetSocketAddress;
/**
* Handles decoded SNMP data by listening to appropriate events that are fired by
* {@link org.krakenapps.pcap.decoder.snmp.SnmpDecoder} and creates
* {@link at.jku.fim.rubanetra.protocol.activity.snmp.Snmpv1Activity}
* and/or {@link at.jku.fim.rubanetra.protocol.activity.snmp.Snmpv2Activity}
* objects.
* This class does not provide any forward bindings and does not support
* SNMPv3 due to lacking decoder support..
*/
public class KrakenSnmpHandler extends KrakenBaseProtocol {
public static final KrakenApplicationProtocolId krakenSnmpProtocolId = new KrakenApplicationProtocolId() {
@Override
public Protocol getKrakenApplicationProtocol() {
return Protocol.SNMP;
}
@Override
public String getProtocolId() {
return Protocol.SNMP.name();
}
@Override
public String getName() {
return "Snmp";
}
};
public static final String PROPERTY_NEW_SNMPV_1_ACTIVITY = "newSnmpv1Activity";
public static final String PROPERTY_NEW_SNMPV_2_ACTIVITY = "newSnmpv2Activity";
private final SnmpDecoder krakenSnmpDecoder;
public KrakenSnmpHandler() {
krakenSnmpDecoder = new SnmpDecoder();
krakenSnmpDecoder.register((InetSocketAddress source, InetSocketAddress destination, Pdu pdu) -> {
Snmpv1Activity act = new Snmpv1Activity(getCurrentPcapActivity(), pdu, source, destination);
propertyChangeSupport.firePropertyChange(PROPERTY_NEW_SNMPV_1_ACTIVITY, null, act);
});
krakenSnmpDecoder.register((InetSocketAddress source, InetSocketAddress destination, org.krakenapps.pcap.decoder.snmp.v2.Pdu pdu) -> {
Snmpv2Activity act = new Snmpv2Activity(getCurrentPcapActivity(), pdu, source, destination);
propertyChangeSupport.firePropertyChange(PROPERTY_NEW_SNMPV_2_ACTIVITY, null, act);
});
// V3 is not yet implemented by Kraken
}
@Override
public ProtocolId getProtocolId() {
return krakenSnmpProtocolId;
}
/**
* @return the internally used SNMP decoder
*/
public SnmpDecoder getKrakenSnmpDecoder() {
return krakenSnmpDecoder;
}
}
@@ -0,0 +1,213 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.tcp.TcpActivity;
import at.jku.fim.rubanetra.protocol.mapper.KrakenTcpProtocolMapperStrategy;
import org.krakenapps.pcap.decoder.ip.InternetProtocol;
import org.krakenapps.pcap.decoder.tcp.TcpDecoder;
import org.krakenapps.pcap.decoder.tcp.TcpPacket;
import org.krakenapps.pcap.decoder.tcp.TcpSession;
/**
* This class handles TCP transport layer related events that are dispatched by
* a {@link org.krakenapps.pcap.decoder.tcp.TcpDecoder}. The following properties
* may be observed:
* Due to Kraken's constraint of using a TCP-Next Protocol mapper, which is per se
* not always desirable for certain use cases, a corresponding Mapper has to be
* constructed and passed during the creation of the TCP decoder itself. Currently,
* this is not directly feasible because in theory, the exact strategy is chosen
* by the user and may deviate from application layer protocol to another application layer
* protocol. This library constraint is respected by creating the TCP decoder
* only during the invocation of {@link #initialize(at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration)}
* as well as the transport layer to application layer mapping strategy.
* However, if a restriction based, e.g. port-based, mapping strategy is considered it should be noted that
* this class tries to dispatch TCP activities regardless of the mapping strategy
* in use. These activities are exclusively passed to listeners on the individual
* instance of this class, which may not care about a potential application layer
* restriction that would in practice discard any non-relevant packets at the
* transport layer due to the mapping strategy in use and prevent potentially useful analysis.
* Therefore, {@link at.jku.fim.rubanetra.protocol.activity.tcp.TcpActivity} objects
* are dispatched to all registered listeners regardless of mapping strategy restrictions in place, and any protocol handler
* bindings will only receive those decoded packets that pass and conform to the mapping strategy in use (which
* could be all, none or a subset of all TCP packets).
* These forward bindings are currently comprised of:
* <ul>
* <li>-> {@link at.jku.fim.rubanetra.protocol.handler.KrakenHttpHandler}</li>
* <li>-> {@link at.jku.fim.rubanetra.protocol.handler.KrakenMsnHandler}</li>
* <li>-> {@link at.jku.fim.rubanetra.protocol.handler.KrakenDnsProtocolHandler}</li>
* <li>-> {@link at.jku.fim.rubanetra.protocol.handler.KrakenFtpHandler}</li>
* <li>-> {@link at.jku.fim.rubanetra.protocol.handler.KrakenNetbiosHandler}</li>
* <li>-> {@link at.jku.fim.rubanetra.protocol.handler.KrakenPop3Handler}</li>
* <li>-> {@link at.jku.fim.rubanetra.protocol.handler.KrakenTelnetHandler}</li>
* <li>-> {@link at.jku.fim.rubanetra.protocol.handler.KrakenSmtpHandler}</li>
* </ul>
*/
public class KrakenTcpHandler extends KrakenBaseProtocol {
public static final String NEW_TCP_ACTIVITY_PROPERTY_NAME = "newTcpActivity";
public static final ProtocolId krakenTcpProtocolId = new ProtocolId() {
private final String protocolId = "Tcp";
@Override
public String getProtocolId() {
return protocolId;
}
@Override
public String getName() {
return protocolId;
}
@Override
public String toString() {
return getName();
}
};
private TcpDecoder krakenTcpDecoder;
private KrakenTcpProtocolMapperStrategy tcpMappingStrategy;
@Override
public void initialize(KrakenProtocolConfiguration setting) {
super.initialize(setting);
String mappingStrategyId = setting.getTransportLayerMappingStrategy();
tcpMappingStrategy = setting.getTransportLayerMappingFactory().createTcpMappingStrategy(mappingStrategyId);
tcpMappingStrategy.setup(setting);
krakenTcpDecoder = new TcpDecoder(tcpMappingStrategy);
krakenTcpDecoder.registerSegmentCallback((session, segment) -> fireNewTcpActivity((TcpPacket) segment, session));
}
protected void fireNewTcpActivity(TcpPacket tcpPacket, TcpSession tcpSession) {
TcpActivity tcpActivity = new TcpActivity(getCurrentPcapActivity(), tcpPacket, tcpSession);
log.debug("Fire new TcpActivity, frame number: {}", tcpActivity.getPcapActivity().getFrameNumber());
propertyChangeSupport.firePropertyChange(NEW_TCP_ACTIVITY_PROPERTY_NAME, null, tcpActivity);
}
@Override
public ProtocolId getProtocolId() {
return krakenTcpProtocolId;
}
/**
* Experimental backward binding from an IPv4 handler to this TCP handler
*
* @param protocol the IPv4 handler
*/
public void bind(KrakenIpv4Handler protocol) {
protocol.getKrakenIpv4Decoder().register(InternetProtocol.TCP, krakenTcpDecoder);
}
/**
* Experimental backward binding from an IPv6 handler to this TCP handler
*
* @param protocol the IPv6 handler
*/
public void bind(KrakenIpv6Handler protocol) {
protocol.getKrakenIpv6Decoder().register(InternetProtocol.TCP, krakenTcpDecoder);
}
/**
* Forward binding to a HTTP protocol handler, i.e. all decoded TCP segments that correspond
* to the transport layer mapping strategy will be forwarded to the provided HTTP handler.
*
* @param protocolHandler the HTTP protocol handler
*/
public void bind(KrakenHttpHandler protocolHandler) {
tcpMappingStrategy.register(protocolHandler.getKrakenHttpDecoder(), KrakenHttpHandler.krakenHttpProtocolId);
}
/**
* Forward binding to a MSN handler, i.e. all decoded TCP segments that correspond to
* the transport layer mapping strategy will be forwarded to the provided MSN handler.
*
* @param protocolHandler the MSN protocol handler
*/
public void bind(KrakenMsnHandler protocolHandler) {
tcpMappingStrategy.register(protocolHandler.getKrakenMsnDecoder(), KrakenMsnHandler.krakenMsnProtocolId);
}
/**
* Forward binding to a DNS handler, i.e. all decoded TCP segments that correspond to
* the transport layer mapping strategy will be forwarded to the provided DNS handler.
*
* @param dnsHandler the DNS protocol handler
*/
public void bind(KrakenDnsProtocolHandler dnsHandler) {
tcpMappingStrategy.register(dnsHandler, KrakenDnsProtocolHandler.krakenDnsProtocolId);
}
/**
* Forward binding to a FTP handler, i.e. all decoded TCP segments that correspond to
* the transport layer mapping strategy will be forwarded to the provided protocol handler.
*
* @param ftpHandler the FTP protocol handler
*/
public void bind(KrakenFtpHandler ftpHandler) {
tcpMappingStrategy.register(ftpHandler.getKrakenFtpDecoder(), KrakenFtpHandler.krakenFtpProtocolId);
}
/**
* Forward binding to a NetBios handler, i.e. all decoded TCP segments that correspond to
* the transport layer mapping strategy will be forwarded to the provided protocol handler.
*
* @param netbiosHandler the NetBios handler
*/
public void bind(KrakenNetbiosHandler netbiosHandler) {
tcpMappingStrategy.register(netbiosHandler.getKrakenNetbiosDecoder(), KrakenNetbiosHandler.krakenNetbiosProtocolId);
}
/**
* Forward binding to a POP3 handler, i.e. all decoded TCP segments that correspond to
* the transport layer mapping strategy will be forwarded to the provided protocol handler.
*
* @param pop3Handler the POP3 handler
*/
public void bind(KrakenPop3Handler pop3Handler) {
tcpMappingStrategy.register(pop3Handler.getKrakenPop3Decoder(), KrakenPop3Handler.krakenPop3ProtocolId);
}
/**
* Forward binding to a Telnet handler, i.e. all decoded TCP segments that correspond to
* the transport layer mapping strategy will be forwarded to the provided protocol handler.
*
* @param telnetHandler the Telnet handler
*/
public void bind(KrakenTelnetHandler telnetHandler) {
tcpMappingStrategy.register(telnetHandler.getKrakenTelnetDecoder(), KrakenTelnetHandler.krakenTelnetProtocolId);
}
/**
* Forward binding to a SMTP handler, i.e. all decoded TCP segments that correspond to
* the transport layer mapping strategy will be forwarded to the provided protocol handler.
*
* @param smtpHandler the SMTP handler
*/
public void bind(KrakenSmtpHandler smtpHandler) {
tcpMappingStrategy.register(smtpHandler.getKrakenSmtpDecoder(), KrakenSmtpHandler.krakenSmtpProtocolId);
}
/**
* @return the internally used TCP decoder
*/
public TcpDecoder getKrakenTcpDecoder() {
return krakenTcpDecoder;
}
}
@@ -0,0 +1,137 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.telnet.TelnetActivity;
import org.krakenapps.pcap.Protocol;
import org.krakenapps.pcap.decoder.telnet.*;
/**
* Handles Telnet data decoded by the Kraken library and creates suitable
* {@link at.jku.fim.rubanetra.protocol.activity.telnet.TelnetActivity}
* objects that are dispatched to all registered listeners. These activities are
* currently not reassembled, i.e. the session reassembly should be performed
* via appropriate Drools rule specifications.
*/
public class KrakenTelnetHandler extends KrakenBaseProtocol {
public static final KrakenApplicationProtocolId krakenTelnetProtocolId = new KrakenApplicationProtocolId() {
@Override
public Protocol getKrakenApplicationProtocol() {
return Protocol.TELNET;
}
@Override
public String getProtocolId() {
return Protocol.TELNET.name();
}
@Override
public String getName() {
return "Telnet";
}
};
private final TelnetDecoder krakenTelnetDecoder;
public KrakenTelnetHandler() {
krakenTelnetDecoder = new TelnetDecoder();
krakenTelnetDecoder.register(new TelnetProcessor() {
@Override
public void onClientCommand(TelnetCommand command, TelnetOption option, byte[] data) {
TelnetActivity act = new TelnetActivity(getCurrentPcapActivity(), TelnetActivity.Type.CLIENT_COMMAND);
act.setCommand(command);
act.setOption(option);
act.setData(data);
fireNewTelnetActivity(act);
}
@Override
public void onServerCommand(TelnetCommand command, TelnetOption option, byte[] data) {
TelnetActivity act = new TelnetActivity(getCurrentPcapActivity(), TelnetActivity.Type.SERVER_COMMAND);
act.setCommand(command);
act.setOption(option);
act.setData(data);
fireNewTelnetActivity(act);
}
@Override
public void onClientAnsiControl(AnsiMode mode, TelnetCommand command, int[] arguments) {
TelnetActivity act = new TelnetActivity(getCurrentPcapActivity(), TelnetActivity.Type.CLIENT_ANSICONTROL);
act.setAnsiMode(mode);
act.setCommand(command);
act.setArguments(arguments);
fireNewTelnetActivity(act);
}
@Override
public void onServerAnsiControl(AnsiMode mode, TelnetCommand command, int[] arguments) {
TelnetActivity act = new TelnetActivity(getCurrentPcapActivity(), TelnetActivity.Type.SERVER_ANSICONTROL);
act.setAnsiMode(mode);
act.setCommand(command);
act.setArguments(arguments);
fireNewTelnetActivity(act);
}
@Override
public void onClientData(String text) {
TelnetActivity act = new TelnetActivity(getCurrentPcapActivity(), TelnetActivity.Type.CLIENT_DATA);
act.setText(text);
fireNewTelnetActivity(act);
}
@Override
public void onServerData(String text) {
TelnetActivity act = new TelnetActivity(getCurrentPcapActivity(), TelnetActivity.Type.SERVER_DATA);
act.setText(text);
fireNewTelnetActivity(act);
}
@Override
public void onClientTitle(String title) {
TelnetActivity act = new TelnetActivity(getCurrentPcapActivity(), TelnetActivity.Type.CLIENT_TITLE);
act.setTitle(title);
fireNewTelnetActivity(act);
}
@Override
public void onServerTitle(String title) {
TelnetActivity act = new TelnetActivity(getCurrentPcapActivity(), TelnetActivity.Type.SERVER_TITLE);
act.setTitle(title);
fireNewTelnetActivity(act);
}
});
}
private void fireNewTelnetActivity(TelnetActivity act) {
propertyChangeSupport.firePropertyChange("newPartialTelnetActivity", null, act);
}
@Override
public ProtocolId getProtocolId() {
return krakenTelnetProtocolId;
}
/**
* @return the internally used Telnet decoder
*/
public TelnetDecoder getKrakenTelnetDecoder() {
return krakenTelnetDecoder;
}
}
@@ -0,0 +1,171 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.handler;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration;
import at.jku.fim.rubanetra.config.model.ProtocolId;
import at.jku.fim.rubanetra.protocol.KrakenBaseProtocol;
import at.jku.fim.rubanetra.protocol.activity.udp.UdpActivity;
import at.jku.fim.rubanetra.protocol.mapper.KrakenTransportLayerMappingFactory;
import at.jku.fim.rubanetra.protocol.mapper.KrakenUdpProtocolMapperStrategy;
import org.krakenapps.pcap.Protocol;
import org.krakenapps.pcap.decoder.ip.InternetProtocol;
import org.krakenapps.pcap.decoder.udp.UdpDecoder;
import org.krakenapps.pcap.decoder.udp.UdpPacket;
/**
* This class handles UDP transport layer related events that are dispatched by
* a {@link org.krakenapps.pcap.decoder.udp.UdpDecoder} similar to the
* {@link at.jku.fim.rubanetra.protocol.handler.KrakenTcpHandler} implementation.
* Due to Kraken's constraint of using a UDP-Next Protocol mapper, which is per se
* not always desirable for certain use cases, a corresponding Mapper has to be
* constructed and passed during the creation of the UDP decoder itself. Currently,
* this is not directly feasible because in theory, the exact strategy is chosen
* by the user and may deviate from application layer protocol to another application layer
* protocol. This library constraint is respected by creating the UDP decoder
* only during the invocation of {@link #initialize(at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration)}
* as well as the transport layer to application layer mapping strategy.
* However, if a restriction based, e.g. port-based, mapping strategy is considered it should be noted that
* this class tries to dispatch UDP activities regardless of the mapping strategy
* in use. These activities are exclusively passed to listeners on the individual
* instance of this class, which may not care about a potential application layer
* restriction that would in practice discard any non-relevant packets at the
* transport layer due to the mapping strategy in use and prevent potentially useful analysis.
* Therefore, {@link at.jku.fim.rubanetra.protocol.activity.udp.UdpActivity} objects
* are dispatched to all registered listeners regardless of mapping strategy restrictions in place, and any protocol handler
* bindings will only receive those decoded packets that pass and conform to the mapping strategy in use (which
* could be all, none or a subset of all UDP packets).
* These forward bindings are currently comprised of:
* <ul>
* <li>-> {@link at.jku.fim.rubanetra.protocol.handler.KrakenDnsProtocolHandler}</li>
* <li>-> {@link at.jku.fim.rubanetra.protocol.handler.KrakenDhcpHandler}</li>
* <li>-> {@link at.jku.fim.rubanetra.protocol.handler.KrakenNetbiosHandler}</li>
* <li>-> {@link at.jku.fim.rubanetra.protocol.handler.KrakenSnmpHandler}</li>
* </ul>
*/
public class KrakenUdpHandler extends KrakenBaseProtocol {
private UdpDecoder krakenUdpDecoder;
private KrakenUdpProtocolMapperStrategy mappingStrategy;
@Override
public void initialize(KrakenProtocolConfiguration setting) {
super.initialize(setting);
String mappingStrategyId = setting.getTransportLayerMappingStrategy();
mappingStrategy = setting.getTransportLayerMappingFactory().createUdpMappingStrategy(mappingStrategyId);
mappingStrategy.setup(setting);
krakenUdpDecoder = new UdpDecoder(mappingStrategy);
krakenUdpDecoder.registerUdpProcessor(this::fireNewUdpActivity);
}
@Override
public ProtocolId getProtocolId() {
return krakenUdpProtocolId;
}
public static final ProtocolId krakenUdpProtocolId = new ProtocolId() {
private final String protocolId = "Udp";
@Override
public String getProtocolId() {
return protocolId;
}
@Override
public String getName() {
return protocolId;
}
@Override
public String toString() {
return getName();
}
};
protected void fireNewUdpActivity(UdpPacket udpPacket) {
UdpActivity udpActivity = new UdpActivity(getCurrentPcapActivity(), udpPacket);
propertyChangeSupport.firePropertyChange("newUdpActivity", null, udpActivity);
}
/**
* Experimental backward binding from an IPv4 protocol decoder to this UDP decoder
*
* @param protocol the IPv4 handler
*/
public void bind(KrakenIpv4Handler protocol) {
protocol.getKrakenIpv4Decoder().register(InternetProtocol.UDP, krakenUdpDecoder);
}
/**
* Experimental backward binding from an IPv6 protocol decoder to this UDP decoder
*
* @param protocol the IPv6 handler
*/
public void bind(KrakenIpv6Handler protocol) {
protocol.getKrakenIpv6Decoder().register(InternetProtocol.UDP, krakenUdpDecoder);
}
/**
* Forward binding from this UDP decoder to the provided DNS protocol handler, i.e.
* any decoded UDP segments will be forwarded to the passed protocol decoder.
*
* @param dnsHandler the DNS protocol handler
*/
public void bind(KrakenDnsProtocolHandler dnsHandler) {
mappingStrategy.register(dnsHandler, KrakenDnsProtocolHandler.krakenDnsProtocolId);
}
/**
* Forward binding from this UDP decoder to the provided DHCP protocol handler, i.e.
* any decoded UDP segments will be forwarded to the passed protocol decoder.
*
* @param dhcpHandler the DHCP protocol handler
*/
public void bind(KrakenDhcpHandler dhcpHandler) {
mappingStrategy.register(dhcpHandler.getKrakenDhcpDecoder(), KrakenDhcpHandler.krakenDhcpProtocolId);
}
/**
* Forward binding from this UDP decoder to the provided NetBios protocol handler, i.e.
* any decoded UDP segments will be forwarded to the passed protocol decoder.
*
* @param netbiosHandler the NetBios handler
*/
public void bind(KrakenNetbiosHandler netbiosHandler) {
mappingStrategy.register(netbiosHandler.getKrakenNetbiosDecoder(), KrakenNetbiosHandler.krakenNetbiosProtocolId);
}
/**
* Forward binding from this UDP decoder to the provided SNMP protocol handler, i.e.
* any decoded UDP segments will be forwarded to the passed protocol decoder.
*
* @param snmpHandler the SNMP protocol handler
*/
public void bind(KrakenSnmpHandler snmpHandler) {
mappingStrategy.register(snmpHandler.getKrakenSnmpDecoder(), KrakenSnmpHandler.krakenSnmpProtocolId);
}
/**
* @return the internally used UDP protocol
*/
public UdpDecoder getKrakenUdpDecoder() {
return krakenUdpDecoder;
}
}
@@ -0,0 +1,46 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.mapper;
import at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId;
import org.krakenapps.pcap.decoder.tcp.TcpProcessor;
import org.krakenapps.pcap.decoder.tcp.TcpProtocolMapper;
import java.util.Set;
/**
* Extends the Kraken interface used for mapping a transport layer protocol (TCP in this case) to application layer
* protocol decoders.
*/
public interface KrakenTcpProtocolMapperStrategy extends TcpProtocolMapper, TransportLayerMappingStrategy {
/**
* An unmodifiable collection of all registered decoders that are capable of processing TCP payloads.
*
* @return an unmodifiable collection of all registered decoders that are capable of processing TCP payloads.
*/
public abstract Set<TcpProcessor> getTcpProcessors();
/**
* Registers a TCP processor for the given application layer protocol.
*
* @param processor the TCP processor to be registered for ...
* @param protocolId ... the application layer protocol id
*/
public abstract void register(TcpProcessor processor, KrakenApplicationProtocolId protocolId);
}
@@ -0,0 +1,131 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.mapper;
import at.jku.fim.rubanetra.protocol.mapper.impl.KrakenTcpDestinationPortProtocolMapper;
import at.jku.fim.rubanetra.protocol.mapper.impl.KrakenUdpDestinationPortProtocolMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
/**
* Constructs suitable {@link at.jku.fim.rubanetra.protocol.mapper.TransportLayerMappingStrategy}
* implementations given a specific strategy identifier string.
* Handles both, the UDP and TCP strategies, i.e.: {@link at.jku.fim.rubanetra.protocol.mapper.KrakenTcpProtocolMapperStrategy}
* and {@link at.jku.fim.rubanetra.protocol.mapper.KrakenUdpProtocolMapperStrategy}.
* Custom strategies may be registered during runtime.
* The default factory {@link #createDefaultKrakenTransportLayerMappingFactory()} only provides a
* destination port strategy for TCP/UDP.
*/
public class KrakenTransportLayerMappingFactory {
public static final String DESTINATION_PORT_STRATEGY = "DESTINATION_PORT";
private final Map<String, Class<? extends KrakenTcpProtocolMapperStrategy>> tcpStrategyMap;
private final Map<String, Class<? extends KrakenUdpProtocolMapperStrategy>> udpStrategyMap;
private final Logger log = LoggerFactory.getLogger(getClass());
public KrakenTransportLayerMappingFactory() {
tcpStrategyMap = new HashMap<>();
udpStrategyMap = new HashMap<>();
}
/**
* Creates a default transport layer mapping factory that consists of only one strategy: a Destination port to
* application layer protocol strategy, i.e. {@link at.jku.fim.rubanetra.protocol.mapper.impl.KrakenTcpDestinationPortProtocolMapper}
* and {@link at.jku.fim.rubanetra.protocol.mapper.impl.KrakenUdpDestinationPortProtocolMapper}.
*
* @return the default transport layer mapping factory
*/
public static KrakenTransportLayerMappingFactory createDefaultKrakenTransportLayerMappingFactory() {
KrakenTransportLayerMappingFactory factory = new KrakenTransportLayerMappingFactory();
factory.registerTcpStrategy(DESTINATION_PORT_STRATEGY, KrakenTcpDestinationPortProtocolMapper.class);
factory.registerUdpStrategy(DESTINATION_PORT_STRATEGY, KrakenUdpDestinationPortProtocolMapper.class);
return factory;
}
/**
* Registers a new TCP mapping strategy, the provided class must define a default constructor.
*
* @param tcpStrategyId the unique strategy identifier
* @param tcpStrategyClass the class to map to the identifier
*/
public void registerTcpStrategy(String tcpStrategyId, Class<? extends KrakenTcpProtocolMapperStrategy> tcpStrategyClass) {
if (tcpStrategyMap.containsKey(tcpStrategyId)) {
log.error("{} has already been registered as TCP mapping strategy.", tcpStrategyId);
throw new IllegalArgumentException();
}
tcpStrategyMap.put(tcpStrategyId, tcpStrategyClass);
}
/**
* Registers a new UDP mapping strategy, the provided class must define a default constructor
*
* @param udpStrategyId the unique strategy identifier
* @param udpStrategyClass the class to map to the identifier
*/
public void registerUdpStrategy(String udpStrategyId, Class<? extends KrakenUdpProtocolMapperStrategy> udpStrategyClass) {
if (udpStrategyMap.containsKey(udpStrategyId)) {
log.error("{} has already been registered as UDP mapping strategy.", udpStrategyId);
throw new IllegalArgumentException();
}
udpStrategyMap.put(udpStrategyId, udpStrategyClass);
}
/**
* Tries to create a new mapping strategy using the passed identifier to determine the mapping class that will be
* instantiated using the default constructor.
*
* @param mappingStrategy the identifier that was used to register the TCP mapping strategy
* @return a new instance of a TCP mapping strategy
*/
public KrakenTcpProtocolMapperStrategy createTcpMappingStrategy(String mappingStrategy) {
Class<? extends KrakenTcpProtocolMapperStrategy> tcpProtocolMapperClass = tcpStrategyMap.get(mappingStrategy);
if (tcpProtocolMapperClass == null) {
throw new IllegalArgumentException(String.format("Mapping strategy %s was not registered to the TransportLayerMappingFactory", mappingStrategy));
}
try {
return tcpProtocolMapperClass.newInstance();
} catch (IllegalAccessException | InstantiationException e) {
throw new IllegalArgumentException(String.format("A default constructor for mapping strategy %s and class %s must be provided.", mappingStrategy, tcpProtocolMapperClass.getName()));
}
}
/**
* Tries to create a new mapping strategy using the passed identifier to determine the mapping class that will be
* instantiated using the default constructor.
*
* @param mappingStrategy the identifier that was used to register the UDP mapping strategy
* @return a new instance of a UDP mapping strategy
*/
public KrakenUdpProtocolMapperStrategy createUdpMappingStrategy(String mappingStrategy) {
Class<? extends KrakenUdpProtocolMapperStrategy> udpProtocolMapperClass = udpStrategyMap.get(mappingStrategy);
if (udpProtocolMapperClass == null) {
throw new IllegalArgumentException(String.format("Mapping strategy %s was not registered to the TransportLayerMappingFactory", mappingStrategy));
}
try {
return udpProtocolMapperClass.newInstance();
} catch (IllegalAccessException | InstantiationException e) {
throw new IllegalArgumentException(String.format("Please provide a default constructor for mapping strategy %s and class %s.", mappingStrategy, udpProtocolMapperClass.getName()));
}
}
}
@@ -0,0 +1,47 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.mapper;
import at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId;
import org.krakenapps.pcap.decoder.udp.UdpProcessor;
import org.krakenapps.pcap.decoder.udp.UdpProtocolMapper;
import java.util.Set;
/**
* Extends the Kraken interface used for mapping a transport layer protocol (UDP in this case) to application layer
* protocol decoders following a certain strategy.
*/
public interface KrakenUdpProtocolMapperStrategy extends UdpProtocolMapper, TransportLayerMappingStrategy {
/**
* Registers an UDP processor that is capable of parsing application layer protocol, identified by
* the protocolId, specific data.
*
* @param processor the UDP processor for a protocol identified by ...
* @param protocolId the application layer protocol identifier
*/
public abstract void register(UdpProcessor processor, KrakenApplicationProtocolId protocolId);
/**
* Returns the unmodifiable set of all registered UDP processors.
*
* @return the unmodifiable set of all registered UDP processors
*/
public abstract Set<UdpProcessor> getUdpProcessors();
}
@@ -0,0 +1,29 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.mapper;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration;
/**
* Defines a strategy that defines a way how a transport layer protocol is mapped to an application layer protocol
* decoder.
*/
public interface TransportLayerMappingStrategy {
public abstract void setup(KrakenProtocolConfiguration protocolSetting);
}
@@ -0,0 +1,135 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.mapper.impl;
import at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration;
import at.jku.fim.rubanetra.config.model.PortSpecification;
import at.jku.fim.rubanetra.protocol.mapper.KrakenTcpProtocolMapperStrategy;
import org.krakenapps.pcap.Protocol;
import org.krakenapps.pcap.decoder.tcp.TcpProcessor;
import org.krakenapps.pcap.decoder.tcp.TcpSegment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* Maps a TCP based {@link at.jku.fim.rubanetra.config.model.PortSpecification} to a single application
* layer protocol. Multiple TCP processors may be registered for the same application layer protocol.
*/
public class KrakenTcpDestinationPortProtocolMapper implements KrakenTcpProtocolMapperStrategy {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Set<TcpProcessor> tcpProcessorSet;
private PortSpecification portSpec;
private KrakenApplicationProtocolId applicationProtocolId;
public KrakenTcpDestinationPortProtocolMapper() {
this.tcpProcessorSet = new HashSet<>();
}
@Override
public Protocol map(TcpSegment segment) {
// at this point we have to identify the protocol, however,
// this is not feasible due to the invalid assumption that there is a single Protocol per port.
// Therefore whenever the destination port is checked at this point not only one but several protocols
// may be identified. There are at least two solutions to this issue: Either the raw buffer is parsed according
// to every application protocol setting or the packet is decoded multiple times by different Protocol
// Mappers.
if (this.applicationProtocolId != null && portSpec.contains(segment.getDestinationPort())) {
return this.applicationProtocolId.getKrakenApplicationProtocol();
} else {
log.debug("returning null pointer as KrakenProtocol for application layer protocol {}", applicationProtocolId);
return null;
}
}
@Override
public Collection<TcpProcessor> getTcpProcessors(Protocol protocol) {
if (this.applicationProtocolId == null) {
if (protocol == null) {
// this means probably that there is no official Application Layer Protocol as successor, therefore we return the set
return Collections.unmodifiableCollection(tcpProcessorSet);
} else {
// protocols do not match
return Collections.emptySet();
}
}
return protocol == this.applicationProtocolId.getKrakenApplicationProtocol() ?
Collections.unmodifiableCollection(tcpProcessorSet) : Collections.unmodifiableCollection(new HashSet<>());
}
@Override
public void register(Protocol protocol, TcpProcessor processor) {
if (this.applicationProtocolId == null || protocol != this.applicationProtocolId.getKrakenApplicationProtocol()) {
throw new IllegalArgumentException();
}
this.tcpProcessorSet.add(processor);
}
@Override
public void unregister(Protocol protocol, TcpProcessor processor) {
this.tcpProcessorSet.remove(processor);
}
@Override
public void register(InetSocketAddress server, Protocol protocol) {
throw new IllegalStateException("Not yet implemented");
}
@Override
public void unregister(InetSocketAddress server) {
throw new IllegalStateException("Not yet implemented");
}
@Override
@Deprecated
public TcpProcessor getTcpProcessor(Protocol protocol) {
throw new IllegalStateException("Not yet implemented");
}
@Override
@Deprecated
public void unregister(Protocol protocol) {
throw new IllegalStateException("Not yet implemented");
}
@Override
public void setup(KrakenProtocolConfiguration protocolSetting) {
this.portSpec = protocolSetting.getPortSpecification();
}
@Override
public void register(TcpProcessor processor, KrakenApplicationProtocolId protocolId) {
if (this.applicationProtocolId != null) {
throw new IllegalStateException("This destination port protocol mapper only supports a single Application Layer Protocol per instance");
}
this.tcpProcessorSet.add(processor);
this.applicationProtocolId = protocolId;
}
@Override
public Set<TcpProcessor> getTcpProcessors() {
return Collections.unmodifiableSet(tcpProcessorSet);
}
}
@@ -0,0 +1,141 @@
/**
* This file is part of Rubanetra.
* Copyright (C) 2013,2014 Stefan Swerk (stefan_rubanetra@swerk.priv.at)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package at.jku.fim.rubanetra.protocol.mapper.impl;
import at.jku.fim.rubanetra.config.model.KrakenApplicationProtocolId;
import at.jku.fim.rubanetra.config.model.KrakenProtocolConfiguration;
import at.jku.fim.rubanetra.config.model.PortSpecification;
import at.jku.fim.rubanetra.protocol.mapper.KrakenUdpProtocolMapperStrategy;
import org.krakenapps.pcap.Protocol;
import org.krakenapps.pcap.decoder.udp.UdpPacket;
import org.krakenapps.pcap.decoder.udp.UdpProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* Maps an UDP based {@link at.jku.fim.rubanetra.config.model.PortSpecification} to a single application
* layer protocol. Multiple UDP processors may be registered for the same application layer protocol.
*/
public class KrakenUdpDestinationPortProtocolMapper implements KrakenUdpProtocolMapperStrategy {
private final Logger log = LoggerFactory.getLogger(getClass());
private final Set<UdpProcessor> udpProcessorSet;
private PortSpecification portSpec;
private KrakenApplicationProtocolId applicationProtocolId;
public KrakenUdpDestinationPortProtocolMapper() {
this.udpProcessorSet = new HashSet<>();
}
@Override
public Protocol map(UdpPacket packet) {
// at this point we have to identify the protocol, however,
// this is not feasible due to the invalid assumption that there is a
// single Protocol per port.
// Therefore whenever the destination port is checked at this point not
// only one but several protocols
// may be identified. There are at least two solutions to this issue: Either the
// raw buffer is parsed according
// to every application protocol setting or the packet is decoded
// multiple times by different Protocol
// Mappers.
if (this.applicationProtocolId != null && portSpec.contains(packet.getDestinationPort())) {
return this.applicationProtocolId.getKrakenApplicationProtocol();
} else {
log.debug("returning null pointer as KrakenProtocol for application layer protocol {}", applicationProtocolId);
return null;
}
}
@Override
public Collection<UdpProcessor> getUdpProcessors(Protocol protocol) {
if (this.applicationProtocolId == null) {
if (protocol == null) {
// this means probably that there is no official Application Layer Protocol as successor, therefore we return the set
return Collections.unmodifiableCollection(udpProcessorSet);
} else {
// protocols do not match
return Collections.emptySet();
}
}
return protocol == this.applicationProtocolId.getKrakenApplicationProtocol() ?
Collections.unmodifiableCollection(udpProcessorSet) : Collections.unmodifiableCollection(new HashSet<>());
}
@Override
@Deprecated
public UdpProcessor getUdpProcessor(Protocol protocol) {
throw new IllegalStateException("Not yet implemented");
}
@Override
public void register(Protocol protocol, UdpProcessor processor) {
if (this.applicationProtocolId == null || protocol != this.applicationProtocolId.getKrakenApplicationProtocol()) {
throw new IllegalArgumentException();
}
this.udpProcessorSet.add(processor);
}
@Override
@Deprecated
public void unregister(Protocol protocol) {
throw new IllegalStateException("Not yet implemented");
}
@Override
public void unregister(Protocol protocol, UdpProcessor processor) {
throw new IllegalStateException("Not yet implemented");
}
@Override
public void registerTemporaryMapping(InetSocketAddress sockAddr, Protocol protocol) {
throw new IllegalStateException("Not yet implemented");
}
@Override
public void unregisterTemporaryMapping(InetSocketAddress sockAddr) {
throw new IllegalStateException("Not yet implemented");
}
@Override
public void setup(KrakenProtocolConfiguration protocolSetting) {
this.portSpec = protocolSetting.getPortSpecification();
}
@Override
public void register(UdpProcessor processor, KrakenApplicationProtocolId protocolId) {
if (this.applicationProtocolId != null) {
throw new IllegalStateException("This destination port protocol mapper only supports a single Application Layer Protocol per instance");
}
this.udpProcessorSet.add(processor);
this.applicationProtocolId = protocolId;
}
@Override
public Set<UdpProcessor> getUdpProcessors() {
return Collections.unmodifiableSet(udpProcessorSet);
}
}

Some files were not shown because too many files have changed in this diff Show More