
module Pcap;


const snaplen: count;
const bufsize: count;

%%{
#include <pcap.h>

#include "zeek/iosource/Manager.h"
%%}

## Precompiles a PCAP filter and binds it to a given identifier.
##
## id: The PCAP identifier to reference the filter *s* later on.
##
## s: The PCAP filter. See ``man tcpdump`` for valid expressions.
##
## Returns: True if *s* is valid and precompiles successfully.
##
## .. zeek:see:: Pcap::install_pcap_filter
##          install_src_addr_filter
##          install_src_net_filter
##          uninstall_src_addr_filter
##          uninstall_src_net_filter
##          install_dst_addr_filter
##          install_dst_net_filter
##          uninstall_dst_addr_filter
##          uninstall_dst_net_filter
##          Pcap::error
function precompile_pcap_filter%(id: PcapFilterID, s: string%): bool
	%{
	if ( id->AsEnum() >= 100 )
		{
		// We use a vector as underlying data structure for fast
		// lookups and limit the ID space so that that doesn't grow too
		// large.
		zeek::emit_builtin_error(
			zeek::util::fmt("PCAP filter ids must remain below 100 (is %" PRId64 ")", id->AsInt()));
		return zeek::val_mgr->False();
		}

	bool success = true;

	zeek::iosource::PktSrc* ps = zeek::iosource_mgr->GetPktSrc();
	if ( ps && ! ps->PrecompileFilter(id->AsInt(), s->CheckString()) )
		success = false;

	return zeek::val_mgr->Bool(success);
	%}

## Installs a PCAP filter that has been precompiled with
## :zeek:id:`Pcap::precompile_pcap_filter`.
##
## id: The PCAP filter id of a precompiled filter.
##
## Returns: True if the filter associated with *id* has been installed
##          successfully.
##
## .. zeek:see:: Pcap::precompile_pcap_filter
##              install_src_addr_filter
##              install_src_net_filter
##              uninstall_src_addr_filter
##              uninstall_src_net_filter
##              install_dst_addr_filter
##              install_dst_net_filter
##              uninstall_dst_addr_filter
##              uninstall_dst_net_filter
##              Pcap::error
function Pcap::install_pcap_filter%(id: PcapFilterID%): bool
	%{
	bool success = true;

	zeek::iosource::PktSrc* ps = zeek::iosource_mgr->GetPktSrc();
	if ( ps && ! ps->SetFilter(id->AsInt()) )
		success = false;

	return zeek::val_mgr->Bool(success);
	%}

## Returns a string representation of the last PCAP error.
##
## Returns: A descriptive error message of the PCAP function that failed.
##
## .. zeek:see:: Pcap::precompile_pcap_filter
##              Pcap::install_pcap_filter
##              install_src_addr_filter
##              install_src_net_filter
##              uninstall_src_addr_filter
##              uninstall_src_net_filter
##              install_dst_addr_filter
##              install_dst_net_filter
##              uninstall_dst_addr_filter
##              uninstall_dst_net_filter
function error%(%): string
	%{
	zeek::iosource::PktSrc* ps = zeek::iosource_mgr->GetPktSrc();
	if ( ps )
		{
		const char* err = ps->ErrorMsg();
		if ( *err )
			return zeek::make_intrusive<zeek::StringVal>(err);
		}

	return zeek::make_intrusive<zeek::StringVal>("no error");
	%}

function findalldevs%(%): Pcap::Interfaces
	%{
	pcap_if_t* alldevs;
	char errbuf[PCAP_ERRBUF_SIZE];

	static auto ifaces_type = id::find_type<TableType>("Pcap::Interfaces");
	auto pcap_interfaces = make_intrusive<TableVal>(ifaces_type);

	int ret = pcap_findalldevs(&alldevs, errbuf);
	if ( ret == PCAP_ERROR )
		{
		emit_builtin_error(util::fmt("Error calling pcap_findalldevs: %s", errbuf));
		// Return an empty set in case of failure.
		return pcap_interfaces;
		}

	static auto iface_type = id::find_type<RecordType>("Pcap::Interface");
	for ( auto d = alldevs; d; d = d->next )
		{
		auto r = make_intrusive<RecordVal>(iface_type);

		r->Assign(0, d->name);
		if ( d->description )
			r->Assign(1, d->description);

		auto addrs = make_intrusive<ListVal>(TYPE_ADDR);
		for ( auto addr = d->addresses; addr != NULL; addr = addr->next )
			{
			if ( addr->addr->sa_family == AF_INET )
				{
				IPAddr a(reinterpret_cast<struct sockaddr_in *>(addr->addr)->sin_addr);
				addrs->Append(make_intrusive<AddrVal>(a));
				}
			else if ( addr->addr->sa_family == AF_INET6 )
				{
				IPAddr a(reinterpret_cast<struct sockaddr_in6 *>(addr->addr)->sin6_addr);
				addrs->Append(make_intrusive<AddrVal>(a));
				}
			}
		r->Assign(2, addrs->ToSetVal());
		r->Assign(3, static_cast<bool>(d->flags & PCAP_IF_LOOPBACK));
#ifdef PCAP_IF_UP
		// These didn't become available until libpcap 1.6.1
		r->Assign(4, static_cast<bool>(d->flags & PCAP_IF_UP));
		r->Assign(5, static_cast<bool>(d->flags & PCAP_IF_RUNNING));
#endif

		pcap_interfaces->Assign(std::move(r), nullptr);
		}

	pcap_freealldevs(alldevs);
	return pcap_interfaces;
	%}
