%top{ // Include cstdint at the start of the generated file. Typically // MSVC will include this header later, after the definitions of // the integral type macros. MSVC then complains that about the // redefinition of the types. Including cstdint early avoids this. #include } %{ // See the file "COPYING" in the main distribution directory for copyright. #include #include #include #include #include #include #include #include #include #include #include #include #include "zeek/input.h" #include "zeek/util.h" #include "zeek/Scope.h" #include "zeek/ZeekString.h" #include "zeek/DNS_Mgr.h" #include "zeek/Desc.h" #include "zeek/Expr.h" #include "zeek/Func.h" #include "zeek/Stmt.h" #include "zeek/IntrusivePtr.h" #include "zeek/Val.h" #include "zeek/Var.h" #include "zeek/Debug.h" #include "zeek/PolicyFile.h" #include "zeek/Reporter.h" #include "zeek/RE.h" #include "zeek/RunState.h" #include "zeek/ScriptCoverageManager.h" #include "zeek/Traverse.h" #include "zeek/module_util.h" #include "zeek/ScannedFile.h" #include "zeek/analyzer/Analyzer.h" #include "zeek/zeekygen/Manager.h" #include "zeek/plugin/Manager.h" #include "zeekparse.h" using namespace zeek::detail; extern YYLTYPE yylloc; // holds location of token extern zeek::EnumType* cur_enum_type; // Track the @if... depth. static std::intptr_t conditional_depth = 0; zeek::detail::int_list entry_cond_depth; // @if depth upon starting file zeek::detail::int_list entry_pragma_stack_depth; // @pragma push depth upon starting file static std::vector pragma_stack; // stack of @pragma pushes // Tracks how many conditionals there have been. This value only // increases. Its value is to support logic such as figuring out // whether a function body has a conditional within it by comparing // the epoch at the beginning of parsing the body with that at the end. int conditional_epoch = 0; // Whether the current file has included conditionals (so far). bool current_file_has_conditionals = false; // The files that include conditionals. Used when compiling scripts to C++ // to flag issues for --optimize-files=/pat/ where one of the files includes // conditional code. std::unordered_set files_with_conditionals; zeek::detail::int_list if_stack; // Whether we're parsing a "when" conditional, for which we treat // the "local" keyword differently. int in_when_cond = 0; int line_number = 1; const char* filename = 0; // Absolute path of file currently being parsed. const char* last_filename = 0; // Absolute path of last file parsed. static const char* last_id_tok = 0; const char* last_tok_filename = 0; const char* last_last_tok_filename = 0; char last_tok[128]; #define YY_USER_ACTION strlcpy(last_tok, yytext, sizeof(last_tok)); \ last_last_tok_filename = last_tok_filename; \ last_tok_filename = ::filename; #define YY_USER_INIT last_tok[0] = '\0'; // We define our own YY_INPUT because we want to trap the case where // a read fails. #define YY_INPUT(buf,result,max_size) \ if ( ((result = fread(buf, 1, max_size, yyin)) == 0) && ferror(yyin) ) \ zeek::reporter->Error("read failed with \"%s\"", strerror(errno)); static std::string find_relative_file(const std::string& filename, const std::string& ext) { if ( filename.empty() ) return {}; if ( filename[0] == '.' ) return zeek::util::find_file(filename, zeek::util::SafeDirname(::filename).result, ext); else return zeek::util::find_file(filename, zeek::util::zeek_path(), ext); } static std::string find_relative_script_file(const std::string& filename) { if ( filename.empty() ) return {}; if ( filename[0] == '.' ) return zeek::util::find_script_file(filename, zeek::util::SafeDirname(::filename).result); else return zeek::util::find_script_file(filename, zeek::util::zeek_path()); } static void start_conditional() { ++conditional_depth; ++conditional_epoch; if ( ! current_file_has_conditionals ) // First time we've observed that this file includes conditionals. files_with_conditionals.insert(::filename); current_file_has_conditionals = true; } static void do_pragma(const std::string& pragma) { static std::unordered_set known_stack_pragmas = { "ignore-deprecations", }; auto parts = zeek::util::split(pragma, " "); auto new_end = std::remove(parts.begin(), parts.end(), ""); parts.erase(new_end, parts.end()); if ( parts.empty() ) { zeek::reporter->FatalError("@pragma without value"); return; } if ( parts[0] == "push" ) { if ( parts.size() < 2 ) { zeek::reporter->FatalError("@pragma push without value"); return; } if ( known_stack_pragmas.count(parts[1]) == 0 ) zeek::reporter->Warning("pushing unknown @pragma value '%s'", parts[1].c_str()); pragma_stack.push_back(parts[1]); } else if ( parts[0] == "pop" ) { if ( pragma_stack.empty() || pragma_stack.size() == entry_pragma_stack_depth.back() ) { zeek::reporter->FatalError("@pragma pop without active pragma"); return; } // Popping with a value: Verify it's popping the right thing. Not providing // a pop value itself is valid. Don't return, probably blows up below anyway. if ( parts.size() > 1 && pragma_stack.back() != parts[1] ) { zeek::reporter->Error("@pragma pop with unexpected '%s', expected '%s'", parts[1].c_str(), pragma_stack.back().c_str()); } // Just pop anything pragma_stack.pop_back(); } else zeek::reporter->Warning("Ignoring unknown pragma: '%s'", pragma.c_str()); // Cheap: Just walk the pragma_stack now to see if we should ignore deprecations. bool ignore_deprecations = std::any_of(pragma_stack.cbegin(), pragma_stack.cend(), [](const auto& s) { return s == "ignore-deprecations"; }); zeek::reporter->SetIgnoreDeprecations(ignore_deprecations); } class FileInfo { public: FileInfo(std::string restore_module = ""); ~FileInfo(); YY_BUFFER_STATE buffer_state; std::string restore_module; const char* name = nullptr; int line = 0; int level = 0; }; // A stack of input buffers we're scanning. file_stack[len-1] is the // top of the stack. static zeek::PList file_stack; #define RET_CONST(v) \ { \ yylval.val = v; \ return TOK_CONSTANT; \ } // Returns true if the file is new, false if it's already been scanned. static int load_files(const char* file); // Update the current parsing and location state for the given file and buffer. static int switch_to(const char* file, YY_BUFFER_STATE buffer); // Be careful to never delete things from this list, as the strings // are referred to (in order to save the locations of tokens and statements, // for error reporting and debugging). static zeek::name_list input_files; static zeek::name_list essential_input_files; %} %option nounput nodefault %x RE %x IGNORE OWS [ \t]* WS [ \t]+ D [0-9]+ HEX [0-9a-fA-F]+ IDCOMPONENT [A-Za-z_][A-Za-z_0-9]* ID {IDCOMPONENT}(::{IDCOMPONENT})* /* Token including including :: prefix for lookups of global identifiers */ GLOBAL_ID ::{IDCOMPONENT} IP6 ("["({HEX}:){7}{HEX}"]")|("["0x{HEX}({HEX}|:)*"::"({HEX}|:)*"]")|("["({HEX}|:)*"::"({HEX}|:)*"]")|("["({HEX}:){6}({D}"."){3}{D}"]")|("["({HEX}|:)*"::"({HEX}|:)*({D}"."){3}{D}"]") FILE [^ \t\r\n]+ PREFIX [^ \t\r\n]+ FLOAT (({D}*"."?{D})|({D}"."?{D}*))([eE][-+]?{D})? H [A-Za-z0-9][A-Za-z0-9\-]* HTLD [A-Za-z][A-Za-z0-9\-]* ESCSEQ (\\([^\r\n]|[0-7]+|x[[:xdigit:]]+)) %% ##!.* { if ( zeek::detail::current_scope() == zeek::detail::global_scope() ) zeek::detail::zeekygen_mgr->SummaryComment(::filename, yytext + 3); } ##<.* { if ( zeek::detail::current_scope() == zeek::detail::global_scope() ) { std::string hint(cur_enum_type && last_id_tok ? zeek::detail::make_full_var_name(zeek::detail::current_module.c_str(), last_id_tok) : ""); zeek::detail::zeekygen_mgr->PostComment(yytext + 3, hint); } } ##.* { if ( zeek::detail::current_scope() == zeek::detail::global_scope() ) if ( yytext[2] != '#' ) zeek::detail::zeekygen_mgr->PreComment(yytext + 2); } #{OWS}@no-test.* return TOK_NO_TEST; #.* /* eat comments */ {WS} /* eat whitespace */ \r?\n { ++line_number; yylloc.IncrementLine(); } /* IPv6 literal constant patterns */ {IP6} { RET_CONST(new zeek::AddrVal(zeek::util::detail::extract_ip(yytext))) } {IP6}{OWS}"/"{OWS}{D} { int len = 0; std::string ip = zeek::util::detail::extract_ip_and_len(yytext, &len); RET_CONST(new zeek::SubNetVal(zeek::IPPrefix(zeek::IPAddr(ip), len, true))) } /* IPv4 literal constant patterns */ ({D}"."){3}{D} RET_CONST(new zeek::AddrVal(yytext)) ({D}"."){3}{D}{OWS}"/"{OWS}{D} { int len = 0; std::string ip = zeek::util::detail::extract_ip_and_len(yytext, &len); RET_CONST(new zeek::SubNetVal(zeek::IPPrefix(zeek::IPAddr(ip), len))) } [!%*/+\-,:;<=>?()\[\]{}~$|&^] return yytext[0]; "--" return TOK_DECR; "++" return TOK_INCR; "+=" return TOK_ADD_TO; "-=" return TOK_REMOVE_FROM; "==" return TOK_EQ; "!=" return TOK_NE; ">=" return TOK_GE; "<=" return TOK_LE; "&&" return TOK_AND_AND; "||" return TOK_OR_OR; "<<" return TOK_LSHIFT; ">>" return TOK_RSHIFT; add return TOK_ADD; addr return TOK_ADDR; any return TOK_ANY; as return TOK_AS; assert return TOK_ASSERT; bool return TOK_BOOL; break return TOK_BREAK; case return TOK_CASE; option return TOK_OPTION; const return TOK_CONST; copy return TOK_COPY; count return TOK_COUNT; default return TOK_DEFAULT; delete return TOK_DELETE; double return TOK_DOUBLE; else return TOK_ELSE; enum return TOK_ENUM; event return TOK_EVENT; export return TOK_EXPORT; fallthrough return TOK_FALLTHROUGH; file return TOK_FILE; for return TOK_FOR; while return TOK_WHILE; function return TOK_FUNCTION; global return TOK_GLOBAL; "?$" return TOK_HAS_FIELD; hook return TOK_HOOK; if return TOK_IF; in return TOK_IN; "!"{OWS}in/[^A-Za-z0-9] return TOK_NOT_IN; /* don't confuse w "! infoo"! */ int return TOK_INT; interval return TOK_INTERVAL; is return TOK_IS; local return in_when_cond ? TOK_WHEN_LOCAL : TOK_LOCAL; module return TOK_MODULE; next return TOK_NEXT; of return TOK_OF; opaque return TOK_OPAQUE; pattern return TOK_PATTERN; port return TOK_PORT; print return TOK_PRINT; record return TOK_RECORD; redef return TOK_REDEF; return return TOK_RETURN; schedule return TOK_SCHEDULE; set return TOK_SET; string return TOK_STRING; subnet return TOK_SUBNET; switch return TOK_SWITCH; table return TOK_TABLE; time return TOK_TIME; timeout return TOK_TIMEOUT; type return TOK_TYPE; vector return TOK_VECTOR; when return TOK_WHEN; &add_func return TOK_ATTR_ADD_FUNC; &create_expire return TOK_ATTR_EXPIRE_CREATE; &default return TOK_ATTR_DEFAULT; &default_insert return TOK_ATTR_DEFAULT_INSERT; &delete_func return TOK_ATTR_DEL_FUNC; &deprecated return TOK_ATTR_DEPRECATED; &raw_output return TOK_ATTR_RAW_OUTPUT; &error_handler return TOK_ATTR_ERROR_HANDLER; &expire_func return TOK_ATTR_EXPIRE_FUNC; &group return TOK_ATTR_GROUP; &log return TOK_ATTR_LOG; &optional return TOK_ATTR_OPTIONAL; &is_assigned return TOK_ATTR_IS_ASSIGNED; &is_used return TOK_ATTR_IS_USED; &priority return TOK_ATTR_PRIORITY; &type_column return TOK_ATTR_TYPE_COLUMN; &read_expire return TOK_ATTR_EXPIRE_READ; &redef return TOK_ATTR_REDEF; &write_expire return TOK_ATTR_EXPIRE_WRITE; &on_change return TOK_ATTR_ON_CHANGE; &broker_store return TOK_ATTR_BROKER_STORE; &broker_allow_complex_type return TOK_ATTR_BROKER_STORE_ALLOW_COMPLEX; &backend return TOK_ATTR_BACKEND; &ordered return TOK_ATTR_ORDERED; &no_ZAM_opt return TOK_ATTR_NO_ZAM_OPT; &no_CPP_opt return TOK_ATTR_NO_CPP_OPT; @deprecated.* { auto num_files = file_stack.length(); auto comment = zeek::util::skip_whitespace(yytext + 11); std::string loaded_from; if ( num_files > 0 ) { auto lf = file_stack[num_files - 1]; if ( lf->name ) loaded_from = zeek::util::fmt(" from %s:%d", lf->name, lf->line); else loaded_from = " from command line arguments"; } zeek::reporter->Deprecation(zeek::util::fmt("deprecated script loaded%s %s", loaded_from.c_str(), comment)); } @pragma.* { do_pragma(zeek::util::skip_whitespace(yytext + 7)); } @DEBUG return TOK_DEBUG; // marks input for debugger @DIR { std::string rval = zeek::util::SafeDirname(::filename).result; if ( ! rval.empty() && rval[0] == '.' ) { char path[MAXPATHLEN]; if ( ! getcwd(path, MAXPATHLEN) ) zeek::reporter->InternalError("getcwd failed: %s", strerror(errno)); else rval = std::string(path) + "/" + rval; } RET_CONST(new zeek::StringVal(rval.c_str())); } @FILENAME { RET_CONST(new zeek::StringVal(zeek::util::SafeBasename(::filename).result)); } @load{WS}{FILE} { const char* new_file = zeek::util::skip_whitespace(yytext + 5); // Skip "@load". std::string loader = ::filename; // load_files may change ::filename, save copy std::string loading = find_relative_script_file(new_file); (void) load_files(new_file); zeek::detail::zeekygen_mgr->ScriptDependency(loader, loading); } @load-sigs{WS}{FILE} { const char* file = zeek::util::skip_whitespace(yytext + 10); std::string path = find_relative_file(file, ".sig"); sig_files.emplace_back(file, path, GetCurrentLocation()); } @load-plugin{WS}{ID} { const char* plugin = zeek::util::skip_whitespace(yytext + 12); std::pair> rc; rc.first = PLUGIN_HOOK_WITH_RESULT(HOOK_LOAD_FILE, HookLoadFile(zeek::plugin::Plugin::PLUGIN, plugin, ""), -1); if ( rc.first < 0 ) rc = PLUGIN_HOOK_WITH_RESULT(HOOK_LOAD_FILE_EXT, HookLoadFileExtended(zeek::plugin::Plugin::PLUGIN, plugin, ""), std::make_pair(-1, std::nullopt)); switch ( rc.first ) { case -1: { // No plugin took charge this @load-plugin directive. auto pre_load_input_files = input_files.size(); zeek::plugin_mgr->ActivateDynamicPlugin(plugin); // No new input files: Likely the plugin was already loaded // or has failed to load. if ( input_files.size() == pre_load_input_files ) break; // Lookup the plugin to get the path to the shared object. // We use that for the loaded_scripts.log and name of the // generated file loading the scripts. const zeek::plugin::Plugin *pp = nullptr; for ( const auto* p : zeek::plugin_mgr->ActivePlugins() ) { if ( p->DynamicPlugin() && p->Name() == plugin ) { pp = p; break; } } std::string name; if ( pp ) name = pp->PluginPath(); else { // This shouldn't happen. If it does, we come up // with an artificial filename rather than using // the shared object name. zeek::reporter->Warning("Did not find %s after loading", plugin); name = std::string("@load-plugin ") + plugin; } // Render all needed @load lines into a string std::string buf = "# @load-plugin generated script\n"; while ( input_files.size() > pre_load_input_files ) { // Any relative files found by the plugin manager are // converted to absolute paths relative to Zeek's working // directory. That way it is clear where these are supposed // to be found and find_relative_script_file() won't get // confused by any ZEEKPATH settings. Also, plugin files // containing any relative @loads themselves will work. std::error_code ec; auto canonical = std::filesystem::canonical(input_files[0]); if ( ec ) zeek::reporter->FatalError("plugin script %s not found: %s", input_files[0], ec.message().c_str()); buf += std::string("@load ") + canonical.string() + "\n"; delete[] input_files.remove_nth(0); } zeek::detail::zeekygen_mgr->Script(name); zeek::detail::ScannedFile sf(file_stack.length(), name, false /*skipped*/, true /*prefixes_checked*/, true /*is_canonical*/); zeek::detail::files_scanned.push_back(std::move(sf)); file_stack.push_back(new FileInfo(zeek::detail::current_module)); YY_BUFFER_STATE buffer = yy_scan_bytes(buf.data(), buf.size()); switch_to(name.c_str(), buffer); break; } case 0: if ( ! zeek::reporter->Errors() ) zeek::reporter->Error("Plugin reported error loading plugin %s", plugin); exit(1); break; case 1: // A plugin took care of it, just skip. break; default: assert(false); break; } } @unload{WS}{FILE} { // Skip "@unload". const char* file = zeek::util::skip_whitespace(yytext + 7); std::string path = find_relative_script_file(file); if ( ! path.empty() && zeek::util::is_dir(path.c_str()) ) { // open_package() appends __load__.zeek to path and verifies existence FILE *f = zeek::util::detail::open_package(path); if (f) fclose(f); else path = ""; } if ( path.empty() ) zeek::reporter->Error("failed find file associated with @unload %s", file); else { // All we have to do is pretend we've already scanned it. zeek::detail::ScannedFile sf(file_stack.length(), std::move(path), true); zeek::detail::files_scanned.push_back(std::move(sf)); } } @prefixes{WS}("+"?)={WS}{PREFIX} { char* pref = zeek::util::skip_whitespace(yytext + 9); // Skip "@prefixes". int append = 0; if ( *pref == '+' ) { append = 1; ++pref; } pref = zeek::util::skip_whitespace(pref + 1); // Skip over '='. if ( ! append ) zeek::detail::zeek_script_prefixes = { "" }; // don't delete the "" prefix zeek::util::tokenize_string(pref, ":", &zeek::detail::zeek_script_prefixes); } @if return TOK_ATIF; @ifdef return TOK_ATIFDEF; @ifndef return TOK_ATIFNDEF; @else return TOK_ATELSE; @endif do_atendif(); @if start_conditional(); @ifdef start_conditional(); @ifndef start_conditional(); @else return TOK_ATELSE; @endif return TOK_ATENDIF; [^@\r\n]+ /* eat */ . /* eat */ T RET_CONST(zeek::val_mgr->True()->Ref()) F RET_CONST(zeek::val_mgr->False()->Ref()) {ID} { yylval.str = zeek::util::copy_string(yytext); last_id_tok = yylval.str; return TOK_ID; } {GLOBAL_ID} { yylval.str = zeek::util::copy_string(yytext); last_id_tok = yylval.str; return TOK_GLOBAL_ID; } {D} { RET_CONST(zeek::val_mgr->Count(static_cast(strtoull(yytext, (char**) NULL, 10))).release()) } {FLOAT} RET_CONST(new zeek::DoubleVal(atof(yytext))) {D}"/tcp" { uint32_t p = atoi(yytext); if ( p > 65535 ) { zeek::reporter->Error("bad port number - %s", yytext); p = 0; } RET_CONST(zeek::val_mgr->Port(p, TRANSPORT_TCP)->Ref()) } {D}"/udp" { uint32_t p = atoi(yytext); if ( p > 65535 ) { zeek::reporter->Error("bad port number - %s", yytext); p = 0; } RET_CONST(zeek::val_mgr->Port(p, TRANSPORT_UDP)->Ref()) } {D}"/icmp" { uint32_t p = atoi(yytext); if ( p > 255 ) { zeek::reporter->Error("bad port number - %s", yytext); p = 0; } RET_CONST(zeek::val_mgr->Port(p, TRANSPORT_ICMP)->Ref()) } {D}"/unknown" { uint32_t p = atoi(yytext); if ( p > 255 ) { zeek::reporter->Error("bad port number - %s", yytext); p = 0; } RET_CONST(zeek::val_mgr->Port(p, TRANSPORT_UNKNOWN)->Ref()) } {FLOAT}{OWS}day(s?) RET_CONST(new zeek::IntervalVal(atof(yytext), zeek::Days)) {FLOAT}{OWS}hr(s?) RET_CONST(new zeek::IntervalVal(atof(yytext), zeek::Hours)) {FLOAT}{OWS}min(s?) RET_CONST(new zeek::IntervalVal(atof(yytext), zeek::Minutes)) {FLOAT}{OWS}sec(s?) RET_CONST(new zeek::IntervalVal(atof(yytext), zeek::Seconds)) {FLOAT}{OWS}msec(s?) RET_CONST(new zeek::IntervalVal(atof(yytext), zeek::Milliseconds)) {FLOAT}{OWS}usec(s?) RET_CONST(new zeek::IntervalVal(atof(yytext), zeek::Microseconds)) "0x"{HEX}+ RET_CONST(zeek::val_mgr->Count(static_cast(strtoull(yytext, 0, 16))).release()) ({H}".")+{HTLD} { zeek::TableValPtr result; std::string msg = zeek::util::fmt("DNS lookup of host literal '%s' is not supported. " "Replace with blocking_lookup_hostname().", yytext); zeek::reporter->Error("%s", msg.c_str()); } \"([^\\\r\\\n\"]|{ESCSEQ})*\" { const char* text = yytext; int len = strlen(text) + 1; int i = 0; char* s = new char[len]; // Skip leading quote. for ( ++text; *text; ++text ) { if ( *text == '\\' ) { ++text; // skip '\' s[i++] = zeek::util::detail::expand_escape(text); --text; // point to end of sequence } else { s[i++] = *text; if ( i >= len ) zeek::reporter->InternalError("bad string length computation"); } } // Get rid of trailing quote. if ( s[i-1] != '"' ) zeek::reporter->InternalError("string scanning confused"); s[i-1] = '\0'; RET_CONST(new zeek::StringVal(new zeek::String(1, (zeek::byte_vec) s, i-1))) } ([^/\\\r\\\n]|{ESCSEQ})+ { yylval.str = zeek::util::copy_string(yytext); return TOK_PATTERN_TEXT; } "/" { BEGIN(INITIAL); yylval.re_modes.ignore_case = false; yylval.re_modes.single_line = false; return TOK_PATTERN_END; } (\/[is]{0,2}) { BEGIN(INITIAL); if ( strlen(yytext) == 2 ) { yylval.re_modes.ignore_case = (yytext[1] == 'i'); yylval.re_modes.single_line = (yytext[1] == 's'); } else { if ( yytext[1] == yytext[2] ) zeek::reporter->Error("pattern has duplicate mode %c", yytext[1]); yylval.re_modes.ignore_case = (yytext[1] == 'i' || yytext[2] == 'i'); yylval.re_modes.single_line = (yytext[1] == 's' || yytext[2] == 's'); } return TOK_PATTERN_END; } \r?\n { zeek::reporter->Error("pattern not terminated before end of line"); } <*>. zeek::reporter->Error("unrecognized character: '%s'", zeek::util::get_escaped_string(yytext, false).data()); <> last_tok[0] = '\0'; return EOF; %% YYLTYPE zeek::detail::GetCurrentLocation() { static YYLTYPE currloc; currloc.SetFile(filename); currloc.SetLine(line_number); return currloc; } void zeek::detail::SetCurrentLocation(YYLTYPE currloc) { ::filename = currloc.FileName(); line_number = currloc.FirstLine(); } static int switch_to(const char* file, YY_BUFFER_STATE buffer) { yy_switch_to_buffer(buffer); line_number = 1; yylloc.SetLine(1); // Don't delete the old filename - it's pointed to by // every Obj created when parsing it. filename = zeek::util::copy_string(file); yylloc.SetFile(filename); current_file_has_conditionals = files_with_conditionals.count(filename) > 0; entry_cond_depth.push_back(conditional_depth); entry_pragma_stack_depth.push_back(pragma_stack.size()); return 1; } static int load_files(const char* orig_file) { std::string file_path = find_relative_script_file(orig_file); std::pair> rc = {-1, std::nullopt}; rc.first = PLUGIN_HOOK_WITH_RESULT(HOOK_LOAD_FILE, HookLoadFile(zeek::plugin::Plugin::SCRIPT, orig_file, file_path), -1); if ( rc.first < 0 ) rc = PLUGIN_HOOK_WITH_RESULT(HOOK_LOAD_FILE_EXT, HookLoadFileExtended(zeek::plugin::Plugin::SCRIPT, orig_file, file_path), std::make_pair(-1, std::nullopt)); if ( rc.first == 0 ) { if ( ! zeek::reporter->Errors() ) // This is just in case the plugin failed to report // the error itself, in which case we want to at // least tell the user that something went wrong. zeek::reporter->Error("Plugin reported error loading %s", orig_file); exit(1); } if ( rc.first == 1 && ! rc.second ) return 0; // A plugin took care of it, just skip. FILE* f = nullptr; if ( rc.first == -1 ) { if ( zeek::util::streq(orig_file, "-") ) { f = stdin; file_path = zeek::detail::ScannedFile::canonical_stdin_path; if ( zeek::detail::g_policy_debug ) { zeek::detail::debug_msg( "Warning: can't use debugger while reading policy from stdin; turning off debugging.\n"); zeek::detail::g_policy_debug = false; } } else { if ( file_path.empty() ) zeek::reporter->FatalError("can't find %s", orig_file); if ( zeek::util::is_dir(file_path.c_str()) ) f = zeek::util::detail::open_package(file_path); else f = zeek::util::open_file(file_path); if ( ! f ) zeek::reporter->FatalError("can't open %s", file_path.c_str()); } zeek::detail::ScannedFile sf(file_stack.length(), file_path); if ( sf.AlreadyScanned() ) { if ( rc.first == -1 && f != stdin ) fclose(f); return 0; } zeek::detail::files_scanned.push_back(std::move(sf)); } if ( zeek::detail::g_policy_debug && ! file_path.empty() ) { // Add the filename to the file mapping table (Debug.h). zeek::detail::Filemap* map = new zeek::detail::Filemap; zeek::detail::g_dbgfilemaps.emplace(file_path, map); LoadPolicyFileText(file_path.c_str(), rc.second); } // Remember where we were to restore the module scope in which // this @load was done when we're finished processing it. file_stack.push_back(new FileInfo(zeek::detail::current_module)); zeek::detail::zeekygen_mgr->Script(file_path); // "orig_file" could be an alias for yytext, which is ephemeral // and will be zapped after the yy_switch_to_buffer() below. YY_BUFFER_STATE buffer; if ( rc.first == 1 ) { // Parse code provided by plugin. assert(rc.second); DBG_LOG(zeek::DBG_SCRIPTS, "Loading %s from code supplied by plugin ", file_path.c_str()); buffer = yy_scan_bytes(rc.second->data(), rc.second->size()); // this copies the data } else { // Parse from file. assert(f); DBG_LOG(zeek::DBG_SCRIPTS, "Loading %s", file_path.c_str()); buffer = yy_create_buffer(f, YY_BUF_SIZE); } yy_switch_to_buffer(buffer); line_number = 1; yylloc.SetLine(1); return switch_to(file_path.c_str(), buffer); } void begin_RE() { BEGIN(RE); } class LocalNameFinder final : public zeek::detail::TraversalCallback { public: LocalNameFinder() = default; zeek::detail::TraversalCode PreExpr(const zeek::detail::Expr* expr) override { if ( expr->Tag() != EXPR_NAME ) return zeek::detail::TC_CONTINUE; const zeek::detail::NameExpr* name_expr = static_cast(expr); if ( name_expr->Id()->IsGlobal() ) return zeek::detail::TC_CONTINUE; local_names.push_back(name_expr); return zeek::detail::TC_CONTINUE; } std::vector local_names; }; static void set_ignoring(bool start_ignoring, std::string_view directive, std::optional value = std::nullopt) { if ( script_coverage_mgr.IsActive() ) { std::string text = std::string(directive); if ( value ) { text += " ( "; text += *value; text += " )"; } script_coverage_mgr.AddConditional(GetCurrentLocation(), text, ! start_ignoring); } if ( start_ignoring ) { if_stack.push_back(conditional_depth); BEGIN(IGNORE); } } static void resume_processing() { if_stack.pop_back(); BEGIN(INITIAL); } void do_atif(zeek::detail::Expr* expr) { start_conditional(); LocalNameFinder cb; expr->Traverse(&cb); zeek::ValPtr val; if ( cb.local_names.empty() ) val = expr->Eval(nullptr); else { for ( size_t i = 0; i < cb.local_names.size(); ++i ) cb.local_names[i]->Error("referencing a local name in @if"); } if ( ! val ) { expr->Error("invalid expression in @if"); return; } set_ignoring(! val->AsBool(), "@if", zeek::obj_desc_short(expr)); } void do_atifdef(const char* id) { start_conditional(); const auto& i = zeek::detail::lookup_ID(id, zeek::detail::current_module.c_str()); // If the ID didn't exist, look to see if this is a module name instead. bool found = (i != nullptr); if ( ! found ) found = (zeek::detail::module_names().count(id) != 0); set_ignoring(! found, "@ifdef", id); } void do_atifndef(const char* id) { start_conditional(); const auto& i = zeek::detail::lookup_ID(id, zeek::detail::current_module.c_str()); // If the ID didn't exist, look to see if this is a module name instead. bool found = (i != nullptr); if ( ! found ) found = (zeek::detail::module_names().count(id) != 0); set_ignoring(found, "@ifndef", id); } void do_atelse() { if ( conditional_depth == 0 ) zeek::reporter->Error("@else without @if..."); if ( ! if_stack.empty() && conditional_depth > if_stack.back() ) return; if ( YY_START == INITIAL ) set_ignoring(true, "@else"); else { set_ignoring(false, "@else"); resume_processing(); } } void do_atendif() { if ( conditional_depth <= entry_cond_depth.back() ) zeek::reporter->Error("unbalanced @if... @endif"); if ( ! if_stack.empty() && conditional_depth == if_stack.back() ) resume_processing(); --conditional_depth; } // If the given Stmt is a directive, trigger an error so that its usage is rejected. void reject_directive(zeek::detail::Stmt* s) { if ( s->Tag() == STMT_NULL ) if ( s->AsNullStmt()->IsDirective() ) zeek::reporter->Error("incorrect use of directive"); } void add_essential_input_file(const char* file) { if ( ! file ) zeek::reporter->InternalError("empty filename"); if ( ! filename ) (void)load_files(file); else essential_input_files.push_back(zeek::util::copy_string(file)); } void add_input_file(const char* file) { if ( ! file ) zeek::reporter->InternalError("empty filename"); if ( ! filename ) (void)load_files(file); else input_files.push_back(zeek::util::copy_string(file)); } void add_input_file_at_front(const char* file) { if ( ! file ) zeek::reporter->InternalError("empty filename"); if ( ! filename ) (void)load_files(file); else input_files.push_front(zeek::util::copy_string(file)); } void add_to_name_list(char* s, char delim, zeek::name_list& nl) { while ( s ) { char* s_delim = strchr(s, delim); if ( s_delim ) *s_delim = 0; nl.push_back(zeek::util::copy_string(s)); if ( s_delim ) s = s_delim + 1; else break; } } int yywrap() { if ( entry_cond_depth.size() > 0 ) { if ( conditional_depth > entry_cond_depth.back() ) zeek::reporter->FatalError("unbalanced @if... @endif"); entry_cond_depth.pop_back(); } if ( entry_pragma_stack_depth.size() > 0 ) { if ( pragma_stack.size() > entry_pragma_stack_depth.back() ) zeek::reporter->FatalError("unbalanced @pragma push pop in file"); entry_pragma_stack_depth.pop_back(); } last_filename = ::filename; if ( zeek::reporter->Errors() > 0 ) return 1; yy_delete_buffer(YY_CURRENT_BUFFER); if ( file_stack.length() > 0 ) delete file_stack.remove_nth(file_stack.length() - 1); if ( YY_CURRENT_BUFFER ) { // There's more on the stack to scan. current_file_has_conditionals = files_with_conditionals.count(::filename) > 0; return 0; } // Stack is now empty. while ( essential_input_files.length() > 0 || input_files.length() > 0 ) { zeek::name_list& files = essential_input_files.length() > 0 ? essential_input_files : input_files; if ( load_files(files[0]) ) { // Don't delete the filename - it's pointed to by // every Obj created when parsing it. (void)files.remove_nth(0); return 0; } // We already scanned the file. Pop it and try the next, // if any. (void)files.remove_nth(0); } // For each file scanned so far, and for each @prefix, look for a // prefixed and flattened version of the loaded file in ZEEKPATH. The // flattening involves taking the path in ZEEKPATH in which the // scanned file lives and replacing '/' path separators with a '.' If // the scanned file is "__load__.zeek", that part of the flattened // file name is discarded. If the prefix is non-empty, it gets placed // in front of the flattened path, separated with another '.' bool found_prefixed_files = false; for ( auto& scanned_file : zeek::detail::files_scanned ) { if ( scanned_file.skipped || scanned_file.prefixes_checked ) continue; scanned_file.prefixes_checked = true; // Prefixes are pushed onto a stack, so iterate backwards. for ( int i = zeek::detail::zeek_script_prefixes.size() - 1; i >= 0; --i ) { // Don't look at empty prefixes. if ( ! zeek::detail::zeek_script_prefixes[i][0] ) continue; std::string canon = zeek::util::detail::without_zeekpath_component(scanned_file.name); std::string flat = zeek::util::detail::flatten_script_name(canon, zeek::detail::zeek_script_prefixes[i]); std::string path = find_relative_script_file(flat); if ( ! path.empty() ) { add_input_file(path.c_str()); found_prefixed_files = true; } // printf("====== prefix search ======\n"); // printf("File : %s\n", scanned_file.name.c_str()); // printf("Canon : %s\n", canon.c_str()); // printf("Flat : %s\n", flat.c_str()); // printf("Found : %s\n", path.empty() ? "F" : "T"); // printf("===========================\n"); } } if ( found_prefixed_files ) return 0; // Add redef statements for any X=Y command line parameters. if ( ! zeek::detail::params.empty() ) { std::string policy; for ( const auto& pi : zeek::detail::params ) { auto p = pi.data(); while ( isalnum(*p) || *p == '_' || *p == ':' ) ++p; auto first_non_id_char = p - pi.data(); auto eq_idx = pi.find('=', first_non_id_char); // Omit the '=' from op just to make fmt string below clearer. auto op = pi.substr(first_non_id_char, eq_idx - first_non_id_char); auto id_str = pi.substr(0, first_non_id_char); auto val_str = pi.substr(eq_idx + 1); const auto& id = zeek::id::find(id_str); if ( ! id ) { zeek::reporter->Error("unknown identifier '%s' in command-line options", id_str.data()); continue; } // Interpret the value based on the identifier's type. // So far, that just means quoting the value for string types. const auto& type = id->GetType(); if ( ! type ) { zeek::reporter->Error( "can't set value of '%s' in command-line " "options: unknown type", id_str.data()); continue; } if ( val_str.empty() && ! zeek::IsString(type->Tag()) ) { zeek::reporter->Error( "must assign non-empty value to '%s' in " "command-line options", id_str.data()); continue; } auto use_quotes = zeek::IsString(type->Tag()); auto fmt_str = use_quotes ? "redef %s %s= \"%s\";" : "redef %s %s= %s;"; policy += zeek::util::fmt(fmt_str, id_str.data(), op.data(), val_str.data()); } zeek::detail::params.clear(); filename = ""; yylloc.SetFile(filename); yy_scan_string(policy.c_str()); return 0; } // If we got this far, then we ran out of files. Check if the user // specified additional code on the command line, if so, parse it. // Use a synthetic filename, and add an extra semicolon on its own // line (so that things like @load work), so that a semicolon is // not strictly necessary. if ( zeek::detail::command_line_policy ) { int tmp_len = strlen(zeek::detail::command_line_policy) + 32; char* tmp = new char[tmp_len]; snprintf(tmp, tmp_len, "%s\n;\n", zeek::detail::command_line_policy); filename = ""; yylloc.SetFile(filename); yy_scan_string(tmp); delete[] tmp; // Make sure we do not get here again: zeek::detail::command_line_policy = 0; return 0; } // Otherwise, we are done. return 1; } FileInfo::FileInfo(std::string arg_restore_module) { buffer_state = YY_CURRENT_BUFFER; restore_module = arg_restore_module; name = ::filename; line = ::line_number; } FileInfo::~FileInfo() { if ( yyin && yyin != stdin ) fclose(yyin); yy_switch_to_buffer(buffer_state); filename = name; line_number = line; yylloc.SetFile(filename); yylloc.SetLine(line); if ( restore_module != "" ) zeek::detail::current_module = restore_module; }