Skip to content
This repository has been archived by the owner on Jan 27, 2022. It is now read-only.

zeek/broccoli-python

Repository files navigation

Python Bindings for Broccoli

opening

This Python module provides bindings for Broccoli, Bro's client communication library. In general, the bindings provide the same functionality as Broccoli's C API.

Download

You can find the latest Broccoli-Python release for download at https://www.zeek.org/download.

Broccoli-Python's git repository is located at https://github.com/zeek/broccoli-python

This document describes Broccoli-Python 0.63-2. See the CHANGES file for version history.

Installation

Installation of the Python module is pretty straight-forward:

./configure
make
python setup.py install

Try the following to test the installation. If you do not see any error message, everything should be fine:

python -c "import broccoli"

Usage

The following examples demonstrate how to send and receive Bro events in Python.

The main challenge when using Broccoli from Python is dealing with the data types of Bro event parameters as there is no one-to-one mapping between Bro's types and Python's types. The Python modules automatically maps between those types which both systems provide (such as strings) and provides a set of wrapper classes for Bro types which do not have a direct Python equivalent (such as IP addresses).

Connecting to Bro

The following code sets up a connection from Python to a remote Bro instance (or another Broccoli) and provides a connection handle for further communication:

from broccoli import *
bc = Connection("127.0.0.1:47758")

An IOError will be raised if the connection cannot be established.

Sending Events

Once you have a connection handle bc set up as shown above, you can start sending events:

bc.send("foo", 5, "attack!")

This sends an event called foo with two parameters, 5 and attack!. Broccoli operates asynchronously, i.e., events scheduled with send() are not always sent out immediately but might be queued for later transmission. To ensure that all events get out (and incoming events are processed, see below), you need to call bc.processInput() regularly.

Data Types

In the example above, the types of the event parameters are automatically derived from the corresponding Python types: the first parameter (5) has the Bro type int and the second one (attack!) has Bro type string.

For types which do not have a Python equivalent, the broccoli module provides wrapper classes which have the same names as the corresponding Bro types. For example, to send an event called bar with one addr argument and one count argument, you can write:

bc.send("bar", addr("192.168.1.1"), count(42))

The following table summarizes the available atomic types and their usage.

Bro Type Python Type Example
addr addr("192.168.1.1")

bool count

bool

True count(42)

double enum

float

3.14 Type currently not supported

int interval net port

int

5 interval(60) Type currently not supported port("80/tcp")

string subnet time

string

"attack!" subnet("192.168.1.0/24") time(1111111111.0)

The broccoli module also supports sending Bro records as event parameters. To send a record, you first define a record type. For example, a Bro record type:

type my_record: record {
    a: int;
    b: addr;
    c: subnet;
};

turns into Python as:

my_record = record_type("a", "b", "c")

As the example shows, Python only needs to know the attribute names but not their types. The types are derived automatically in the same way as discussed above for atomic event parameters.

Now you can instantiate a record instance of the newly defined type and send it out:

rec = record(my_record)
rec.a = 5
rec.b = addr("192.168.1.1")
rec.c = subnet("192.168.1.0/24")
bc.send("my_event", rec)

Note

The Python module does not support nested records at this time.

Receiving Events

To receive events, you define a callback function having the same name as the event and mark it with the event decorator:

@event
def foo(arg1, arg2):
    print arg1, arg2

Once you start calling bc.processInput() regularly (see above), each received foo event will trigger the callback function.

By default, the event's arguments are always passed in with built-in Python types. For Bro types which do not have a direct Python equivalent (see table above), a substitute built-in type is used which corresponds to the type the wrapper class' constructor expects (see the examples in the table). For example, Bro type addr is passed in as a string and Bro type time is passed in as a float.

Alternatively, you can define a _typed prototype for the event. If you do so, arguments will first be type-checked and then passed to the call-back with the specified type (which means instances of the wrapper classes for non-Python types). Example:

@event(count, addr)
def bar(arg1, arg2):
    print arg1, arg2

Here, arg1 will be an instance of the count wrapper class and arg2 will be an instance of the addr wrapper class.

Protoyping works similarly with built-in Python types:

@event(int, string):
def foo(arg1, arg2):
    print arg1, arg2

In general, the prototype specifies the types in which the callback wants to receive the arguments. This actually provides support for simple type casts as some types support conversion to into something different. If for instance the event source sends an event with a single port argument, @event(port) will pass the port as an instance of the port wrapper class; @event(string) will pass it as a string (e.g., "80/tcp"); and @event(int) will pass it as an integer without protocol information (e.g., just 80). If an argument cannot be converted into the specified type, a TypeError will be raised.

To receive an event with a record parameter, the record type first needs to be defined, as described above. Then the type can be used with the @event decorator in the same way as atomic types:

my_record = record_type("a", "b", "c")
@event(my_record)
def my_event(rec):
    print rec.a, rec.b, rec.c

Helper Functions

The broccoli module provides one helper function: current_time() returns the current time as a float which, if necessary, can be wrapped into a time parameter (i.e., time(current_time())

Examples

There are some example scripts in the tests/ subdirectory of the broccoli-python repository here:

  • broping.py is a (simplified) Python version of Broccoli's test program broping. Start Bro with broping.bro.
  • broping-record.py is a Python version of Broccoli's broping for records. Start Bro with broping-record.bro.
  • test.py is a very ugly but comprehensive regression test and part of the communication test-suite. Start Bro with test.bro.