diff --git a/CHANGES b/CHANGES index 162e76f2a0..d85f0becb0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,17 @@ +6.0.0-dev.673 | 2023-05-26 14:55:40 -0700 + + * change base scripts to use run-time if's or @if ... &analyze (Vern Paxson, Corelight) + + * a number of BTests updated with @if ... &analyze (Vern Paxson, Corelight) + + * BTests for new @if ... &analyze functionality (Vern Paxson, Corelight) + + * "if ( ... ) &analyze" language feature (Vern Paxson, Corelight) + + * classes for tracking "@if (...) &analyze" notion of code being/not being "activated" (Vern Paxson, Corelight) + + * RemoveGlobal() method for Scope class + simplifying interfaces (Vern Paxson, Corelight) + 6.0.0-dev.662 | 2023-05-26 20:51:43 +0200 * Update ZeekJS submodule to 0.9.1 (Arne Welzel, Corelight) diff --git a/VERSION b/VERSION index ce9186b11e..16e313249f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.0.0-dev.664 +6.0.0-dev.673 diff --git a/doc b/doc index 5d73e4d3ef..d60aec3659 160000 --- a/doc +++ b/doc @@ -1 +1 @@ -Subproject commit 5d73e4d3ef7ad3399a9586ea356400c506e10926 +Subproject commit d60aec3659a8527530e7a5c0377f8df3b3396f78 diff --git a/scripts/base/files/x509/main.zeek b/scripts/base/files/x509/main.zeek index 3765ff03ae..4f9d013404 100644 --- a/scripts/base/files/x509/main.zeek +++ b/scripts/base/files/x509/main.zeek @@ -143,10 +143,8 @@ event zeek_init() &priority=5 Files::register_for_mime_type(Files::ANALYZER_SHA256, "application/x-x509-ca-cert"); Files::register_for_mime_type(Files::ANALYZER_SHA256, "application/pkix-cert"); -@if ( Cluster::is_enabled() ) - if ( known_log_certs_use_broker ) + if ( Cluster::is_enabled() && known_log_certs_use_broker ) known_log_certs = known_log_certs_with_broker; -@endif } hook Files::log_policy(rec: Files::Info, id: Log::ID, filter: Log::Filter) &priority=5 diff --git a/scripts/base/frameworks/cluster/broker-stores.zeek b/scripts/base/frameworks/cluster/broker-stores.zeek index 19b4a18bce..519b1a9413 100644 --- a/scripts/base/frameworks/cluster/broker-stores.zeek +++ b/scripts/base/frameworks/cluster/broker-stores.zeek @@ -18,11 +18,11 @@ export { # If we are not the manager, disable automatically generating masters. We will attach # clones instead. -@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER ) +@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER ) &analyze redef Broker::table_store_master = F; @endif -@if ( Broker::table_store_master ) +@if ( Broker::table_store_master ) &analyze global broker_backed_ids: set[string]; diff --git a/scripts/base/frameworks/cluster/main.zeek b/scripts/base/frameworks/cluster/main.zeek index 495885c431..8afab1d8f7 100644 --- a/scripts/base/frameworks/cluster/main.zeek +++ b/scripts/base/frameworks/cluster/main.zeek @@ -347,7 +347,7 @@ function nodeid_topic(id: string): string return nodeid_topic_prefix + id + "/"; } -@if ( Cluster::is_enabled() ) +@if ( Cluster::is_enabled() ) &analyze event Cluster::hello(name: string, id: string) &priority=10 { @@ -383,9 +383,6 @@ event Cluster::hello(name: string, id: string) &priority=10 event Broker::peer_added(endpoint: Broker::EndpointInfo, msg: string) &priority=10 { - if ( ! Cluster::is_enabled() ) - return; - local e = Broker::make_event(Cluster::hello, node, Broker::node_id()); Broker::publish(nodeid_topic(endpoint$id), e); } diff --git a/scripts/base/frameworks/cluster/nodes/logger.zeek b/scripts/base/frameworks/cluster/nodes/logger.zeek index 9dedac27a0..19a3408d77 100644 --- a/scripts/base/frameworks/cluster/nodes/logger.zeek +++ b/scripts/base/frameworks/cluster/nodes/logger.zeek @@ -64,7 +64,7 @@ function archiver_rotation_format_func(ri: Log::RotationFmtInfo): Log::RotationP return rval; } -@if ( Supervisor::is_supervised() ) +@if ( Supervisor::is_supervised() ) &analyze redef Log::default_rotation_dir = "log-queue"; diff --git a/scripts/base/frameworks/cluster/nodes/manager.zeek b/scripts/base/frameworks/cluster/nodes/manager.zeek index 7504575dfc..abd158c82a 100644 --- a/scripts/base/frameworks/cluster/nodes/manager.zeek +++ b/scripts/base/frameworks/cluster/nodes/manager.zeek @@ -19,7 +19,7 @@ redef Log::enable_remote_logging = T; ## Log rotation interval. redef Log::default_rotation_interval = 24 hrs; -@if ( ! Supervisor::is_supervised() ) +@if ( ! Supervisor::is_supervised() ) &analyze ## Use the cluster's delete-log script. redef Log::default_rotation_postprocessor_cmd = "delete-log"; @endif diff --git a/scripts/base/frameworks/cluster/nodes/proxy.zeek b/scripts/base/frameworks/cluster/nodes/proxy.zeek index 1e01a865c8..646f396869 100644 --- a/scripts/base/frameworks/cluster/nodes/proxy.zeek +++ b/scripts/base/frameworks/cluster/nodes/proxy.zeek @@ -13,7 +13,7 @@ redef Log::enable_remote_logging = T; redef Log::default_rotation_interval = 24hrs; -@if ( ! Supervisor::is_supervised() ) +@if ( ! Supervisor::is_supervised() ) &analyze ## Use the cluster's delete-log script. redef Log::default_rotation_postprocessor_cmd = "delete-log"; @endif diff --git a/scripts/base/frameworks/cluster/nodes/worker.zeek b/scripts/base/frameworks/cluster/nodes/worker.zeek index 73c1edfb6d..2e828b443e 100644 --- a/scripts/base/frameworks/cluster/nodes/worker.zeek +++ b/scripts/base/frameworks/cluster/nodes/worker.zeek @@ -12,7 +12,7 @@ redef Log::enable_remote_logging = T; redef Log::default_rotation_interval = 24hrs; -@if ( ! Supervisor::is_supervised() ) +@if ( ! Supervisor::is_supervised() ) &analyze ## Use the cluster's delete-log script. redef Log::default_rotation_postprocessor_cmd = "delete-log"; @endif diff --git a/scripts/base/frameworks/config/main.zeek b/scripts/base/frameworks/config/main.zeek index f1c26ebe26..a043e9ed6f 100644 --- a/scripts/base/frameworks/config/main.zeek +++ b/scripts/base/frameworks/config/main.zeek @@ -47,7 +47,6 @@ export { global set_value: function(ID: string, val: any, location: string &default = ""): bool; } -@if ( Cluster::is_enabled() ) type OptionCacheValue: record { val: any; location: string; @@ -57,18 +56,21 @@ global option_cache: table[string] of OptionCacheValue; global Config::cluster_set_option: event(ID: string, val: any, location: string); -function broadcast_option(ID: string, val: any, location: string) &is_used +@if ( Cluster::is_enabled() ) &analyze + +function broadcast_option(ID: string, val: any, location: string) { for ( topic in Cluster::broadcast_topics ) Broker::publish(topic, Config::cluster_set_option, ID, val, location); } -event Config::cluster_set_option(ID: string, val: any, location: string) +event Config::cluster_set_option(ID: string, val: any, location: string) &is_used { -@if ( Cluster::local_node_type() == Cluster::MANAGER ) - option_cache[ID] = OptionCacheValue($val=val, $location=location); - broadcast_option(ID, val, location); -@endif + if ( Cluster::local_node_type() == Cluster::MANAGER ) + { + option_cache[ID] = OptionCacheValue($val=val, $location=location); + broadcast_option(ID, val, location); + } Option::set(ID, val, location); } @@ -85,13 +87,14 @@ function set_value(ID: string, val: any, location: string &default = ""): bool if ( ! Option::set(ID, val, location) ) return F; -@if ( Cluster::local_node_type() == Cluster::MANAGER ) - option_cache[ID] = OptionCacheValue($val=val, $location=location); - broadcast_option(ID, val, location); -@else - Broker::publish(Cluster::manager_topic, Config::cluster_set_option, - ID, val, location); -@endif + if ( Cluster::local_node_type() == Cluster::MANAGER ) + { + option_cache[ID] = OptionCacheValue($val=val, $location=location); + broadcast_option(ID, val, location); + } + else + Broker::publish(Cluster::manager_topic, Config::cluster_set_option, + ID, val, location); return T; } @@ -102,7 +105,7 @@ function set_value(ID: string, val: any, location: string &default = ""): bool } @endif # Cluster::is_enabled -@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) +@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) &analyze # Handling of new worker nodes. event Cluster::node_up(name: string, id: string) &priority=-10 { @@ -156,10 +159,9 @@ event zeek_init() &priority=10 Log::create_stream(LOG, [$columns=Info, $ev=log_config, $path="config", $policy=log_policy]); # Limit logging to the manager - everyone else just feeds off it. -@if ( !Cluster::is_enabled() || Cluster::local_node_type() == Cluster::MANAGER ) - # Iterate over all existing options and add ourselves as change handlers - # with a low priority so that we can log the changes. - for ( opt in global_options() ) - Option::set_change_handler(opt, config_option_changed, -100); -@endif + if ( !Cluster::is_enabled() || Cluster::local_node_type() == Cluster::MANAGER ) + # Iterate over all existing options and add ourselves as change handlers + # with a low priority so that we can log the changes. + for ( opt in global_options() ) + Option::set_change_handler(opt, config_option_changed, -100); } diff --git a/scripts/base/frameworks/intel/cluster.zeek b/scripts/base/frameworks/intel/cluster.zeek index d890e455f9..18bea47e52 100644 --- a/scripts/base/frameworks/intel/cluster.zeek +++ b/scripts/base/frameworks/intel/cluster.zeek @@ -17,11 +17,11 @@ global insert_indicator: event(item: Item) &is_used; const send_store_on_node_up = T &redef; # If this process is not a manager process, we don't want the full metadata. -@if ( Cluster::local_node_type() != Cluster::MANAGER ) +@if ( Cluster::local_node_type() != Cluster::MANAGER ) &analyze redef have_full_data = F; @endif -@if ( Cluster::local_node_type() == Cluster::MANAGER ) +@if ( Cluster::local_node_type() == Cluster::MANAGER ) &analyze event zeek_init() { Broker::auto_publish(Cluster::worker_topic, remove_indicator); @@ -73,7 +73,7 @@ event Intel::match_remote(s: Seen) &priority=5 } @endif -@if ( Cluster::local_node_type() == Cluster::WORKER ) +@if ( Cluster::local_node_type() == Cluster::WORKER ) &analyze event zeek_init() { Broker::auto_publish(Cluster::manager_topic, match_remote); @@ -94,7 +94,7 @@ event Intel::insert_indicator(item: Intel::Item) &priority=5 } @endif -@if ( Cluster::local_node_type() == Cluster::PROXY ) +@if ( Cluster::local_node_type() == Cluster::PROXY ) &analyze event Intel::insert_indicator(item: Intel::Item) &priority=5 { # Just forwarding from manager to workers. diff --git a/scripts/base/frameworks/netcontrol/cluster.zeek b/scripts/base/frameworks/netcontrol/cluster.zeek index a79e03cd95..d0ee1c7f43 100644 --- a/scripts/base/frameworks/netcontrol/cluster.zeek +++ b/scripts/base/frameworks/netcontrol/cluster.zeek @@ -16,7 +16,7 @@ export { global cluster_netcontrol_delete_rule: event(id: string, reason: string); } -@if ( Cluster::local_node_type() == Cluster::MANAGER ) +@if ( Cluster::local_node_type() == Cluster::MANAGER ) &analyze event zeek_init() { Broker::auto_publish(Cluster::worker_topic, NetControl::rule_added); @@ -93,7 +93,7 @@ function remove_rule(id: string, reason: string &default="") : bool } } -@if ( Cluster::local_node_type() == Cluster::MANAGER ) +@if ( Cluster::local_node_type() == Cluster::MANAGER ) &analyze event NetControl::cluster_netcontrol_delete_rule(id: string, reason: string) { delete_rule_impl(id, reason); @@ -147,7 +147,7 @@ event rule_error(r: Rule, p: PluginState, msg: string) &priority=-5 @endif # Workers use the events to keep track in their local state tables -@if ( Cluster::local_node_type() != Cluster::MANAGER ) +@if ( Cluster::local_node_type() != Cluster::MANAGER ) &analyze event rule_new(r: Rule) &priority=5 { diff --git a/scripts/base/frameworks/notice/actions/pp-alarms.zeek b/scripts/base/frameworks/notice/actions/pp-alarms.zeek index 954ee359a1..5dc04c4748 100644 --- a/scripts/base/frameworks/notice/actions/pp-alarms.zeek +++ b/scripts/base/frameworks/notice/actions/pp-alarms.zeek @@ -153,16 +153,17 @@ function pretty_print_alarm(out: file, n: Info) { local pdescr = ""; -@if ( Cluster::is_enabled() ) - pdescr = "local"; + if ( Cluster::is_enabled() ) + { + pdescr = "local"; - if ( n?$peer_descr ) - pdescr = n$peer_descr; - else if ( n?$peer_name ) - pdescr = n$peer_name; + if ( n?$peer_descr ) + pdescr = n$peer_descr; + else if ( n?$peer_name ) + pdescr = n$peer_name; - pdescr = fmt("<%s> ", pdescr); -@endif + pdescr = fmt("<%s> ", pdescr); + } local msg = fmt( "%s%s", pdescr, n$msg); diff --git a/scripts/base/frameworks/notice/main.zeek b/scripts/base/frameworks/notice/main.zeek index 85830ae499..231e87602d 100644 --- a/scripts/base/frameworks/notice/main.zeek +++ b/scripts/base/frameworks/notice/main.zeek @@ -539,9 +539,9 @@ hook Notice::notice(n: Notice::Info) &priority=-5 { event Notice::begin_suppression(n$ts, n$suppress_for, n$note, n$identifier); suppressing[n$note, n$identifier] = n$ts + n$suppress_for; -@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER ) - event Notice::manager_begin_suppression(n$ts, n$suppress_for, n$note, n$identifier); -@endif + + if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER ) + event Notice::manager_begin_suppression(n$ts, n$suppress_for, n$note, n$identifier); } } @@ -552,7 +552,7 @@ event Notice::begin_suppression(ts: time, suppress_for: interval, note: Type, suppressing[note, identifier] = suppress_until; } -@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) +@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) &analyze event zeek_init() { Broker::auto_publish(Cluster::worker_topic, Notice::begin_suppression); @@ -566,7 +566,7 @@ event Notice::manager_begin_suppression(ts: time, suppress_for: interval, note: } @endif -@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER ) +@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER ) &analyze event zeek_init() { Broker::auto_publish(Cluster::manager_topic, Notice::manager_begin_suppression); @@ -644,13 +644,14 @@ function apply_policy(n: Notice::Info) if ( ! n?$ts ) n$ts = network_time(); -@if ( Cluster::is_enabled() ) - if ( ! n?$peer_name ) - n$peer_name = Cluster::node; + if ( Cluster::is_enabled() ) + { + if ( ! n?$peer_name ) + n$peer_name = Cluster::node; - if ( ! n?$peer_descr ) - n$peer_descr = Cluster::node; -@endif + if ( ! n?$peer_descr ) + n$peer_descr = Cluster::node; + } if ( n?$f ) populate_file_info(n$f, n); diff --git a/scripts/base/frameworks/openflow/cluster.zeek b/scripts/base/frameworks/openflow/cluster.zeek index a16539c281..afba73095e 100644 --- a/scripts/base/frameworks/openflow/cluster.zeek +++ b/scripts/base/frameworks/openflow/cluster.zeek @@ -13,7 +13,7 @@ export { global cluster_flow_clear: event(name: string); } -@if ( Cluster::local_node_type() != Cluster::MANAGER ) +@if ( Cluster::local_node_type() != Cluster::MANAGER ) &analyze # Workers need ability to forward commands to manager. event zeek_init() { @@ -49,7 +49,7 @@ function flow_clear(controller: Controller): bool return T; } -@if ( Cluster::local_node_type() == Cluster::MANAGER ) +@if ( Cluster::local_node_type() == Cluster::MANAGER ) &analyze event OpenFlow::cluster_flow_mod(name: string, match: ofp_match, flow_mod: ofp_flow_mod) { if ( name !in name_to_controller ) diff --git a/scripts/base/frameworks/software/main.zeek b/scripts/base/frameworks/software/main.zeek index 7e7631a4f3..2dfc7b25c2 100644 --- a/scripts/base/frameworks/software/main.zeek +++ b/scripts/base/frameworks/software/main.zeek @@ -544,11 +544,10 @@ function found(id: conn_id, info: Info): bool return F; } - @if ( Cluster::is_enabled() ) + if ( Cluster::is_enabled() ) Cluster::publish_hrw(Cluster::proxy_pool, info$host, Software::new, info); - @else + else event Software::new(info); - @endif return T; } diff --git a/scripts/base/frameworks/sumstats/cluster.zeek b/scripts/base/frameworks/sumstats/cluster.zeek index 5a975c5ee9..5014336003 100644 --- a/scripts/base/frameworks/sumstats/cluster.zeek +++ b/scripts/base/frameworks/sumstats/cluster.zeek @@ -59,7 +59,7 @@ export { # intermediate updates so they don't overwhelm the manager. global recent_global_view_keys: set[string, Key] &create_expire=1min; -@if ( Cluster::local_node_type() != Cluster::MANAGER ) +@if ( Cluster::local_node_type() != Cluster::MANAGER ) &analyze event zeek_init() &priority=100 { @@ -207,7 +207,7 @@ function request_key(ss_name: string, key: Key): Result @endif -@if ( Cluster::local_node_type() == Cluster::MANAGER ) +@if ( Cluster::local_node_type() == Cluster::MANAGER ) &analyze event zeek_init() &priority=100 { diff --git a/scripts/base/frameworks/telemetry/cluster.zeek b/scripts/base/frameworks/telemetry/cluster.zeek index a589f0ddc5..ec23c3882c 100644 --- a/scripts/base/frameworks/telemetry/cluster.zeek +++ b/scripts/base/frameworks/telemetry/cluster.zeek @@ -12,7 +12,7 @@ redef Broker::metrics_export_endpoint_name = Cluster::node; # The manager opens port 9911 and imports metrics from all nodes by default. -@if ( Cluster::local_node_type() == Cluster::MANAGER ) +@if ( Cluster::local_node_type() == Cluster::MANAGER ) &analyze redef Broker::metrics_port = 9911/tcp; redef Broker::metrics_import_topics = vector("zeek/cluster/metrics/"); diff --git a/scripts/base/misc/find-filtered-trace.zeek b/scripts/base/misc/find-filtered-trace.zeek index 511db74b10..c7ecc49417 100644 --- a/scripts/base/misc/find-filtered-trace.zeek +++ b/scripts/base/misc/find-filtered-trace.zeek @@ -28,7 +28,7 @@ function should_detect(): bool &is_used return F; } -@if ( should_detect() ) +@if ( should_detect() ) &analyze global saw_tcp_conn_with_data: bool = F; global saw_a_tcp_conn: bool = F; diff --git a/scripts/base/protocols/dhcp/main.zeek b/scripts/base/protocols/dhcp/main.zeek index 6bbd5d13e2..f62a195aca 100644 --- a/scripts/base/protocols/dhcp/main.zeek +++ b/scripts/base/protocols/dhcp/main.zeek @@ -134,7 +134,7 @@ event zeek_init() &priority=5 Analyzer::register_for_ports(Analyzer::ANALYZER_DHCP, ports); } -@if ( Cluster::is_enabled() ) +@if ( Cluster::is_enabled() ) &analyze event zeek_init() { Broker::auto_publish(Cluster::manager_topic, DHCP::aggregate_msgs); @@ -180,7 +180,7 @@ global join_data: table[count] of Info = table() -@if ( ! Cluster::is_enabled() || Cluster::local_node_type() == Cluster::MANAGER ) +@if ( ! Cluster::is_enabled() || Cluster::local_node_type() == Cluster::MANAGER ) &analyze # We are handling this event at priority 1000 because we really want # the DHCP::log_info global to be set correctly before a user might try # to access it. diff --git a/scripts/base/protocols/ftp/main.zeek b/scripts/base/protocols/ftp/main.zeek index 804f3eb11b..877fd9192c 100644 --- a/scripts/base/protocols/ftp/main.zeek +++ b/scripts/base/protocols/ftp/main.zeek @@ -216,27 +216,30 @@ function ftp_message(c: connection) delete s$data_channel; } +const cluster_is_enabled = Cluster::is_enabled(); +const should_publish = + Cluster::local_node_type() == Cluster::PROXY || + Cluster::local_node_type() == Cluster::MANAGER; + event sync_add_expected_data(s: Info, chan: ExpectedDataChannel) &is_used { -@if ( Cluster::local_node_type() == Cluster::PROXY || - Cluster::local_node_type() == Cluster::MANAGER ) - Broker::publish(Cluster::worker_topic, sync_add_expected_data, minimize_info(s), chan); -@else - ftp_data_expected[chan$resp_h, chan$resp_p] = s; - Analyzer::schedule_analyzer(chan$orig_h, chan$resp_h, chan$resp_p, - Analyzer::ANALYZER_FTP_DATA, - 5mins); -@endif + if ( should_publish ) + Broker::publish(Cluster::worker_topic, sync_add_expected_data, minimize_info(s), chan); + else + { + ftp_data_expected[chan$resp_h, chan$resp_p] = s; + Analyzer::schedule_analyzer(chan$orig_h, chan$resp_h, chan$resp_p, + Analyzer::ANALYZER_FTP_DATA, + 5mins); + } } event sync_remove_expected_data(resp_h: addr, resp_p: port) &is_used { -@if ( Cluster::local_node_type() == Cluster::PROXY || - Cluster::local_node_type() == Cluster::MANAGER ) - Broker::publish(Cluster::worker_topic, sync_remove_expected_data, resp_h, resp_p); -@else - delete ftp_data_expected[resp_h, resp_p]; -@endif + if ( should_publish ) + Broker::publish(Cluster::worker_topic, sync_remove_expected_data, resp_h, resp_p); + else + delete ftp_data_expected[resp_h, resp_p]; } function add_expected_data_channel(s: Info, chan: ExpectedDataChannel) @@ -247,9 +250,8 @@ function add_expected_data_channel(s: Info, chan: ExpectedDataChannel) Analyzer::schedule_analyzer(chan$orig_h, chan$resp_h, chan$resp_p, Analyzer::ANALYZER_FTP_DATA, 5mins); -@if ( Cluster::is_enabled() ) - Broker::publish(ftp_relay_topic(), sync_add_expected_data, minimize_info(s), chan); -@endif + if ( cluster_is_enabled ) + Broker::publish(ftp_relay_topic(), sync_add_expected_data, minimize_info(s), chan); } event ftp_request(c: connection, command: string, arg: string) &priority=5 @@ -464,9 +466,8 @@ hook finalize_ftp_data(c: connection) if ( [c$id$resp_h, c$id$resp_p] in ftp_data_expected ) { delete ftp_data_expected[c$id$resp_h, c$id$resp_p]; -@if ( Cluster::is_enabled() ) - Broker::publish(ftp_relay_topic(), sync_remove_expected_data, c$id$resp_h, c$id$resp_p); -@endif + if ( cluster_is_enabled ) + Broker::publish(ftp_relay_topic(), sync_remove_expected_data, c$id$resp_h, c$id$resp_p); } } diff --git a/scripts/base/protocols/irc/dcc-send.zeek b/scripts/base/protocols/irc/dcc-send.zeek index 34e097b645..64669b07df 100644 --- a/scripts/base/protocols/irc/dcc-send.zeek +++ b/scripts/base/protocols/irc/dcc-send.zeek @@ -44,26 +44,29 @@ function dcc_relay_topic(): string &is_used return rval; } +const cluster_is_enabled = Cluster::is_enabled(); +const should_publish = + Cluster::local_node_type() == Cluster::PROXY || + Cluster::local_node_type() == Cluster::MANAGER; + event dcc_transfer_add(host: addr, p: port, info: Info) &is_used { -@if ( Cluster::local_node_type() == Cluster::PROXY || - Cluster::local_node_type() == Cluster::MANAGER ) - Broker::publish(Cluster::worker_topic, dcc_transfer_add, host, p, info); -@else - dcc_expected_transfers[host, p] = info; - Analyzer::schedule_analyzer(0.0.0.0, host, p, - Analyzer::ANALYZER_IRC_DATA, 5 min); -@endif + if ( should_publish ) + Broker::publish(Cluster::worker_topic, dcc_transfer_add, host, p, info); + else + { + dcc_expected_transfers[host, p] = info; + Analyzer::schedule_analyzer(0.0.0.0, host, p, + Analyzer::ANALYZER_IRC_DATA, 5 min); + } } event dcc_transfer_remove(host: addr, p: port) &is_used { -@if ( Cluster::local_node_type() == Cluster::PROXY || - Cluster::local_node_type() == Cluster::MANAGER ) - Broker::publish(Cluster::worker_topic, dcc_transfer_remove, host, p); -@else - delete dcc_expected_transfers[host, p]; -@endif + if ( should_publish ) + Broker::publish(Cluster::worker_topic, dcc_transfer_remove, host, p); + else + delete dcc_expected_transfers[host, p]; } function log_dcc(f: fa_file) @@ -89,10 +92,9 @@ function log_dcc(f: fa_file) delete dcc_expected_transfers[cid$resp_h, cid$resp_p]; -@if ( Cluster::is_enabled() ) - Broker::publish(dcc_relay_topic(), dcc_transfer_remove, - cid$resp_h, cid$resp_p); -@endif + if ( cluster_is_enabled ) + Broker::publish(dcc_relay_topic(), dcc_transfer_remove, + cid$resp_h, cid$resp_p); return; } } @@ -117,9 +119,8 @@ event irc_dcc_message(c: connection, is_orig: bool, Analyzer::schedule_analyzer(0.0.0.0, address, p, Analyzer::ANALYZER_IRC_DATA, 5 min); dcc_expected_transfers[address, p] = c$irc; -@if ( Cluster::is_enabled() ) - Broker::publish(dcc_relay_topic(), dcc_transfer_add, address, p, c$irc); -@endif + if ( cluster_is_enabled ) + Broker::publish(dcc_relay_topic(), dcc_transfer_add, address, p, c$irc); } event scheduled_analyzer_applied(c: connection, a: Analyzer::Tag) &priority=10 @@ -138,9 +139,8 @@ hook finalize_irc_data(c: connection) { delete dcc_expected_transfers[c$id$resp_h, c$id$resp_p]; -@if ( Cluster::is_enabled() ) - Broker::publish(dcc_relay_topic(), dcc_transfer_remove, - c$id$resp_h, c$id$resp_p); -@endif + if ( cluster_is_enabled ) + Broker::publish(dcc_relay_topic(), dcc_transfer_remove, + c$id$resp_h, c$id$resp_p); } } diff --git a/scripts/policy/frameworks/management/persistence.zeek b/scripts/policy/frameworks/management/persistence.zeek index bb5731ced0..ac61be2b4c 100644 --- a/scripts/policy/frameworks/management/persistence.zeek +++ b/scripts/policy/frameworks/management/persistence.zeek @@ -8,7 +8,7 @@ # For testing, keep persistent state local to the current working directory, # and disable log rotation. -@if ( getenv("ZEEK_MANAGEMENT_TESTING") != "" ) +@if ( getenv("ZEEK_MANAGEMENT_TESTING") != "" ) &analyze redef Management::spool_dir = "."; redef Management::state_dir = "."; @@ -21,7 +21,7 @@ redef Log::default_rotation_interval = 0 secs; # config with the Supervisor; see base/frameworks/cluster/nodes/logger.zeek. redef Log::default_rotation_dir = build_path(Management::get_spool_dir(), "log-queue"); -@if ( getenv("ZEEK_MANAGEMENT_NODE") != "" ) +@if ( getenv("ZEEK_MANAGEMENT_NODE") != "" ) &analyze # Management agents and controllers don't have loggers, nor their configuration, # so establish a similar one here: diff --git a/scripts/policy/frameworks/netcontrol/catch-and-release.zeek b/scripts/policy/frameworks/netcontrol/catch-and-release.zeek index fed7486fd1..b069706323 100644 --- a/scripts/policy/frameworks/netcontrol/catch-and-release.zeek +++ b/scripts/policy/frameworks/netcontrol/catch-and-release.zeek @@ -198,21 +198,25 @@ function populate_log_record(ip: addr, bi: BlockInfo, action: CatchReleaseAction return log; } +const cluster_is_enabled = Cluster::is_enabled(); +const is_mgr = cluster_is_enabled && Cluster::local_node_type() == Cluster::MANAGER; +const is_not_mgr = cluster_is_enabled && Cluster::local_node_type() != Cluster::MANAGER; + +const single_enforcement_point = ! cluster_is_enabled || is_mgr; + function per_block_interval(t: table[addr] of BlockInfo, idx: addr): interval { local remaining_time = t[idx]$watch_until - network_time(); if ( remaining_time < 0secs ) remaining_time = 0secs; -@if ( ! Cluster::is_enabled() || ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) ) - if ( remaining_time == 0secs ) + if ( single_enforcement_point && remaining_time == 0 secs ) { local log = populate_log_record(idx, t[idx], FORGOTTEN); Log::write(CATCH_RELEASE, log); event NetControl::catch_release_forgotten(idx, t[idx]); } -@endif return remaining_time; } @@ -225,9 +229,9 @@ global blocks: table[addr] of BlockInfo = {} &expire_func=per_block_interval; -@if ( Cluster::is_enabled() ) +@if ( cluster_is_enabled ) &analyze -@if ( Cluster::local_node_type() == Cluster::MANAGER ) +@if ( is_mgr ) &analyze event zeek_init() { Broker::auto_publish(Cluster::worker_topic, NetControl::catch_release_block_new); @@ -259,7 +263,7 @@ function cr_check_rule(r: Rule): bool return F; } -@if ( ! Cluster::is_enabled() || ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) ) +@if ( single_enforcement_point ) &analyze event rule_added(r: Rule, p: PluginState, msg: string) { @@ -297,7 +301,7 @@ event rule_timeout(r: Rule, i: FlowInfo, p: PluginState) @endif -@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) +@if ( is_mgr ) &analyze event catch_release_add(a: addr, location: string) { drop_address_catch_release(a, location); @@ -314,7 +318,7 @@ event catch_release_encountered(a: addr) } @endif -@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER ) +@if ( is_not_mgr ) &analyze event catch_release_block_new(a: addr, b: BlockInfo) { blocks[a] = b; @@ -327,7 +331,7 @@ event catch_release_block_delete(a: addr) } @endif -@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) +@if ( is_mgr ) &analyze @endif function get_catch_release_info(a: addr): BlockInfo @@ -360,46 +364,50 @@ function drop_address_catch_release(a: addr, location: string &default=""): Bloc bi = BlockInfo($watch_until=network_time()+catch_release_intervals[1], $current_interval=0, $current_block_id=r$id); if ( location != "" ) bi$location = location; -@if ( ! Cluster::is_enabled() || ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) ) - log = populate_log_record(a, bi, ADDED); - log$message = "Address already blocked outside of catch-and-release. Catch and release will monitor and only actively block if it appears in network traffic."; - Log::write(CATCH_RELEASE, log); - blocks[a] = bi; - event NetControl::catch_release_block_new(a, bi); -@endif -@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER ) - event NetControl::catch_release_add(a, location); -@endif + + if ( single_enforcement_point ) + { + log = populate_log_record(a, bi, ADDED); + log$message = "Address already blocked outside of catch-and-release. Catch and release will monitor and only actively block if it appears in network traffic."; + Log::write(CATCH_RELEASE, log); + blocks[a] = bi; + event NetControl::catch_release_block_new(a, bi); + } + + if ( is_not_mgr ) + event NetControl::catch_release_add(a, location); + return bi; } local block_interval = catch_release_intervals[0]; -@if ( ! Cluster::is_enabled() || ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) ) - local ret = drop_address(a, block_interval, location); - - if ( ret != "" ) + if ( single_enforcement_point ) { - bi = BlockInfo($watch_until=network_time()+catch_release_intervals[1], $block_until=network_time()+block_interval, $current_interval=0, $current_block_id=ret); - if ( location != "" ) - bi$location = location; - blocks[a] = bi; - event NetControl::catch_release_block_new(a, bi); - blocks[a] = bi; - log = populate_log_record(a, bi, DROP_REQUESTED); - Log::write(CATCH_RELEASE, log); + local ret = drop_address(a, block_interval, location); + + if ( ret != "" ) + { + bi = BlockInfo($watch_until=network_time()+catch_release_intervals[1], $block_until=network_time()+block_interval, $current_interval=0, $current_block_id=ret); + if ( location != "" ) + bi$location = location; + blocks[a] = bi; + event NetControl::catch_release_block_new(a, bi); + blocks[a] = bi; + log = populate_log_record(a, bi, DROP_REQUESTED); + Log::write(CATCH_RELEASE, log); + return bi; + } + Reporter::error(fmt("Catch and release could not add block for %s; failing.", a)); + return BlockInfo($watch_until=double_to_time(0), $current_interval=0, $current_block_id=""); + } + + if ( is_not_mgr ) + { + bi = BlockInfo($watch_until=network_time()+catch_release_intervals[1], $block_until=network_time()+block_interval, $current_interval=0, $current_block_id=""); + event NetControl::catch_release_add(a, location); return bi; } - Reporter::error(fmt("Catch and release could not add block for %s; failing.", a)); - return BlockInfo($watch_until=double_to_time(0), $current_interval=0, $current_block_id=""); -@endif - -@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER ) - bi = BlockInfo($watch_until=network_time()+catch_release_intervals[1], $block_until=network_time()+block_interval, $current_interval=0, $current_block_id=""); - event NetControl::catch_release_add(a, location); - return bi; -@endif - } function unblock_address_catch_release(a: addr, reason: string &default=""): bool @@ -407,22 +415,22 @@ function unblock_address_catch_release(a: addr, reason: string &default=""): boo if ( a !in blocks ) return F; -@if ( ! Cluster::is_enabled() || ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) ) - local bi = blocks[a]; - local log = populate_log_record(a, bi, UNBLOCK); - if ( reason != "" ) - log$message = reason; - Log::write(CATCH_RELEASE, log); - delete blocks[a]; - if ( bi?$block_until && bi$block_until > network_time() && bi$current_block_id != "" ) - remove_rule(bi$current_block_id, reason); -@endif -@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) - event NetControl::catch_release_block_delete(a); -@endif -@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER ) - event NetControl::catch_release_delete(a, reason); -@endif + if ( single_enforcement_point ) + { + local bi = blocks[a]; + local log = populate_log_record(a, bi, UNBLOCK); + if ( reason != "" ) + log$message = reason; + Log::write(CATCH_RELEASE, log); + delete blocks[a]; + if ( bi?$block_until && bi$block_until > network_time() && bi$current_block_id != "" ) + remove_rule(bi$current_block_id, reason); + } + + if ( is_mgr ) + event NetControl::catch_release_block_delete(a); + else if ( is_not_mgr ) + event NetControl::catch_release_delete(a, reason); return T; } @@ -431,66 +439,66 @@ function catch_release_seen(a: addr) { if ( a in blocks ) { -@if ( ! Cluster::is_enabled() || ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) ) local bi = blocks[a]; - local log: CatchReleaseInfo; - local e = Entity($ty=ADDRESS, $ip=addr_to_subnet(a)); - if ( [e,DROP] in rule_entities ) + if ( single_enforcement_point ) { - if ( catch_release_warn_blocked_ip_encountered == F ) - return; + local log: CatchReleaseInfo; + local e = Entity($ty=ADDRESS, $ip=addr_to_subnet(a)); - # This should be blocked - block has not been applied yet by hardware? Ignore for the moment... - log = populate_log_record(a, bi, INFO); - log$action = INFO; - log$message = "Block seen while in rule_entities. No action taken."; + if ( [e,DROP] in rule_entities ) + { + if ( catch_release_warn_blocked_ip_encountered == F ) + return; + + # This should be blocked - block has not been applied yet by hardware? Ignore for the moment... + log = populate_log_record(a, bi, INFO); + log$action = INFO; + log$message = "Block seen while in rule_entities. No action taken."; + Log::write(CATCH_RELEASE, log); + return; + } + + # ok, this one returned again while still in the backoff period. + + local try = bi$current_interval; + if ( (try+1) in catch_release_intervals ) + ++try; + + bi$current_interval = try; + if ( (try+1) in catch_release_intervals ) + bi$watch_until = network_time() + catch_release_intervals[try+1]; + else + bi$watch_until = network_time() + catch_release_intervals[try]; + + bi$block_until = network_time() + catch_release_intervals[try]; + ++bi$num_reblocked; + + local block_interval = catch_release_intervals[try]; + local location = ""; + if ( bi?$location ) + location = bi$location; + local drop = drop_address(a, block_interval, fmt("Re-drop by catch-and-release: %s", location)); + bi$current_block_id = drop; + + blocks[a] = bi; + + log = populate_log_record(a, bi, SEEN_AGAIN); Log::write(CATCH_RELEASE, log); - return; } - # ok, this one returned again while still in the backoff period. + if ( is_mgr ) + event NetControl::catch_release_block_new(a, bi); - local try = bi$current_interval; - if ( (try+1) in catch_release_intervals ) - ++try; + else if ( is_not_mgr ) + { + if ( a in catch_release_recently_notified ) + return; - bi$current_interval = try; - if ( (try+1) in catch_release_intervals ) - bi$watch_until = network_time() + catch_release_intervals[try+1]; - else - bi$watch_until = network_time() + catch_release_intervals[try]; - - bi$block_until = network_time() + catch_release_intervals[try]; - ++bi$num_reblocked; - - local block_interval = catch_release_intervals[try]; - local location = ""; - if ( bi?$location ) - location = bi$location; - local drop = drop_address(a, block_interval, fmt("Re-drop by catch-and-release: %s", location)); - bi$current_block_id = drop; - - blocks[a] = bi; - - log = populate_log_record(a, bi, SEEN_AGAIN); - Log::write(CATCH_RELEASE, log); -@endif -@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) - event NetControl::catch_release_block_new(a, bi); -@endif -@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER ) - if ( a in catch_release_recently_notified ) - return; - - event NetControl::catch_release_encountered(a); - add catch_release_recently_notified[a]; -@endif - - return; + event NetControl::catch_release_encountered(a); + add catch_release_recently_notified[a]; + } } - - return; } event new_connection(c: connection) diff --git a/scripts/policy/misc/load-balancing.zeek b/scripts/policy/misc/load-balancing.zeek index 7a1be09871..a2c4d685f1 100644 --- a/scripts/policy/misc/load-balancing.zeek +++ b/scripts/policy/misc/load-balancing.zeek @@ -26,7 +26,7 @@ export { }; } -@if ( Cluster::is_enabled() ) +@if ( Cluster::is_enabled() ) &analyze event zeek_init() &priority=5 { diff --git a/scripts/policy/misc/weird-stats.zeek b/scripts/policy/misc/weird-stats.zeek index 0dfc638938..286e183512 100644 --- a/scripts/policy/misc/weird-stats.zeek +++ b/scripts/policy/misc/weird-stats.zeek @@ -77,7 +77,7 @@ function observe_weird_stats() SumStats::Observation($dbl=(v + 0.0))); } -@if ( Cluster::is_enabled() ) +@if ( Cluster::is_enabled() ) &analyze # I'm not sure if this is a hack or not: the manager will generate this # event at the end of its epoch so workers can handle it just in time to diff --git a/scripts/policy/protocols/conn/known-hosts.zeek b/scripts/policy/protocols/conn/known-hosts.zeek index 822bd26ad5..2b79d6ac08 100644 --- a/scripts/policy/protocols/conn/known-hosts.zeek +++ b/scripts/policy/protocols/conn/known-hosts.zeek @@ -96,6 +96,10 @@ event Known::host_found(info: HostsInfo) } } +const should_log_host = + ! Cluster::is_enabled() || + Cluster::local_node_type() == Cluster::PROXY; + event known_host_add(info: HostsInfo) { if ( use_host_store ) @@ -106,10 +110,8 @@ event known_host_add(info: HostsInfo) add Known::hosts[info$host]; - @if ( ! Cluster::is_enabled() || - Cluster::local_node_type() == Cluster::PROXY ) + if ( should_log_host ) Log::write(Known::HOSTS_LOG, info); - @endif } event Cluster::node_up(name: string, id: string) diff --git a/scripts/policy/protocols/conn/known-services.zeek b/scripts/policy/protocols/conn/known-services.zeek index 3c323525fd..84967fa578 100644 --- a/scripts/policy/protocols/conn/known-services.zeek +++ b/scripts/policy/protocols/conn/known-services.zeek @@ -144,6 +144,10 @@ event service_info_commit(info: ServicesInfo) } } +const should_log_service = + ! Cluster::is_enabled() || + Cluster::local_node_type() == Cluster::PROXY; + event known_service_add(info: ServicesInfo) { if ( Known::use_service_store ) @@ -172,10 +176,8 @@ event known_service_add(info: ServicesInfo) } } - @if ( ! Cluster::is_enabled() || - Cluster::local_node_type() == Cluster::PROXY ) + if ( should_log_service ) Log::write(Known::SERVICES_LOG, info_to_log); - @endif } event Cluster::node_up(name: string, id: string) diff --git a/scripts/policy/protocols/ssl/decryption.zeek b/scripts/policy/protocols/ssl/decryption.zeek index e6e7404b60..987588c8b6 100644 --- a/scripts/policy/protocols/ssl/decryption.zeek +++ b/scripts/policy/protocols/ssl/decryption.zeek @@ -41,16 +41,15 @@ export { global add_secret: event(client_random: string, secrets: string); } -@if ( keylog_file == "" ) -# If a keylog file was given via an environment variable, let's disable secret expiration - that does not -# make sense for pcaps. global secrets: table[string] of string = {} &redef; global keys: table[string] of string = {} &redef; -@else -global secrets: table[string] of string = {} &read_expire=secret_expiration &redef; -global keys: table[string] of string = {} &read_expire=secret_expiration &redef; -@endif +@if ( keylog_file != "" ) &analyze +# If a keylog file was given directly (not via an environment variable), +# set up secret expiration (which doesn't make sense for PCAPs). +redef secrets &read_expire=secret_expiration; +redef keys &read_expire=secret_expiration; +@endif redef record SSL::Info += { # Decryption uses client_random as identifier diff --git a/scripts/policy/protocols/ssl/known-certs.zeek b/scripts/policy/protocols/ssl/known-certs.zeek index a5dd1d2382..0be7d62e5c 100644 --- a/scripts/policy/protocols/ssl/known-certs.zeek +++ b/scripts/policy/protocols/ssl/known-certs.zeek @@ -108,6 +108,10 @@ event Known::cert_found(info: CertsInfo, hash: string) } } +const should_log_cert = + ! Cluster::is_enabled() || + Cluster::local_node_type() == Cluster::PROXY; + event known_cert_add(info: CertsInfo, hash: string) { if ( Known::use_cert_store ) @@ -118,10 +122,8 @@ event known_cert_add(info: CertsInfo, hash: string) add Known::certs[info$host, hash]; - @if ( ! Cluster::is_enabled() || - Cluster::local_node_type() == Cluster::PROXY ) + if ( should_log_cert ) Log::write(Known::CERTS_LOG, info); - @endif } event Known::cert_found(info: CertsInfo, hash: string) diff --git a/scripts/policy/protocols/ssl/validate-certs.zeek b/scripts/policy/protocols/ssl/validate-certs.zeek index 4d23c8e02d..42d30ae6f9 100644 --- a/scripts/policy/protocols/ssl/validate-certs.zeek +++ b/scripts/policy/protocols/ssl/validate-certs.zeek @@ -61,7 +61,9 @@ export { global intermediate_cache: table[string] of vector of opaque of x509; -@if ( Cluster::is_enabled() ) +const cluster_is_enabled = Cluster::is_enabled(); + +@if ( cluster_is_enabled ) &analyze event zeek_init() { Broker::auto_publish(Cluster::worker_topic, SSL::intermediate_add); @@ -72,19 +74,18 @@ event zeek_init() function add_to_cache(key: string, value: vector of opaque of x509) { intermediate_cache[key] = value; -@if ( Cluster::is_enabled() ) - event SSL::new_intermediate(key, value); -@endif + if ( cluster_is_enabled ) + event SSL::new_intermediate(key, value); } -@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER ) +@if ( cluster_is_enabled && Cluster::local_node_type() != Cluster::MANAGER ) &analyze event SSL::intermediate_add(key: string, value: vector of opaque of x509) { intermediate_cache[key] = value; } @endif -@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) +@if ( cluster_is_enabled && Cluster::local_node_type() == Cluster::MANAGER ) &analyze event SSL::new_intermediate(key: string, value: vector of opaque of x509) { if ( key in intermediate_cache ) diff --git a/src/ActivationManager.cc b/src/ActivationManager.cc new file mode 100644 index 0000000000..104b6cdd21 --- /dev/null +++ b/src/ActivationManager.cc @@ -0,0 +1,199 @@ +#include "zeek/ActivationManager.h" + +#include "zeek/Desc.h" + +using namespace std; + +namespace zeek::detail + { + +void ActivationEvent::Dump(int indent_level) const + { + Indent(indent_level); + + switch ( et ) + { + case ActivationEvent::COND: + printf("Cond"); + break; + case ActivationEvent::CREATE_GLOBAL: + printf("Create Global"); + break; + case ActivationEvent::ADDING_GLOBAL_VAL: + printf("Add Global Val"); + break; + case ActivationEvent::REDEF: + printf("Redef"); + break; + case ActivationEvent::HANDLER_REDEF: + printf("Handler Redef"); + break; + case ActivationEvent::BODY: + printf("Body"); + break; + } + + if ( id ) + printf(" ID=%s", obj_desc(id.get()).c_str()); + + if ( expr ) + printf(" expr=%s", obj_desc(expr.get()).c_str()); + + printf("\n"); + + if ( et != ActivationEvent::COND ) + return; + + Indent(indent_level); + printf("TRUE:\n"); + for ( auto& s : T_sub_events ) + s->Dump(indent_level + 1); + + if ( ! in_true_branch ) + { + Indent(indent_level); + printf("FALSE:\n"); + for ( auto& s : F_sub_events ) + s->Dump(indent_level + 1); + } + + Indent(indent_level); + printf("END\n"); + } + +void ActivationEvent::Indent(int indent_level) const + { + while ( indent_level-- > 0 ) + printf("\t"); + } + +Activation::Activation(ExprPtr cond, bool _is_activated, bool _parent_activated, int _cond_depth) + { + is_activated = _is_activated; + parent_activated = _parent_activated; + cond_depth = _cond_depth; + + cond_event = std::make_shared(ActivationEvent::COND); + cond_event->AddExpr(cond); + } + +Activation::~Activation() + { + ResetGlobals(); + } + +void Activation::ResetGlobals() + { + if ( ! is_activated ) + { // undo changes we temporarily introduced + for ( auto& gv : global_vals ) + gv->SetVal(nullptr); + + auto gs = global_scope(); + + if ( gs ) + for ( auto& gid : global_IDs ) + gs->RemoveGlobal(gid->Name(), gid); + } + + global_vals.clear(); + global_IDs.clear(); + } + +ActivationManager::~ActivationManager() + { +#if 0 + for ( auto& ae : activation_events ) + ae->Dump(0); +#endif + } + +void ActivationManager::Start(ExprPtr cond, bool activate, int cond_depth) + { + activate = activate && IsActivated(); + auto a = std::make_unique(cond, activate, IsActivated(), cond_depth); + + auto ce = a->CondEvent(); + + if ( activation_stack.empty() ) + activation_events.push_back(ce); + else + activation_stack.back()->CondEvent()->AddSubEvent(std::move(ce)); + + activation_stack.push_back(std::move(a)); + } + +void ActivationManager::SwitchToElse() + { + ASSERT(! activation_stack.empty()); + activation_stack.back()->SwitchToElse(); + } + +void ActivationManager::End() + { + ASSERT(! activation_stack.empty()); + activation_stack.pop_back(); + } + +void ActivationManager::CreatingGlobalID(IDPtr gid) + { + if ( activation_stack.empty() ) + return; + + auto cg = std::make_shared(ActivationEvent::CREATE_GLOBAL); + cg->AddID(gid); + + activation_stack.back()->CondEvent()->AddSubEvent(std::move(cg)); + activation_stack.back()->AddGlobalID(std::move(gid)); + } + +void ActivationManager::AddingGlobalVal(IDPtr gid) + { + if ( activation_stack.empty() ) + return; + + auto gv = std::make_shared(ActivationEvent::ADDING_GLOBAL_VAL); + gv->AddID(gid); + + activation_stack.back()->CondEvent()->AddSubEvent(std::move(gv)); + activation_stack.back()->AddGlobalVal(std::move(gid)); + } + +void ActivationManager::AddingRedef(const IDPtr& id, InitClass c, ExprPtr init, AttrVec& attrs) + { + if ( activation_stack.empty() ) + return; + + auto r = std::make_shared(ActivationEvent::REDEF); + r->AddID(id); + r->AddInitClass(c); + r->AddExpr(init); + r->AddAttrs(attrs); + + activation_stack.back()->CondEvent()->AddSubEvent(std::move(r)); + } + +void ActivationManager::RedefingHandler(const IDPtr& id) + { + if ( activation_stack.empty() ) + return; + + auto hr = std::make_shared(ActivationEvent::HANDLER_REDEF); + hr->AddID(id); + + activation_stack.back()->CondEvent()->AddSubEvent(std::move(hr)); + } + +void ActivationManager::AddingBody(IDPtr func, std::shared_ptr ingr) + { + if ( activation_stack.empty() ) + return; + + auto b = std::make_shared(ActivationEvent::BODY); + b->AddID(func); + b->AddIngredients(std::move(ingr)); + + activation_stack.back()->CondEvent()->AddSubEvent(std::move(b)); + } + + } // namespace zeek::detail diff --git a/src/ActivationManager.h b/src/ActivationManager.h new file mode 100644 index 0000000000..24d528e931 --- /dev/null +++ b/src/ActivationManager.h @@ -0,0 +1,311 @@ +// See the file "COPYING" in the main distribution directory for copyright. + +// Classes for supporting @if &analyze constructs. + +#pragma once + +#include "zeek/Expr.h" +#include "zeek/Func.h" + +namespace zeek::detail + { + +using ScriptFuncPtr = IntrusivePtr; +using AttrVec = std::unique_ptr>; + +class ActivationManager; + +/** + * Expresses an event (of one of the AE_Type types below) occurring during an + * @if &analyze. Events reflect what *could* have happened rather than + * what *did* happen, so events will be present even for @if &analyze blocks + * that were skipped due to their condition evaluating to false. + * + * The current use for these events is to enable the CPP script compiler + * to generate run-time execution of @if &analyze conditionals. To do that, + * it needs a record of their associated effects. + * + * We use a "flat" class that encompasses all of the possibilities, rather + * than subclassing on the different types. This is because ActivationEvent's + * are a record for *reading* rather than conducting further processing + * directly; hence there aren't apt active methods to virtualize. If we + * used subclasses, we'd wind up having to cast to get to the specifics + * elements of a given event, which is clunky enough that it's not clear + * we gain anything useful. + */ +class ActivationEvent + { +public: + /** + * The different types of ActivationEvent's. + */ + enum AE_Type + { + // Represents an @if &analyze conditional, with a corresponding + // condition expression, and sub-events (i.e., other + // ActivationEvent's) for what occurs in the "true" and + // "false" branches. + COND, + + // Represents the introduction of a new global identifier. + CREATE_GLOBAL, + + // Represents adding a value (in particular, a ScriptFunc) to + // a particular global. + ADDING_GLOBAL_VAL, + + // Represents a global having its initialization value and/or + // attributes redef'd. + REDEF, + + // Represents an event handler being redef'd (which discards + // its current value). + HANDLER_REDEF, + + // Represents adding a body to a function/hook/event handler. + BODY, + }; + + // ActivationEvent's always have a type. All the other fields + // are optional, and are populated depending on the type. + ActivationEvent(AE_Type _et) : et(_et) { } + + // Type of the activation event. + AE_Type Type() const { return et; } + + // An associated expression associated. + void AddExpr(ExprPtr _expr) { expr = std::move(_expr); } + ExprPtr GetExpr() const { return expr; } + + // An associated identifier. + void AddID(IDPtr _id) { id = std::move(_id); } + IDPtr GetID() const { return id; } + + // An associated initialization class (equivalent to =/+=/-=). + void AddInitClass(InitClass _c) { c = _c; } + InitClass GetInitClass() const { return c; } + + // A set of associated attributes, or none if a nil pointer. + void AddAttrs(AttrVec& _attrs) + { + // It's a pity that the code base has settled on unique_ptr's + // for collections of attributes rather than shared_ptr's ... + if ( _attrs ) + { + attrs = std::make_unique>(); + *attrs = *_attrs; + } + } + const auto& GetAttrs() const { return attrs; } + + // A set of associated "ingredients" for building a function. + void AddIngredients(std::shared_ptr _ingr) { ingr = std::move(_ingr); } + const auto& GetIngredients() const { return ingr; } + + // Adds a "subevent" to this event, only valid for events that are + // themselves conditionals. Note that the subevent might itself + // be a (nested) conditional. + void AddSubEvent(std::shared_ptr ae) + { + ASSERT(et == COND); + CurrSubEvents().push_back(std::move(ae)); + } + + // Changes this event's accrual of subevents to correspond to its + // "else" branch rather than its main/true branch. + void SwitchToElse() + { + ASSERT(et == COND); + ASSERT(in_true_branch); + in_true_branch = false; + } + + // Prints out the event (and any subevents) for debug purposes. + void Dump(int indent_level) const; + +private: + // Manages indentation when dumping events. + void Indent(int indent_level) const; + + using SubEvents = std::vector>; + + SubEvents& CurrSubEvents() { return in_true_branch ? T_sub_events : F_sub_events; } + + AE_Type et; + ExprPtr expr; + IDPtr id; + InitClass c = INIT_NONE; + AttrVec attrs; + std::shared_ptr ingr; + + // For events corresponding to conditionals, we track two sets + // of sub_events, one for the main (true) branch of the conditional, + // and one for the else (false) branch, if any, with in_true_branch + // reflecting which one we're working on (per CurrSubEvents()). + bool in_true_branch = true; // tells us which one to use + SubEvents T_sub_events; + SubEvents F_sub_events; + }; + +/** + * An "Activation" object tracks the status of a current @if &analyze + * conditional as it's being parsed. Its role is to keep track of what's + * up with the conditional for the live parsing, as opposed to what *could* + * have happened (which is instead reflected in a set of ActivationEvent's). + */ +class Activation + { +public: + Activation(ExprPtr cond, bool _is_activated, bool _parent_activated, int _cond_depth); + ~Activation(); + + // True if we're in the part of the @if &analyze conditional for + // which we should be incorporating statements (making changes to + // globals, adding function bodies, etc.). + bool IsActivated() const { return is_activated; } + + // Returns the @if (not @if &analyze) conditional depth associated + // with this activation. Used to tell whether a given @else or @endif + // corresponds to this @if &analyze, or something nested within it. + int CondDepth() const { return cond_depth; } + + // Returns the ActivationEvent associated with this @if &analyze. + auto CondEvent() const { return cond_event; } + + // Tells the Activation to switch from its main (conditinal-is-true) + // processing to its "else" (conditional-is-false) processing. + void SwitchToElse() + { + // We're done tracking globals for the current body. + ResetGlobals(); + + // Toggle our activation status *unless* our parent (another + // @if &analyze) was itself not active, in which case we + // stay inactive. + if ( parent_activated ) + is_activated = ! is_activated; + + // Keep the ActivationEvent in synch. + cond_event->SwitchToElse(); + } + + void AddGlobalID(IDPtr gid) { global_IDs.push_back(std::move(gid)); } + void AddGlobalVal(IDPtr gid) { global_vals.push_back(std::move(gid)); } + +private: + // If we weren't active, then undo the effects that the parser had + // to make (installing new globals and event handlers) in order + // to assess the correctness of the code within the block. + void ResetGlobals(); + + std::shared_ptr cond_event; + + // True if our parent was itself activated. Always true if there + // wasn't an outer @if &analyze. + bool parent_activated; + + // Whether we're currently activated. + bool is_activated; + + // Depth of @if conditionals when this activation began. + int cond_depth; + + // Set of globals that were added during processing of the current body. + std::vector global_IDs; + + // Similar: set of globals for which we added values. We track + // this only for function/hook/event handler bodies. + std::vector global_vals; + }; + +/** + * Class for managing the processing of @if &analyze's. Deals with + * potential nesting, and with constructing a trace of the associated + * ActivationEvents. + */ +class ActivationManager + { +public: + ActivationManager() = default; + ~ActivationManager(); + + // True if we are currently inside an @if &analyze. + bool InsideConditional() const { return ! activation_stack.empty(); } + + // True if the current @if &analyze corresponds to the given + // @if conditional-depth. Needed to disambiguate @else and @endif + // tokens in the presence of possible inter-nesting of @if and + // @if &analyze constructs. + bool InsideConditional(int cond_depth) const + { + if ( activation_stack.empty() ) + return false; + + return activation_stack.back()->CondDepth() == cond_depth; + } + + // True if processing of the current script is "activated". This + // is the usual state of things, other than when inside the branch + // of an @if &analyze that doesn't correspond to its condition. + bool IsActivated() const + { + return activation_stack.empty() || activation_stack.back()->IsActivated(); + } + + // Returns the nesting level of @if &analyze's. Used by the scanner + // to find dangling conditionals at the end of files. Typed as an + // integer to match similar other structures in the scanner. + int ActivationDepth() const { return static_cast(activation_stack.size()); } + + // Tells the manager to begin a new @if &analyze conditional. + // "cond" is the associated condition, "activate" reflects whether + // the condition is true, and "cond_depth" is the depth of any + // parent @if constructs. + void Start(ExprPtr cond, bool activate, int cond_depth); + + // Tells the manager that an @else has been seen for its innermost + // @if &analyze. + void SwitchToElse(); + + // Tells the manager that an @endif has been seen for its innermost + // @if &analyze. + void End(); + + // Tells the manager that the parser is creating a new global + // identifier. + void CreatingGlobalID(IDPtr gid); + + // Tells the manager that the parser is adding a value to a global. + void AddingGlobalVal(IDPtr gid); + + // Tells the manager that the parser is redef'ing an identifier using + // the associated values. + void AddingRedef(const IDPtr& id, InitClass c, ExprPtr init, AttrVec& attrs); + + // Tells the manger that the given identifier's event handler is + // being redef'd. + void RedefingHandler(const IDPtr& id); + + // Tells the manager that the parser is adding a function body to + // the given function. + void AddingBody(IDPtr func, std::shared_ptr ingr); + + // Returns a list of top-level ActivationEvent's. These will all + // be COND events, which in general should be traversed recursively + // to extract what happened inside the conditionals, including + // additional conditionals potentially nested inside. + const auto& ActivationEvents() const { return activation_events; } + +private: + // Currently live @if &analyze information. + std::vector> activation_stack; + + // A trace of all of the top-level @if &analyze events. Any + // nested @if &analyze's are available from the top-level events + // as sub-events. + std::vector> activation_events; + }; + +extern ActivationManager* activation_mgr; + + } // namespace zeek::detail diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 37527f55dd..e94eafc66d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -282,6 +282,7 @@ set(MAIN_SRCS module_util.cc zeek-affinity.cc zeek-setup.cc + ActivationManager.cc Anon.cc Attr.cc Base64.cc diff --git a/src/Expr.cc b/src/Expr.cc index bcd9ac96ae..ae0ac1b6d7 100644 --- a/src/Expr.cc +++ b/src/Expr.cc @@ -3193,7 +3193,7 @@ HasFieldExpr::HasFieldExpr(ExprPtr arg_op, const char* arg_field_name) HasFieldExpr::~HasFieldExpr() { - delete field_name; + delete[] field_name; } ValPtr HasFieldExpr::Fold(Val* v) const diff --git a/src/Scope.cc b/src/Scope.cc index 4164bfb98e..ca2af6f73f 100644 --- a/src/Scope.cc +++ b/src/Scope.cc @@ -46,6 +46,19 @@ const IDPtr& Scope::Find(std::string_view name) const return ID::nil; } +void Scope::RemoveGlobal(std::string name, IDPtr /* gid */) + { + ASSERT(this == global_scope()); + + local.erase(name); + + // We could remove the identifier from ordered_vars, but for now + // we skip doing so because (1) the only removals we do are for global + // scope (per the method name), and the only use of ordered_vars is + // for traversing function parameters (i.e., non-global scope), and + // (2) it would be a pain to do so given the current data structure. + } + IDPtr Scope::GenerateTemporary(const char* name) { return make_intrusive(name, SCOPE_FUNCTION, false); diff --git a/src/Scope.h b/src/Scope.h index b84b5dbfb7..4545219346 100644 --- a/src/Scope.h +++ b/src/Scope.h @@ -37,12 +37,15 @@ public: const IDPtr& Find(std::string_view name) const; - template void Insert(N&& name, I&& id) + void Insert(std::string name, IDPtr id) { - local[std::forward(name)] = std::forward(id); - ordered_vars.push_back(std::forward(id)); + local[name] = id; + ordered_vars.push_back(id); } + // Must only be called for the global scope. + void RemoveGlobal(std::string name, IDPtr gid); + const IDPtr& GetID() const { return scope_id; } const std::unique_ptr>& Attrs() const { return attrs; } diff --git a/src/Var.cc b/src/Var.cc index d23edc0df8..72d942f43f 100644 --- a/src/Var.cc +++ b/src/Var.cc @@ -6,6 +6,7 @@ #include +#include "zeek/ActivationManager.h" #include "zeek/Desc.h" #include "zeek/EventRegistry.h" #include "zeek/Expr.h" @@ -234,7 +235,7 @@ static void make_var(const IDPtr& id, TypePtr t, InitClass c, ExprPtr init, { if ( IsFunc(id->GetType()->Tag()) ) add_prototype(id, t.get(), attr.get(), init); - else + else if ( activation_mgr->IsActivated() ) id->Error("already defined", init.get()); return; @@ -405,7 +406,18 @@ static void make_var(const IDPtr& id, TypePtr t, InitClass c, ExprPtr init, void add_global(const IDPtr& id, TypePtr t, InitClass c, ExprPtr init, std::unique_ptr> attr, DeclType dt) { - make_var(id, std::move(t), c, std::move(init), std::move(attr), dt, true); + bool do_init = activation_mgr->IsActivated(); + + if ( dt == VAR_REDEF ) + { + activation_mgr->AddingRedef(id, c, init, attr); + + if ( ! do_init ) + // Don't actually change the attributes. + attr = nullptr; + } + + make_var(id, std::move(t), c, std::move(init), std::move(attr), dt, do_init); } StmtPtr add_local(IDPtr id, TypePtr t, InitClass c, ExprPtr init, @@ -711,6 +723,8 @@ void begin_func(IDPtr id, const char* module_name, FunctionFlavor flavor, bool i else if ( is_redef ) id->Error("redef of not-previously-declared value"); + bool is_activated = activation_mgr->IsActivated(); + if ( id->HasVal() ) { FunctionFlavor id_flavor = id->GetVal()->AsFunc()->Flavor(); @@ -724,12 +738,17 @@ void begin_func(IDPtr id, const char* module_name, FunctionFlavor flavor, bool i case FUNC_FLAVOR_EVENT: case FUNC_FLAVOR_HOOK: if ( is_redef ) - // Clear out value so it will be replaced. - id->SetVal(nullptr); + { + activation_mgr->RedefingHandler(id); + + if ( ! is_activated ) + // Clear out value so it will be replaced. + id->SetVal(nullptr); + } break; case FUNC_FLAVOR_FUNCTION: - if ( ! id->IsRedefinable() ) + if ( ! id->IsRedefinable() && is_activated ) id->Error("already defined", t.get()); break; @@ -753,12 +772,15 @@ void begin_func(IDPtr id, const char* module_name, FunctionFlavor flavor, bool i if ( ! check_params(i, prototype, args, canon_args, module_name) ) break; - if ( Attr* depr_attr = find_attr(current_scope()->Attrs().get(), ATTR_DEPRECATED) ) - current_scope()->GetID()->MakeDeprecated(depr_attr->GetExpr()); + if ( is_activated ) + { + if ( Attr* depr_attr = find_attr(current_scope()->Attrs().get(), ATTR_DEPRECATED) ) + current_scope()->GetID()->MakeDeprecated(depr_attr->GetExpr()); - // Reset the AST node statistics to track afresh for this function. - Stmt::ResetNumStmts(); - Expr::ResetNumExprs(); + // Reset the AST node statistics to track afresh for this function. + Stmt::ResetNumStmts(); + Expr::ResetNumExprs(); + } } class OuterIDBindingFinder : public TraversalCallback @@ -846,7 +868,7 @@ void end_func(StmtPtr body, const char* module_name, bool free_of_conditionals) oi->num_stmts = Stmt::GetNumStmts(); oi->num_exprs = Expr::GetNumExprs(); - auto ingredients = std::make_unique(pop_scope(), std::move(body), + auto ingredients = std::make_shared(pop_scope(), std::move(body), module_name); auto id = ingredients->GetID(); if ( ! id->HasVal() ) @@ -854,27 +876,27 @@ void end_func(StmtPtr body, const char* module_name, bool free_of_conditionals) auto f = make_intrusive(id); id->SetVal(make_intrusive(std::move(f))); id->SetConst(); + activation_mgr->AddingGlobalVal(id); } - id->GetVal()->AsFunc()->AddBody(ingredients->Body(), ingredients->Inits(), - ingredients->FrameSize(), ingredients->Priority(), - ingredients->Groups()); - script_coverage_mgr.AddFunction(id, ingredients->Body()); auto func_ptr = cast_intrusive(id->GetVal())->AsFuncPtr(); auto func = cast_intrusive(func_ptr); func->SetScope(ingredients->Scope()); - for ( const auto& group : ingredients->Groups() ) - group->AddFunc(func); + activation_mgr->AddingBody(id, ingredients); - analyze_func(std::move(func)); + if ( activation_mgr->IsActivated() ) + { + func->AddBody(ingredients->Body(), ingredients->Inits(), ingredients->FrameSize(), + ingredients->Priority(), ingredients->Groups()); - // Note: ideally, something would take ownership of this memory until the - // end of script execution, but that's essentially the same as the - // lifetime of the process at the moment, so ok to "leak" it. - ingredients.release(); + for ( const auto& group : ingredients->Groups() ) + group->AddFunc(func); + + analyze_func(std::move(func)); + } } IDPList gather_outer_ids(ScopePtr scope, StmtPtr body) diff --git a/src/input.h b/src/input.h index 205ffe734d..249a8ec781 100644 --- a/src/input.h +++ b/src/input.h @@ -23,7 +23,7 @@ extern void add_to_name_list(char* s, char delim, zeek::name_list& nl); extern void begin_RE(); -extern void do_atif(zeek::detail::Expr* expr); +extern void do_atif(zeek::detail::Expr* expr, bool is_activate); extern void do_atifdef(const char* id); extern void do_atifndef(const char* id); extern void do_atelse(); diff --git a/src/parse.y b/src/parse.y index 4db15571ab..9bc7664141 100644 --- a/src/parse.y +++ b/src/parse.y @@ -31,6 +31,18 @@ %token TOK_ATTR_TYPE_COLUMN TOK_ATTR_DEPRECATED %token TOK_ATTR_IS_ASSIGNED TOK_ATTR_IS_USED TOK_ATTR_ORDERED +// Heads-up, this one is a weirdo. It combines both the attribute and +// a leading ')' before it (the two can be separated by spaces/tabs, but +// no newlines). This is necessary because if we use the more natural +// +// TOK_ATIF '(' expr ')' TOK_ATTR_ANALYZE +// +// then the parser needs to look ahead past the ')' to see if the attribute +// is there. If it *isn't*, then the scanner will return the first token +// of the conditional block for the look-ahead ... which will break the parse +// if that block should in fact have been skipped. +%token TOK_ATTR_ANALYZE + %token TOK_DEBUG %token TOK_NO_TEST @@ -98,6 +110,7 @@ #include "zeek/RE.h" #include "zeek/Scope.h" #include "zeek/Reporter.h" +#include "zeek/ActivationManager.h" #include "zeek/ScriptCoverageManager.h" #include "zeek/ScriptValidation.h" #include "zeek/zeekygen/Manager.h" @@ -139,16 +152,19 @@ extern const char* g_curr_debug_error; extern int in_when_cond; static int in_hook = 0; -int in_init = 0; -int in_record = 0; +static int in_init = 0; +static int in_body = 0; +static int in_global_stmts = 0; +static int in_record = 0; static int in_record_redef = 0; static int in_enum_redef = 0; -bool resolving_global_ID = false; -bool defining_global_ID = false; -std::vector saved_in_init; +static bool resolving_global_ID = false; +static bool defining_global_ID = false; +static bool is_activated = true; +static std::vector saved_in_init; static int expr_list_has_opt_comma = 0; -std::vector> locals_at_this_scope; +static std::vector> locals_at_this_scope; static std::unordered_set out_of_scope_locals; static Location func_hdr_location; @@ -321,6 +337,9 @@ static void build_global(ID* id, Type* t, InitClass ic, Expr* e, add_global(id_ptr, std::move(t_ptr), ic, e_ptr, std::move(attrs_ptr), dt); + if ( ! activation_mgr->IsActivated() ) + return; + if ( dt == VAR_REDEF ) zeekygen_mgr->Redef(id, ::filename, ic, std::move(e_ptr)); else @@ -392,9 +411,13 @@ zeek: auto loc = zeek::detail::GetCurrentLocation(); if ( loc.filename ) set_location(loc); + + ++in_global_stmts; } stmt_list { + --in_global_stmts; + if ( stmts ) stmts->AsStmtList()->Stmts().push_back($3); else @@ -1400,29 +1423,49 @@ decl: } | TOK_REDEF TOK_ENUM global_id TOK_ADD_TO '{' - { ++in_enum_redef; parse_redef_enum($3); zeekygen_mgr->Redef($3, ::filename); } + { + ++in_enum_redef; + parse_redef_enum($3); + zeekygen_mgr->Redef($3, ::filename); + } enum_body '}' ';' { + if ( activation_mgr->InsideConditional() ) + reporter->Error("enum redef cannot appear inside @if &analyze"); --in_enum_redef; // Zeekygen already grabbed new enum IDs as the type created them. } | TOK_REDEF TOK_RECORD global_id '$' TOK_ID - { cur_decl_type_id = $3; zeekygen_mgr->Redef($3, ::filename, INIT_EXTRA); } + { + cur_decl_type_id = $3; + zeekygen_mgr->Redef($3, ::filename, INIT_EXTRA); + } TOK_ADD_TO '{' attr_list '}' ';' { + if ( activation_mgr->InsideConditional() ) + reporter->Error("record redef cannot appear inside @if &analyze"); cur_decl_type_id = 0; parse_redef_record_field($3, $5, INIT_EXTRA, std::unique_ptr>($9)); } + | TOK_REDEF TOK_RECORD global_id '$' TOK_ID - { cur_decl_type_id = $3; zeekygen_mgr->Redef($3, ::filename, INIT_REMOVE); } + { + cur_decl_type_id = $3; + zeekygen_mgr->Redef($3, ::filename, INIT_REMOVE); + } TOK_REMOVE_FROM '{' attr_list '}' ';' { + if ( activation_mgr->InsideConditional() ) + reporter->Error("record redef cannot appear inside @if &analyze"); cur_decl_type_id = 0; parse_redef_record_field($3, $5, INIT_REMOVE, std::unique_ptr>($9)); } | TOK_REDEF TOK_RECORD global_id - { cur_decl_type_id = $3; zeekygen_mgr->Redef($3, ::filename); } + { + cur_decl_type_id = $3; + zeekygen_mgr->Redef($3, ::filename); + } TOK_ADD_TO '{' { ++in_record; ++in_record_redef; } type_decl_list @@ -1433,6 +1476,8 @@ decl: if ( ! $3->GetType() ) $3->Error("unknown identifier"); + else if ( activation_mgr->InsideConditional() ) + reporter->Error("record redef cannot appear inside @if &analyze"); else extend_record($3, std::unique_ptr($8), std::unique_ptr>($11)); @@ -1465,7 +1510,13 @@ conditional_list: conditional: TOK_ATIF '(' expr ')' - { do_atif($3); } + { do_atif($3, false); } + | TOK_ATIF '(' expr TOK_ATTR_ANALYZE + { + if ( in_body ) + reporter->Error("@if &analyze cannot appear inside a function body"); + do_atif($3, true); + } | TOK_ATIFDEF '(' TOK_ID ')' { do_atifdef($3); } | TOK_ATIFNDEF '(' TOK_ID ')' @@ -1516,6 +1567,7 @@ func_body: { saved_in_init.push_back(in_init); in_init = 0; + ++in_body; locals_at_this_scope.clear(); out_of_scope_locals.clear(); @@ -1525,6 +1577,7 @@ func_body: { in_init = saved_in_init.back(); saved_in_init.pop_back(); + --in_body; } '}' @@ -1545,12 +1598,14 @@ lambda_body: { saved_in_init.push_back(in_init); in_init = 0; + ++in_body; } stmt_list { in_init = saved_in_init.back(); saved_in_init.pop_back(); + --in_body; } '}' @@ -1963,11 +2018,21 @@ stmt: ; stmt_list: - stmt_list stmt + stmt_list { is_activated = activation_mgr->IsActivated(); } stmt { - set_location(@1, @2); - $1->AsStmtList()->Stmts().push_back($2); - $1->UpdateLocationEndInfo(@2); + set_location(@1, @3); + + // We can't simply test activation_mgr->IsActivated() + // here because the parser can wind up looking ahead + // to the @endif token and restoring activation that + // in fact was off for the statement. So we capture + // the activation state prior to parsing the statement + // in "is_activated" and test that instead. + if ( ! in_global_stmts || is_activated ) + { + $1->AsStmtList()->Stmts().push_back($3); + $1->UpdateLocationEndInfo(@3); + } } | { $$ = new StmtList(); } @@ -2207,8 +2272,10 @@ global_or_event_id: resolving_global_ID ? current_module.c_str() : 0; - $$ = install_ID($1, module_name, - true, is_export).release(); + auto gid = install_ID($1, module_name, + true, is_export); + activation_mgr->CreatingGlobalID(gid); + $$ = gid.release(); } } ; diff --git a/src/scan.l b/src/scan.l index c961b928e2..39976af9c2 100644 --- a/src/scan.l +++ b/src/scan.l @@ -29,8 +29,6 @@ #include "zeek/DNS_Mgr.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" @@ -41,6 +39,7 @@ #include "zeek/Traverse.h" #include "zeek/module_util.h" #include "zeek/ScannedFile.h" +#include "zeek/ActivationManager.h" #include "zeek/analyzer/Analyzer.h" #include "zeek/zeekygen/Manager.h" @@ -55,9 +54,10 @@ extern YYLTYPE yylloc; // holds start line and column of token extern zeek::EnumType* cur_enum_type; // Track the @if... depth. -static std::intptr_t conditional_depth = 0; +static int conditional_depth = 0; zeek::detail::int_list entry_cond_depth; // @if depth upon starting file +zeek::detail::int_list entry_act_depth; // @if &analyze 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 @@ -127,6 +127,9 @@ static std::string find_relative_script_file(const std::string& filename) static void start_conditional() { + if ( activation_mgr->InsideConditional() ) + zeek::reporter->Warning("@conditional inside @if &analyze"); + ++conditional_depth; ++conditional_epoch; @@ -155,17 +158,18 @@ static void do_pragma(const std::string& pragma) if ( parts[0] == "push" ) { - if ( parts.size() < 2 ) - { - zeek::reporter->FatalError("@pragma push without value"); - return; - } + 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()); + 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]); + pragma_stack.push_back(parts[1]); } + else if ( parts[0] == "pop" ) { if ( pragma_stack.empty() || pragma_stack.size() == entry_pragma_stack_depth.back() ) @@ -176,10 +180,11 @@ static void do_pragma(const std::string& pragma) // 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] ) { + 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(); @@ -397,6 +402,8 @@ when return TOK_WHEN; &backend return TOK_ATTR_BACKEND; &ordered return TOK_ATTR_ORDERED; +")"{OWS}&analyze return TOK_ATTR_ANALYZE; // see parse.y for discussion of weirdness + @deprecated.* { auto num_files = file_stack.length(); auto comment = zeek::util::skip_whitespace(yytext + 11); @@ -534,11 +541,12 @@ when return TOK_WHEN; @endif do_atendif(); @if start_conditional(); +&analyze zeek::reporter->Warning("@if &analyze inside regular @if"); @ifdef start_conditional(); @ifndef start_conditional(); @else return TOK_ATELSE; @endif return TOK_ATENDIF; -[^@\r\n]+ /* eat */ +[^@&\r\n]+ /* eat */ . /* eat */ T RET_CONST(zeek::val_mgr->True()->Ref()) @@ -802,6 +810,7 @@ static int load_files(const char* orig_file) current_file_has_conditionals = files_with_conditionals.count(filename) > 0; entry_cond_depth.push_back(conditional_depth); + entry_act_depth.push_back(activation_mgr->ActivationDepth()); entry_pragma_stack_depth.push_back(pragma_stack.size()); return 1; @@ -846,10 +855,8 @@ static void resume_processing() BEGIN(INITIAL); } -void do_atif(zeek::detail::Expr* expr) +void do_atif(zeek::detail::Expr* expr, bool is_activate) { - start_conditional(); - LocalNameFinder cb; expr->Traverse(&cb); zeek::ValPtr val; @@ -865,11 +872,28 @@ void do_atif(zeek::detail::Expr* expr) if ( ! val ) { expr->Error("invalid expression in @if"); + + if ( ! is_activate ) + start_conditional(); + return; } - if ( ! val->AsBool() ) - begin_ignoring(); + bool is_true = val->AsBool(); + + if ( is_activate ) + { + if ( conditional_depth > 0 ) + zeek::reporter->Warning("@if &analyze inside conditional"); + activation_mgr->Start({zeek::NewRef{}, expr}, is_true, conditional_depth); + } + else + { + start_conditional(); + + if ( ! is_true ) + begin_ignoring(); + } } void do_atifdef(const char* id) @@ -904,6 +928,12 @@ void do_atifndef(const char *id) void do_atelse() { + if ( activation_mgr->InsideConditional(conditional_depth) ) + { // This is the @else corresponding to an @if &analyze. + activation_mgr->SwitchToElse(); + return; + } + if ( conditional_depth == 0 ) zeek::reporter->Error("@else without @if..."); @@ -918,6 +948,15 @@ void do_atelse() void do_atendif() { + if ( activation_mgr->InsideConditional(conditional_depth) ) + { // We're ending an @if &analyze. + if ( activation_mgr->ActivationDepth() <= entry_act_depth.back() ) + zeek::reporter->Error("unbalanced @if &analyze... @endif"); + else + activation_mgr->End(); + return; + } + if ( conditional_depth <= entry_cond_depth.back() ) zeek::reporter->Error("unbalanced @if... @endif"); @@ -1000,6 +1039,13 @@ int yywrap() entry_cond_depth.pop_back(); } + if ( entry_act_depth.size() > 0 ) + { + if ( activation_mgr->ActivationDepth() > entry_act_depth.back() ) + zeek::reporter->FatalError("unbalanced @if &analyze... @endif"); + entry_act_depth.pop_back(); + } + if ( entry_pragma_stack_depth.size() > 0 ) { if ( pragma_stack.size() > entry_pragma_stack_depth.back() ) diff --git a/src/zeek-setup.cc b/src/zeek-setup.cc index 46d5eacef0..53413818a6 100644 --- a/src/zeek-setup.cc +++ b/src/zeek-setup.cc @@ -23,6 +23,7 @@ #define DOCTEST_CONFIG_IMPLEMENT #include "zeek/3rdparty/doctest.h" +#include "zeek/ActivationManager.h" #include "zeek/Anon.h" #include "zeek/DFA.h" #include "zeek/DNS_Mgr.h" @@ -183,6 +184,7 @@ zeek::plugin::Manager* zeek::plugin_mgr = nullptr; zeek::detail::RuleMatcher* zeek::detail::rule_matcher = nullptr; zeek::detail::DNS_Mgr* zeek::detail::dns_mgr = nullptr; zeek::detail::TimerMgr* zeek::detail::timer_mgr = nullptr; +zeek::detail::ActivationManager* zeek::detail::activation_mgr = nullptr; zeek::logging::Manager* zeek::log_mgr = nullptr; zeek::threading::Manager* zeek::thread_mgr = nullptr; @@ -443,6 +445,7 @@ static void terminate_zeek() delete session_mgr; delete fragment_mgr; delete telemetry_mgr; + delete activation_mgr; #ifdef HAVE_SPICY delete spicy_mgr; #endif @@ -673,6 +676,7 @@ SetupResult setup(int argc, char** argv, Options* zopts) auto zeekygen_cfg = options.zeekygen_config_file.value_or(""); zeekygen_mgr = new zeekygen::detail::Manager(zeekygen_cfg, zeek_argv[0]); + activation_mgr = new ActivationManager(); add_essential_input_file("base/init-bare.zeek"); add_essential_input_file("builtin-plugins/__preload__.zeek"); diff --git a/testing/btest/Baseline/core.check-unused-event-handlers/.stderr b/testing/btest/Baseline/core.check-unused-event-handlers/.stderr index 3d69e621ce..d3db972878 100644 --- a/testing/btest/Baseline/core.check-unused-event-handlers/.stderr +++ b/testing/btest/Baseline/core.check-unused-event-handlers/.stderr @@ -1,9 +1,6 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. ### NOTE: This file has been sorted with diff-sort. warning in <...>/check-unused-event-handlers.test, line 7: handler for non-existing event cannot be invoked (this_is_never_used) -warning in , line 1: event handler never invoked: Cluster::hello -warning in , line 1: event handler never invoked: Cluster::node_down -warning in , line 1: event handler never invoked: Cluster::node_up warning in , line 1: event handler never invoked: Control::configuration_update warning in , line 1: event handler never invoked: Control::configuration_update_request warning in , line 1: event handler never invoked: Control::configuration_update_response diff --git a/testing/btest/Baseline/coverage.zeek-profiler-file/step1.out b/testing/btest/Baseline/coverage.zeek-profiler-file/step1.out index e9b69cafea..baee8e49bd 100644 --- a/testing/btest/Baseline/coverage.zeek-profiler-file/step1.out +++ b/testing/btest/Baseline/coverage.zeek-profiler-file/step1.out @@ -1,3 +1,5 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. 1 ./profiling-test1.zeek, line 2 print new conn; +0 ./profiling-test1.zeek, line 6 print hidden new conn; 1 ./profiling-test1.zeek, lines 1-2 event new_connection BODY +0 ./profiling-test1.zeek, lines 5-6 event new_connection BODY diff --git a/testing/btest/Baseline/coverage.zeek-profiler-file/step2.out b/testing/btest/Baseline/coverage.zeek-profiler-file/step2.out index 62505a3666..8ab2229047 100644 --- a/testing/btest/Baseline/coverage.zeek-profiler-file/step2.out +++ b/testing/btest/Baseline/coverage.zeek-profiler-file/step2.out @@ -1,3 +1,5 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. 2 ./profiling-test1.zeek, line 2 print new conn; +0 ./profiling-test1.zeek, line 6 print hidden new conn; 2 ./profiling-test1.zeek, lines 1-2 event new_connection BODY +0 ./profiling-test1.zeek, lines 5-6 event new_connection BODY diff --git a/testing/btest/Baseline/coverage.zeek-profiler-file/step3.out b/testing/btest/Baseline/coverage.zeek-profiler-file/step3.out index 7e37b66ce2..0d1aff1265 100644 --- a/testing/btest/Baseline/coverage.zeek-profiler-file/step3.out +++ b/testing/btest/Baseline/coverage.zeek-profiler-file/step3.out @@ -1,5 +1,9 @@ ### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. 2 ./profiling-test1.zeek, line 2 print new conn; +0 ./profiling-test1.zeek, line 6 print hidden new conn; 2 ./profiling-test1.zeek, lines 1-2 event new_connection BODY +0 ./profiling-test1.zeek, lines 5-6 event new_connection BODY 1 ./profiling-test2.zeek, line 2 print new conn; +0 ./profiling-test2.zeek, line 6 print hidden new conn; 1 ./profiling-test2.zeek, lines 1-2 event new_connection BODY +0 ./profiling-test2.zeek, lines 5-6 event new_connection BODY diff --git a/testing/btest/Baseline/language.at-if-analyze-invalid-10/out b/testing/btest/Baseline/language.at-if-analyze-invalid-10/out new file mode 100644 index 0000000000..e0145a612a --- /dev/null +++ b/testing/btest/Baseline/language.at-if-analyze-invalid-10/out @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +warning in <...>/at-if-analyze-invalid.zeek, line 6: @if &analyze inside conditional +error in <...>/at-if-analyze-invalid.zeek, line 7: unknown identifier warning_and_noticed_syntax_err_F, at or near "warning_and_noticed_syntax_err_F" diff --git a/testing/btest/Baseline/language.at-if-analyze-invalid-2/out b/testing/btest/Baseline/language.at-if-analyze-invalid-2/out new file mode 100644 index 0000000000..0d3e9e1ffb --- /dev/null +++ b/testing/btest/Baseline/language.at-if-analyze-invalid-2/out @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +error in <...>/at-if-analyze-invalid.zeek, line 4: @if &analyze cannot appear inside a function body +error in <...>/at-if-analyze-invalid.zeek, line 6: unknown identifier warning_and_noticed_syntax_error_F, at or near "warning_and_noticed_syntax_error_F" diff --git a/testing/btest/Baseline/language.at-if-analyze-invalid-3/out b/testing/btest/Baseline/language.at-if-analyze-invalid-3/out new file mode 100644 index 0000000000..9a49cf8a6a --- /dev/null +++ b/testing/btest/Baseline/language.at-if-analyze-invalid-3/out @@ -0,0 +1,2 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +error in <...>/at-if-analyze-invalid.zeek, line 4: unknown identifier noticed_syntax_error_T, at or near "noticed_syntax_error_T" diff --git a/testing/btest/Baseline/language.at-if-analyze-invalid-4/out b/testing/btest/Baseline/language.at-if-analyze-invalid-4/out new file mode 100644 index 0000000000..df3c42aa07 --- /dev/null +++ b/testing/btest/Baseline/language.at-if-analyze-invalid-4/out @@ -0,0 +1,2 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +error in <...>/at-if-analyze-invalid.zeek, line 4: unknown identifier noticed_syntax_error_F, at or near "noticed_syntax_error_F" diff --git a/testing/btest/Baseline/language.at-if-analyze-invalid-5/out b/testing/btest/Baseline/language.at-if-analyze-invalid-5/out new file mode 100644 index 0000000000..ae863a3204 --- /dev/null +++ b/testing/btest/Baseline/language.at-if-analyze-invalid-5/out @@ -0,0 +1,5 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +error in <...>/at-if-analyze-invalid.zeek, line 8: record redef cannot appear inside @if &analyze +error in <...>/at-if-analyze-invalid.zeek, line 9: record redef cannot appear inside @if &analyze +error in <...>/at-if-analyze-invalid.zeek, line 10: record redef cannot appear inside @if &analyze +error in <...>/at-if-analyze-invalid.zeek, line 11: enum redef cannot appear inside @if &analyze diff --git a/testing/btest/Baseline/language.at-if-analyze-invalid-6/out b/testing/btest/Baseline/language.at-if-analyze-invalid-6/out new file mode 100644 index 0000000000..25306bb651 --- /dev/null +++ b/testing/btest/Baseline/language.at-if-analyze-invalid-6/out @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +warning in <...>/at-if-analyze-invalid.zeek, line 3: @if &analyze inside regular @if +error in <...>/at-if-analyze-invalid.zeek, line 11: unknown identifier but_a_syntax_error_here1, at or near "but_a_syntax_error_here1" diff --git a/testing/btest/Baseline/language.at-if-analyze-invalid-7/out b/testing/btest/Baseline/language.at-if-analyze-invalid-7/out new file mode 100644 index 0000000000..31f7c69723 --- /dev/null +++ b/testing/btest/Baseline/language.at-if-analyze-invalid-7/out @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +warning in <...>/at-if-analyze-invalid.zeek, line 3: @if &analyze inside conditional +error in <...>/at-if-analyze-invalid.zeek, line 7: unknown identifier warning_and_noticed_syntax_err_F, at or near "warning_and_noticed_syntax_err_F" diff --git a/testing/btest/Baseline/language.at-if-analyze-invalid-8/out b/testing/btest/Baseline/language.at-if-analyze-invalid-8/out new file mode 100644 index 0000000000..af90888745 --- /dev/null +++ b/testing/btest/Baseline/language.at-if-analyze-invalid-8/out @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +warning in <...>/at-if-analyze-invalid.zeek, line 5: @if &analyze inside regular @if +error in <...>/at-if-analyze-invalid.zeek, line 10: unknown identifier but_a_syntax_error_here, at or near "but_a_syntax_error_here" diff --git a/testing/btest/Baseline/language.at-if-analyze-invalid-9/out b/testing/btest/Baseline/language.at-if-analyze-invalid-9/out new file mode 100644 index 0000000000..2dd23936a3 --- /dev/null +++ b/testing/btest/Baseline/language.at-if-analyze-invalid-9/out @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +warning in <...>/at-if-analyze-invalid.zeek, line 6: @if &analyze inside conditional +error in <...>/at-if-analyze-invalid.zeek, line 7: unknown identifier warning_and_noticed_syntax_err_T, at or near "warning_and_noticed_syntax_err_T" diff --git a/testing/btest/Baseline/language.at-if-analyze-invalid/out b/testing/btest/Baseline/language.at-if-analyze-invalid/out new file mode 100644 index 0000000000..af42fd93d7 --- /dev/null +++ b/testing/btest/Baseline/language.at-if-analyze-invalid/out @@ -0,0 +1,3 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +error in <...>/at-if-analyze-invalid.zeek, line 8: @if &analyze cannot appear inside a function body +error in <...>/at-if-analyze-invalid.zeek, line 10: unknown identifier warning_and_noticed_syntax_error_T, at or near "warning_and_noticed_syntax_error_T" diff --git a/testing/btest/Baseline/language.at-if-analyze/.stderr b/testing/btest/Baseline/language.at-if-analyze/.stderr new file mode 100644 index 0000000000..49d861c74c --- /dev/null +++ b/testing/btest/Baseline/language.at-if-analyze/.stderr @@ -0,0 +1 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. diff --git a/testing/btest/Baseline/language.at-if-analyze/out b/testing/btest/Baseline/language.at-if-analyze/out new file mode 100644 index 0000000000..b3fe602f1b --- /dev/null +++ b/testing/btest/Baseline/language.at-if-analyze/out @@ -0,0 +1,6 @@ +### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63. +I got called!, should happen +redef #1 +T/!F +!F/T +hi #2!, F, 0, 4 diff --git a/testing/btest/bifs/hll_cluster.zeek b/testing/btest/bifs/hll_cluster.zeek index dae968ad66..1008f6354b 100644 --- a/testing/btest/bifs/hll_cluster.zeek +++ b/testing/btest/bifs/hll_cluster.zeek @@ -26,7 +26,7 @@ redef Log::default_rotation_interval = 0secs; global hll_data: event(data: opaque of cardinality); -@if ( Cluster::local_node_type() == Cluster::WORKER ) +@if ( Cluster::local_node_type() == Cluster::WORKER ) &analyze event zeek_init() { @@ -89,7 +89,7 @@ event Broker::peer_added(endpoint: Broker::EndpointInfo, msg: string) @endif -@if ( Cluster::local_node_type() == Cluster::MANAGER ) +@if ( Cluster::local_node_type() == Cluster::MANAGER ) &analyze global result_count = 0; global hll: opaque of cardinality; diff --git a/testing/btest/coverage/zeek-profiler-file.zeek b/testing/btest/coverage/zeek-profiler-file.zeek index 05f2845fa5..83ca8d0bb1 100644 --- a/testing/btest/coverage/zeek-profiler-file.zeek +++ b/testing/btest/coverage/zeek-profiler-file.zeek @@ -15,9 +15,19 @@ @TEST-START-FILE profiling-test1.zeek event new_connection(c: connection) { print "new conn"; } + +@if ( F ) &analyze +event new_connection(c: connection) + { print "hidden new conn"; } +@endif @TEST-END-FILE @TEST-START-FILE profiling-test2.zeek event new_connection(c: connection) { print "new conn"; } + +@if ( F ) &analyze +event new_connection(c: connection) + { print "hidden new conn"; } +@endif @TEST-END-FILE diff --git a/testing/btest/language/at-if-analyze-invalid.zeek b/testing/btest/language/at-if-analyze-invalid.zeek new file mode 100644 index 0000000000..0c8cfedaf9 --- /dev/null +++ b/testing/btest/language/at-if-analyze-invalid.zeek @@ -0,0 +1,108 @@ +# @TEST-DOC: Tests that @if/&analyze correctly validates code in non-activated branches +# @TEST-EXEC: cat %INPUT +# @TEST-EXEC-FAIL: zeek -b %INPUT >out 2>&1 +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-remove-abspath btest-diff out + +event zeek_init() + { + @if ( T ) &analyze + # This should complain because it's inside a body + warning_and_noticed_syntax_error_T + @endif + } + +@TEST-START-NEXT + +event zeek_init() + { + @if ( F ) &analyze + # This should also complain because it's inside a body + warning_and_noticed_syntax_error_F + @endif + } + +@TEST-START-NEXT + +@if ( T ) &analyze +# This should definitely complain ... +noticed_syntax_error_T +@endif + +@TEST-START-NEXT + +@if ( F ) &analyze +# ... and so should this, even though it's in a non-activated body +noticed_syntax_error_F +@endif + +@TEST-START-NEXT + +type r: record { a: count; }; +type e: enum { FOO }; + +@if ( F ) &analyze +# Try a bunch of forbidden redef's: adding a record field, adding/removing +# attributes, extending an enum. All should yield complaints. +redef record r += { redef_disallowed_even_though_F: bool; }; +redef record r$a += { &log }; +redef record r$a -= { &log }; +redef enum e += { redef_disallowed_even_though_F }; +@endif + +@TEST-START-NEXT + +@if ( F ) +@if ( T ) &analyze +# Generates a warning because of if-analyze inside a non-if-analyze - +# but doesn't then analyze the body. +warning_and_unnoticed_syntax_err_T +@endif +@endif + +# We add this to make sure there's *some* non-empty output. +but_a_syntax_error_here1 + +@TEST-START-NEXT + +@if ( T ) +@if ( F ) &analyze +# In this case, both a warning for the mixed nesting *and*, because the +# outer conditional is true, a complaint since we go ahead with the +# if-analyze +warning_and_noticed_syntax_err_F +@endif +@endif + +@TEST-START-NEXT + +# Similar test but for "@else" branches. +@if ( T ) +@else +@if ( F ) &analyze +warning_and_unnoticed_syntax_err_F +@endif +@endif + +but_a_syntax_error_here + +@TEST-START-NEXT + +# Similar test but for "@else" branches. +@if ( F ) +blah blah blah +@else +@if ( T ) &analyze +warning_and_noticed_syntax_err_T +@endif +@endif + +@TEST-START-NEXT + +# Similar test but for "@else" branches. +@if ( F ) +blah blah blah +@else +@if ( F ) &analyze +warning_and_noticed_syntax_err_F +@endif +@endif diff --git a/testing/btest/language/at-if-analyze.zeek b/testing/btest/language/at-if-analyze.zeek new file mode 100644 index 0000000000..c875fad841 --- /dev/null +++ b/testing/btest/language/at-if-analyze.zeek @@ -0,0 +1,93 @@ +# @TEST-DOC: Makes sure that code inside not-taken @if/&analyze blocks has its effects correctly unwound +# @TEST-REQUIRES: test "${ZEEK_USE_CPP}" != "1" +# @TEST-EXEC: zeek -b %INPUT >out +# @TEST-EXEC: btest-diff out +# @TEST-EXEC: btest-diff .stderr + +global yep = T &redef; +global nope = F &redef; +global redef_me = 0 &redef; + +@if ( nope ) &analyze + redef nope = F; + event zeek_init() # this should not run + { + print "hi #1!", nope, redef_me; + } + + # Neither of these redef's should have lasting effect. + @if ( nope ) &analyze + redef redef_me = 1; + @else + redef redef_me = 2; + @endif +@endif + +@if ( yep ) &analyze + redef yep = F; + global old_redef_me = redef_me; + event zeek_init() # this should run + { + print "hi #2!", yep, old_redef_me, redef_me; + } + + @if ( yep ) &analyze + redef redef_me = 3; + @else + redef redef_me = 4; # we expect this, since we redef'd "yep" + @endif +@endif + +function side_effects(msg: string): bool + { + print "I got called!", msg; + return T; + } + +global my_table: table[count] of string &default="no redef" &redef; + +@if ( T ) &analyze +global z = side_effects("should happen"); +redef my_table: table[count] of string &default="redef #1"; +@endif + +@if ( F ) &analyze +# Tricky - need to parse/validate the declaration, but not do the init +global z = side_effects("shouldn't happen"); +redef my_table: table[count] of string &default="redef #2"; +@endif + +# Okay, which &default did we actually pick up? +print my_table[5]; + +@if ( T ) &analyze + @if ( F ) &analyze + print "T/F"; + @else + # We expect this one + print "T/!F"; + @endif +@else + # We expect none of these + @if ( F ) &analyze + print "!T/F"; + @else + print "!T/!F"; + @endif +@endif + +@if ( F ) &analyze + # We expect none of these + @if ( F ) &analyze + print "F/F"; + @else + print "F/!F"; + @endif +@else + @if ( T ) &analyze + # We expect this one + print "!F/T"; + @else + print "!F/!T"; + @endif +@endif diff --git a/testing/btest/scripts/base/files/x509/log-caching-cluster.test b/testing/btest/scripts/base/files/x509/log-caching-cluster.test index f7a0373f24..7f8e03e0a0 100644 --- a/testing/btest/scripts/base/files/x509/log-caching-cluster.test +++ b/testing/btest/scripts/base/files/x509/log-caching-cluster.test @@ -33,7 +33,7 @@ event terminate_me() { terminate(); } -@if ( Cluster::local_node_type() == Cluster::WORKER ) +@if ( Cluster::local_node_type() == Cluster::WORKER ) &analyze event zeek_init() { suspend_processing(); diff --git a/testing/btest/scripts/base/files/x509/log-caching-disabled-cluster.test b/testing/btest/scripts/base/files/x509/log-caching-disabled-cluster.test index 7e44945cd3..afaf88702a 100644 --- a/testing/btest/scripts/base/files/x509/log-caching-disabled-cluster.test +++ b/testing/btest/scripts/base/files/x509/log-caching-disabled-cluster.test @@ -35,7 +35,7 @@ event terminate_me() { terminate(); } -@if ( Cluster::local_node_type() == Cluster::WORKER ) +@if ( Cluster::local_node_type() == Cluster::WORKER ) &analyze event zeek_init() { suspend_processing(); diff --git a/testing/btest/scripts/base/frameworks/config/basic_cluster.zeek b/testing/btest/scripts/base/frameworks/config/basic_cluster.zeek index df0b081c06..dd2b6cf3f3 100644 --- a/testing/btest/scripts/base/frameworks/config/basic_cluster.zeek +++ b/testing/btest/scripts/base/frameworks/config/basic_cluster.zeek @@ -43,7 +43,7 @@ event zeek_init() Broker::auto_publish(Cluster::worker_topic, ready_for_data); } -@if ( Cluster::node == "worker-1" ) +@if ( Cluster::node == "worker-1" ) &analyze event Cluster::Experimental::cluster_started() { Config::set_value("testport", 44/tcp); diff --git a/testing/btest/scripts/base/frameworks/config/cluster_resend.zeek b/testing/btest/scripts/base/frameworks/config/cluster_resend.zeek index fb9bf793a8..15abe79015 100644 --- a/testing/btest/scripts/base/frameworks/config/cluster_resend.zeek +++ b/testing/btest/scripts/base/frameworks/config/cluster_resend.zeek @@ -38,14 +38,14 @@ global n = 0; event ready_for_data() { -@if ( Cluster::node == "manager-1" ) - Config::set_value("testcount", 1); -@endif + if ( Cluster::node == "manager-1" ) + Config::set_value("testcount", 1); -@if ( Cluster::node == "worker-1" ) - Config::set_value("testport", 44/tcp); - Config::set_value("teststring", "b", "comment"); -@endif + if ( Cluster::node == "worker-1" ) + { + Config::set_value("testport", 44/tcp); + Config::set_value("teststring", "b", "comment"); + } } global option_changed_count = 0; @@ -72,7 +72,7 @@ event zeek_init() &priority=5 Option::set_change_handler("testcount", option_changed, -100); } -@if ( Cluster::local_node_type() == Cluster::MANAGER ) +@if ( Cluster::local_node_type() == Cluster::MANAGER ) &analyze global peer_count = 0; event Cluster::node_up(name: string, id: string) &priority=-5 diff --git a/testing/btest/scripts/base/frameworks/config/read_config_cluster.zeek b/testing/btest/scripts/base/frameworks/config/read_config_cluster.zeek index 1b2538478e..7f3ca8730e 100644 --- a/testing/btest/scripts/base/frameworks/config/read_config_cluster.zeek +++ b/testing/btest/scripts/base/frameworks/config/read_config_cluster.zeek @@ -94,7 +94,7 @@ event Broker::peer_lost(endpoint: Broker::EndpointInfo, msg: string) terminate(); } -@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) +@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) &analyze event die() { terminate(); diff --git a/testing/btest/scripts/base/frameworks/intel/read-file-dist-cluster.zeek b/testing/btest/scripts/base/frameworks/intel/read-file-dist-cluster.zeek index 833214f502..a4c09e8950 100644 --- a/testing/btest/scripts/base/frameworks/intel/read-file-dist-cluster.zeek +++ b/testing/btest/scripts/base/frameworks/intel/read-file-dist-cluster.zeek @@ -33,7 +33,7 @@ redef Log::default_rotation_interval=0sec; module Intel; -@if ( Cluster::local_node_type() == Cluster::MANAGER ) +@if ( Cluster::local_node_type() == Cluster::MANAGER ) &analyze redef Intel::read_files += { "../intel.dat" }; @endif diff --git a/testing/btest/scripts/base/frameworks/logging/field-extension-cluster-error.zeek b/testing/btest/scripts/base/frameworks/logging/field-extension-cluster-error.zeek index 175237cd39..8f67d5f0ef 100644 --- a/testing/btest/scripts/base/frameworks/logging/field-extension-cluster-error.zeek +++ b/testing/btest/scripts/base/frameworks/logging/field-extension-cluster-error.zeek @@ -20,7 +20,7 @@ redef Cluster::nodes = { @load base/frameworks/reporter @load base/protocols/conn -@if ( Cluster::node == "worker-1" ) +@if ( Cluster::node == "worker-1" ) &analyze redef exit_only_after_terminate = T; @endif diff --git a/testing/btest/scripts/base/frameworks/logging/field-extension-cluster.zeek b/testing/btest/scripts/base/frameworks/logging/field-extension-cluster.zeek index bd61ad37f5..cbd47256c3 100644 --- a/testing/btest/scripts/base/frameworks/logging/field-extension-cluster.zeek +++ b/testing/btest/scripts/base/frameworks/logging/field-extension-cluster.zeek @@ -18,7 +18,7 @@ redef Cluster::nodes = { @load base/protocols/http @load base/frameworks/cluster -@if ( Cluster::node == "worker-1" ) +@if ( Cluster::node == "worker-1" ) &analyze redef exit_only_after_terminate = T; @endif diff --git a/testing/btest/scripts/base/frameworks/netcontrol/basic-cluster.zeek b/testing/btest/scripts/base/frameworks/netcontrol/basic-cluster.zeek index c9056cc1b7..6e64cb4712 100644 --- a/testing/btest/scripts/base/frameworks/netcontrol/basic-cluster.zeek +++ b/testing/btest/scripts/base/frameworks/netcontrol/basic-cluster.zeek @@ -26,7 +26,7 @@ redef exit_only_after_terminate = T; @load base/frameworks/netcontrol -@if ( Cluster::local_node_type() == Cluster::WORKER ) +@if ( Cluster::local_node_type() == Cluster::WORKER ) &analyze event zeek_init() { suspend_processing(); diff --git a/testing/btest/scripts/base/frameworks/notice/suppression-cluster.zeek b/testing/btest/scripts/base/frameworks/notice/suppression-cluster.zeek index 68d4407e74..035a9f7e6f 100644 --- a/testing/btest/scripts/base/frameworks/notice/suppression-cluster.zeek +++ b/testing/btest/scripts/base/frameworks/notice/suppression-cluster.zeek @@ -61,7 +61,7 @@ event Notice::begin_suppression(ts: time, suppress_for: interval, note: Notice:: Broker::publish(Cluster::node_topic("manager-1"), proceed); } -@if ( Cluster::local_node_type() == Cluster::MANAGER ) +@if ( Cluster::local_node_type() == Cluster::MANAGER ) &analyze event Cluster::Experimental::cluster_started() { diff --git a/testing/btest/scripts/base/frameworks/openflow/log-cluster.zeek b/testing/btest/scripts/base/frameworks/openflow/log-cluster.zeek index c3a0d10de2..6753f84110 100644 --- a/testing/btest/scripts/base/frameworks/openflow/log-cluster.zeek +++ b/testing/btest/scripts/base/frameworks/openflow/log-cluster.zeek @@ -21,7 +21,7 @@ redef Log::default_rotation_interval = 0secs; global of_controller: OpenFlow::Controller; -@if ( Cluster::local_node_type() == Cluster::WORKER ) +@if ( Cluster::local_node_type() == Cluster::WORKER ) &analyze event zeek_init() { suspend_processing(); diff --git a/testing/btest/scripts/base/frameworks/sumstats/manual-epoch-cluster.zeek b/testing/btest/scripts/base/frameworks/sumstats/manual-epoch-cluster.zeek index 8732b2aea9..2482eaf1b2 100644 --- a/testing/btest/scripts/base/frameworks/sumstats/manual-epoch-cluster.zeek +++ b/testing/btest/scripts/base/frameworks/sumstats/manual-epoch-cluster.zeek @@ -78,7 +78,7 @@ event ready_for_data() did_data = T; } -@if ( Cluster::local_node_type() == Cluster::MANAGER ) +@if ( Cluster::local_node_type() == Cluster::MANAGER ) &analyze event second_test() { diff --git a/testing/btest/scripts/base/frameworks/telemetry/cluster.zeek b/testing/btest/scripts/base/frameworks/telemetry/cluster.zeek index 56ea828b36..43fefc3477 100644 --- a/testing/btest/scripts/base/frameworks/telemetry/cluster.zeek +++ b/testing/btest/scripts/base/frameworks/telemetry/cluster.zeek @@ -66,7 +66,7 @@ event run_test() } } -@if ( Cluster::node == "manager-1" ) +@if ( Cluster::node == "manager-1" ) &analyze # Use a dynamic metrics port for testing to avoid colliding on 9911/tcp # when running tests in parallel. global orig_metrics_port = Broker::metrics_port; diff --git a/testing/btest/scripts/policy/frameworks/management/controller/agent-checkin.zeek b/testing/btest/scripts/policy/frameworks/management/controller/agent-checkin.zeek index be0881c6ad..58cfc13525 100644 --- a/testing/btest/scripts/policy/frameworks/management/controller/agent-checkin.zeek +++ b/testing/btest/scripts/policy/frameworks/management/controller/agent-checkin.zeek @@ -22,7 +22,7 @@ redef Management::Agent::name = "agent"; # Tell the agent where to locate the controller. redef Management::Agent::controller = [$address="127.0.0.1", $bound_port=to_port(getenv("ZEEK_CONTROLLER_PORT"))]; -@if ( Supervisor::is_supervised() ) +@if ( Supervisor::is_supervised() ) &analyze @load policy/frameworks/management/agent/api diff --git a/testing/btest/supervisor/config-cluster-pcap.zeek b/testing/btest/supervisor/config-cluster-pcap.zeek index 88130c20f2..8388204309 100644 --- a/testing/btest/supervisor/config-cluster-pcap.zeek +++ b/testing/btest/supervisor/config-cluster-pcap.zeek @@ -11,7 +11,7 @@ redef Log::default_rotation_interval = 0sec; -@if ( Supervisor::is_supervisor() ) +@if ( Supervisor::is_supervisor() ) &analyze redef SupervisorControl::enable_listen = T;