zeek/src/spicy/spicyz/glue-compiler.h
2024-08-19 09:57:04 +02:00

307 lines
13 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <map>
#include <memory>
#include <set>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include <hilti/rt/filesystem.h>
#include <hilti/rt/types/port.h>
#include <hilti/rt/util.h>
#include <spicy/rt/mime.h>
#include <hilti/ast/builder/builder.h>
#include <hilti/ast/declarations/function.h>
#include <hilti/ast/declarations/module.h>
#include <hilti/ast/expression.h>
#include <hilti/ast/type.h>
#include <hilti/compiler/context.h>
#include <hilti/compiler/driver.h>
#include <spicy/ast/builder/builder.h>
#include <spicy/ast/declarations/unit-hook.h>
#include <spicy/ast/types/unit.h>
#include "driver.h"
#include "zeek/spicy/port-range.h"
namespace spicy::rt {
struct Parser;
}
namespace zeek::spicy {
namespace glue {
/** Representation of a Spicy protocol analyzer, parsed from an EVT file. */
struct ProtocolAnalyzer {
// Information parsed directly from the *.evt file.
hilti::Location location; /**< Location where the analyzer was defined. */
hilti::ID name; /**< Name of the analyzer. */
hilti::rt::Protocol protocol = hilti::rt::Protocol::Undef; /**< The transport layer the analyzer uses. */
std::vector<::zeek::spicy::rt::PortRange> ports; /**< The ports associated with the analyzer. */
hilti::ID unit_name_orig; /**< The fully-qualified name of the unit type to parse the originator
side. */
hilti::ID unit_name_resp; /**< The fully-qualified name of the unit type to parse the originator
side. */
std::string replaces; /**< Name of another analyzer this one replaces. */
// Computed information.
std::optional<TypeInfo> unit_orig; /**< The type of the unit to parse the originator side. */
std::optional<TypeInfo> unit_resp; /**< The type of the unit to parse the originator side. */
};
/** Representation of a Spicy file analyzer, parsed from an EVT file. */
struct FileAnalyzer {
// Information parsed directly from the *.evt file.
hilti::Location location; /**< Location where the analyzer was defined. */
hilti::ID name; /**< Name of the analyzer. */
std::vector<std::string> mime_types; /**< The mime_types associated with the analyzer. */
hilti::ID unit_name; /**< The fully-qualified name of the unit type to parse with. */
std::string replaces; /**< Name of another analyzer this one replaces. */
// Computed information.
std::optional<TypeInfo> unit; /**< The type of the unit to parse the originator side. */
};
/** Representation of a Spicy packet analyzer, parsed from an EVT file. */
struct PacketAnalyzer {
// Information parsed directly from the *.evt file.
hilti::Location location; /**< Location where the analyzer was defined. */
hilti::ID name; /**< Name of the analyzer. */
hilti::ID unit_name; /**< The fully-qualified name of the unit type to parse with. */
std::string replaces; /**< Name of another analyzer this one replaces. */
// Computed information.
std::optional<TypeInfo> unit; /**< The type of the unit to parse the originator side. */
};
/**
* Representation of an expression computing the value of a parameter passed
* to Spicy-generated events.
*/
struct ExpressionAccessor {
// Information parsed directly from the *.evt file.
int nr; /**< Position of this expression in argument list. */
std::string expression; /**< The original string representation of the expression. */
hilti::Location location; /**< Location where the expression was defined. */
};
/** Representation of a compiled Spicy module. */
struct SpicyModule {
// Provided.
hilti::ID id; /**< Name of the module */
hilti::rt::filesystem::path file; /**< The path the module was read from. */
std::set<hilti::rt::filesystem::path> evts; /**< EVT files that refer to this module. */
// Generated code.
hilti::node::RetainedPtr<hilti::declaration::Module> spicy_module =
nullptr; /**< the ``BroHooks_*.spicy`` module. */
};
/** Representation of an event parsed from an EVT file. */
struct Event {
// Information parsed directly from the *.evt file.
hilti::rt::filesystem::path file; /**< The path of the *.evt file we parsed this from. */
hilti::ID name; /**< The name of the event. */
hilti::ID path; /**< The hook path as specified in the evt file. */
std::vector<hilti::node::RetainedPtr<hilti::declaration::Parameter>>
parameters; /**< Event parameters specified in the evt file. */
std::string condition; /**< Condition that must be true for the event to trigger. */
std::vector<std::string> exprs; /**< The argument expressions. */
int priority; /**< Event/hook priority. */
hilti::Location location; /**< Location where event is defined. */
// Computed information.
hilti::ID hook; /**< The name of the hook triggering the event. */
hilti::ID unit; /**< The fully qualified name of the unit type. */
hilti::node::RetainedPtr<::spicy::type::Unit> unit_type = nullptr; /**< The Spicy type of referenced unit. */
hilti::ID unit_module_id; /**< The name of the module the referenced unit is defined in. */
hilti::rt::filesystem::path unit_module_path; /**< The path of the module that the referenced unit is defined in. */
std::shared_ptr<glue::SpicyModule>
spicy_module; /**< State for the Spichy module the referenced unit is defined in. */
// Code generation.
std::vector<ExpressionAccessor> expression_accessors; /**< One HILTI function per expression to access the value. */
};
/** Representation of an "export" statement parsed from an EVT file. */
struct Export {
hilti::ID spicy_id;
hilti::ID zeek_id;
hilti::Location location;
// Additional information for exported record types.
bool log_all = false; /**< mark all fields for logging in exported record */
std::set<hilti::ID> with; /**< fields to include in exported record */
std::set<hilti::ID> without; /**< fields to exclude from exported record */
std::set<hilti::ID> logs; /**< fields to mark for logging in exported record */
/**
* Checks that the information is semantically correct given the provided
* type information. Logs any errors and returns false on failure.
**/
bool validate(const TypeInfo& ti) const;
};
} // namespace glue
/** Generates the glue code between Zeek and Spicy based on *.evt files. */
class GlueCompiler {
public:
/** Constructor. */
GlueCompiler() {}
/** Destructor. */
virtual ~GlueCompiler();
/**
* Returns the AST context in use. Only available once the driver has
* initialized the glue compiler.
*/
auto* context() { return _driver->context()->astContext(); }
/**
* Returns the AST builder in use. Only available once the driver has
* initialized the glue compiler.
*/
auto* builder() { return static_cast<::spicy::Builder*>(_driver->builder()); }
/** Parses an `*.evt` file, without generating any code yet. */
bool loadEvtFile(hilti::rt::filesystem::path& path);
/**
* Registers a Spicy file to generate glue code for, without generating
* any code yet.
*
* @param id ID of the module
* @param file path the module is loaded from
*/
void addSpicyModule(const hilti::ID& id, const hilti::rt::filesystem::path& file);
/**
* Generates all glue code based on previously registered `*.evt` and
* Spicy files.
*/
bool compile();
/** Returns all IDs that have been exported so far. */
const auto& exportedIDs() const { return _exports; }
/** Returns the `export` declaration for a specific type given by the Zeek-side ID, if available. */
std::optional<glue::Export> exportForZeekID(const hilti::ID& id) const;
/** Provides `export` details for a given record field. */
struct ExportedField {
bool skip = false; /**< True if field is not to be included in the exported type. */
bool log = false; /**< True if field is logged. */
};
/**
* Retrieves the `export` details for a given record field. If our EVT
* file doesn't mention the field explicitly, the method returns the
* default behavior.
*/
ExportedField exportForField(const hilti::ID& zeek_id, const hilti::ID& field_id) const;
using ZeekTypeCache = std::map<hilti::ID, hilti::Expression*>;
/**
* Generates code to convert a HILTI type to a corresponding Zeek type at
* runtime. The code is added to the given body.
*/
hilti::Result<hilti::Nothing> createZeekType(hilti::QualifiedType* t, const hilti::ID& id,
::spicy::Builder* builder, ZeekTypeCache* cache) const;
/** Return type for `recordField()`. */
struct RecordField {
hilti::ID id; /**< name of record field */
hilti::QualifiedType* type = nullptr; /**< Spicy-side type object */
bool is_optional; /**< true if field is optional */
bool is_anonymous; /**< true if field is annymous */
};
/**
* Helper to retrieve a list of Zeek-side record fields that converting a
* Spicy unit to a Zeek record will yield.
*
* @param unit the unit type to retrieve fields for
* @return list of fields
*/
static std::vector<RecordField> recordFields(const ::spicy::type::Unit* unit);
protected:
friend class Driver;
/** Called by driver to initialized a provided glue compiler. */
void init(Driver* driver, int zeek_version);
private:
/**
* Filters input EVT file by applying preprocessor directives.
*/
void preprocessEvtFile(hilti::rt::filesystem::path& path, std::istream& in, std::ostream& out);
/**
* Extracts the next semicolon-terminated block from an input stream,
* accounting for special EVT constructs like strings and comments.
*
* @param in stream to read from
* @param lineno pointer to integer that will be increased with line breaks
* @return the read block of data, with comments removed, and empty if end of
* data has been reached; error will be set if parsing failed
*/
hilti::Result<std::string> getNextEvtBlock(std::istream& in, int* lineno) const;
// Parsers for parts from EVT files.
glue::ProtocolAnalyzer parseProtocolAnalyzer(const std::string& chunk);
glue::FileAnalyzer parseFileAnalyzer(const std::string& chunk);
glue::PacketAnalyzer parsePacketAnalyzer(const std::string& chunk);
glue::Event parseEvent(const std::string& chunk);
glue::Export parseExport(const std::string& chunk);
/** Computes the missing pieces for all `Event` instances. */
bool PopulateEvents();
/**
* Create the Spicy hook for an event that triggers a corresponding Zeek
* event.
*/
bool CreateSpicyHook(glue::Event* ev);
Driver* _driver = nullptr; /**< driver provided to Init() */
std::optional<int> _zeek_version; /**< Zeek version provided to Init() */
std::map<hilti::ID, std::shared_ptr<glue::SpicyModule>> _spicy_modules;
std::vector<std::pair<hilti::ID, hilti::ID>>
_imports; /**< imports from EVT files, with ID and scope; latter is optional and can be empty */
std::map<hilti::ID, glue::Export> _exports; /**< exports from EVT files */
std::vector<glue::Event> _events; /**< events parsed from EVT files */
std::vector<glue::ProtocolAnalyzer> _protocol_analyzers; /**< protocol analyzers parsed from EVT files */
std::vector<glue::FileAnalyzer> _file_analyzers; /**< file analyzers parsed from EVT files */
std::vector<glue::PacketAnalyzer> _packet_analyzers; /**< file analyzers parsed from EVT files */
std::vector<hilti::Location> _locations; /**< location stack during parsing EVT files */
std::string _doc_id; /**< name/ID associated with the current EVT module inside Zeekygen documentation */
std::string _doc_description; /**< textual description to be included for the current EVT module inside Zeekygen
documentation */
};
} // namespace zeek::spicy
namespace std {
template<>
struct hash<zeek::spicy::glue::Event> {
std::size_t operator()(const zeek::spicy::glue::Event& e) {
// We only hash enough information here to unique identify the event.
return hilti::rt::hashCombine(std::hash<std::string>()(e.file), std::hash<std::string>()(e.name),
std::hash<std::string>()(e.path), std::hash<std::string>()(e.location));
}
};
} // namespace std