BroConnection Class |
Namespace: BroccoliSharp
The BroConnection type exposes the following members.
Name | Description | |
---|---|---|
BroConnection(Int32, BroConnectionFlags) |
Creates a new BroConnection using the existing socket handle.
| |
BroConnection(Socket, BroConnectionFlags) |
Creates a new BroConnection using the existing socket.
| |
BroConnection(TcpClient, BroConnectionFlags) |
Creates a new client-based BroConnection using the existing tcpClient.
| |
BroConnection(TcpListener, BroConnectionFlags) |
Creates a new server-based BroConnection using the existing tcpListener.
| |
BroConnection(String, BroConnectionFlags) |
Creates a new BroConnection with specified connection parameters.
|
Name | Description | |
---|---|---|
AdoptEvents(BroConnection) |
Requests the same events as those in source Bro connection.
| |
AdoptEvents(BroConnection, BroConnection) |
Requests the same events for the destination as those in source.
| |
Connect |
Attempts to establish connection to peer.
| |
Dispose |
Releases all the resources used by this BroConnection object.
| |
Dispose(Boolean) |
Releases the unmanaged resources used by this BroConnection object and optionally releases the managed resources.
| |
Equals | (Inherited from Object.) | |
Finalize |
Releases the unmanaged resources before this BroConnection object is reclaimed by GC.
(Overrides ObjectFinalize.) | |
GetHashCode | Serves as a hash function for a particular type. (Inherited from Object.) | |
GetType | Gets the Type of the current instance. (Inherited from Object.) | |
MemberwiseClone | Creates a shallow copy of the current Object. (Inherited from Object.) | |
OnReceivedEvent |
Raises the ReceivedEvent with the specified args.
| |
ProcessInput |
Processes input sent to the sensor by Bro.
| |
Reconnect |
Drops the current connection and reconnects, reusing all settings.
| |
RegisterForEvent(String, Object) |
Registers for events that arrive with the name of eventName.
| |
RegisterForEvent(String, ActionBroEventArgs, Object) |
Registers for events that arrive with the name of eventName using specified eventHandler.
| |
RequestEvents |
Notifies peering Bro to send events.
| |
SendEvent(BroEvent) |
Attempts to send a BroEvent to a Bro agent.
| |
SendEvent(Byte, Int32) |
Enqueues a serialized event directly into the send buffer for this BroConnection.
| |
SendEvent(String, BroValue) |
Attempts to send an event to a Bro agent with the specified name and parameters.
| |
SendPacket |
Sends Bro packet from this BroConnection.
| |
ToString | Returns a string that represents the current object. (Inherited from Object.) | |
UnregisterForEvent |
Unregisters for events that arrive with the name of eventName.
|
Name | Description | |
---|---|---|
Class |
Gets or sets class associated with this BroConnection.
| |
Data |
Gets data storage facility that can store arbitrary data associated with this BroConnection.
| |
EventQueue |
Gets event queue functions for this BroConnection.
| |
FileDescriptor |
Gets file descriptor of this BroConnection.
| |
Flags |
Gets BroConnectionFlags associated with this BroConnection.
| |
HostName |
Gets host name, formatted as host:port, associated with this BroConnection.
| |
InputBufferLength |
Gets connection statistic for the number of bytes to process in input buffer.
| |
IsAlive |
Gets flag that determines whether a connection is currently alive or has died.
| |
OutputBufferLength |
Gets connection statistic for the number of bytes to process in output buffer.
| |
PacketContext |
Gets or sets current packet context, i.e., the libpcap DLT linklayer type, for this BroConnection.
| |
PeerClass |
Gets connection class indicated by peer.
|
Name | Description | |
---|---|---|
ReceivedEvent |
Occurs when a Broccoli event call-back has been received.
|
In order to establish a connection to a remote Bro, you need to create a BroConnection. You then use this connection object to request events, establish the connection with the remote Bro, send events, etc. You can construct a client based BroConnection with a host name or IP Address string; the string should be in "hostname:port" or "x.x.x.x:port" format, respectively. For more control, you can implement your own .NET TcpClient or Socket objects and use that connection to construct a new BroConnection.
To establish a Broccoli-enabled server connection, you first need to implement the usual .NET TcpListener or Socket objects. Once established, you can create a new BroConnection with a constructor that accepts these objects. The rest of the connection handling then proceeds as in the client scenario.
Note |
---|
BroccoliSharp has not yet been tested with SSL connections. However, this may work by simply using the .NET SslStream with an existing Socket. Note that the SSL implementation in Mono is managed, so this would not be used in conjunction with the bro_ctx structure used to initialize the Broccoli API with OpenSSL. As a result, the bro_ctx structure is not defined in the internal BroccoliSharp BroApi. See Mono SSL for more details on SSL setup. |
All BroConnection constructors accept BroConnectionFlags for fine-tuning connection behavior. These flags are:
Connection Flag | Description: |
---|---|
None | No functionality. Use when no flags are desired. |
Reconnect | When using this option, Broccoli will attempt to reconnect to the peer transparently after losing connectivity. Essentially whenever you try to read from or write to the peer and its connection has broke down, a full reconnect including complete handshaking is attempted. You can check whether the connection to a peer is alive at any time using IsAlive. |
AlwaysQueue | When using this option, Broccoli will queue any events you send for later transmission when a connection is currently down. Without using this flag, any events you attempt to send while a connection is down get dropped on the floor. Note that Broccoli maintains a maximum queue size per connection so if you attempt to send lots of events while the connection is down, the oldest events may start to get dropped nonetheless. Again, you can check whether the connection is currently okay by using IsAlive. |
DontCache | When using this option, Broccoli will ask the peer not to use caching on the objects it sends to us. This is the default and the flag need not normally be used. It is kept to maintain backward compatibility. |
Yield | When using this option, ProcessInput processes at most one event at a time and then returns. |
Cache | When using this option, Broccoli will ask the peer to use caching on the objects it sends to us. Caching is normally disabled. |
Just by creating a BroConnection instance you do not also establish a connection right away, this is done using Connect. The main reason for this is to allow you to subscribe to events (using RegisterForEvent(), see example below) before establishing the connection. Upon successfully returning from Connect you are guaranteed to receive all instances of the event types you have requested, while later on during the connection some time may elapse between the issuing of a request for events and the processing of that request at the remote end. Connections are established via TCP. The port numbers Bro agents and Broccoli applications listen on can vary from peer to peer.
BroConnection connection = new BroConnection("bro.yourorg.com:1234")) // Register for events before connecting to Bro... connection.RegisterForEvent("foo"); // Connect to remote Bro connection.Connect();
Finally, Dispose will terminate a connection and release all resources associated with the connection. You can create as many connections as you like, to one or more peers.
When you want to establish connections from multiple BroccoliSharp applications with different purposes, the peer needs a means to understand what kind of application each connection belongs to. The real meaning of "kind of application" here is "sets of event types to request", because depending on the class of an application, the peer will likely want to receive different types of events.
BroccoliSharp lets you set the class of a connection by assigning a class name to the connection object's Class property. When using this feature, you need set that value before issuing a Connect since the class of a connection is determined at connection startup.
If your peer is a Bro node, you need to match the chosen connection class in the remote Bro's Communication::nodes configuration. See "Configuring event reception in Bro scripts", for how to do this. Finally, in order to obtain the class of a connection as indicated by the remote side, check the value of the PeerClass property:
Console.WriteLine("Peer class = " + connection.PeerClass);
In order to send an event to the remote Bro agent, you first create an empty BroEvent with the name of the event, then add parameters to pass to the event handler at the remote agent and then send off the event.
Let's assume we want to request a report of all connections a remote Bro currently keeps state for that match a given destination port and host name and that have amassed more than a certain number of bytes. The idea is to send an event to the remote Bro that contains the query, identifiable through a request ID, and have the remote Bro answer us with remote_conns events containing the information we asked for. The definition of our requesting event could like the following in the Bro policy:
event report_conns(req_id: int, dest_host: string, dest_port: port, min_size: count);
BroEvent ev = new BroEvent("report_conns");
With this information, we can now compose a Bro request_connections event:
ev.AddParameter(new BroValue(0, BroType.Int)); // req_id ev.AddParameter("desthost.destdomain.com"); // dest_host ev.AddParameter(new BroPort(80, "tcp"); // dest_port ev.AddParameter(new BroValue(1000, BroType.Count)); // min_size
ev.AddParameter(new BroValue(2, BroType.Enum, "transport_proto"));
ev.AddParameter(2, BroType.Enum, "transport_proto");
All that's left to do now is to send off the event. For this, use the Bro connection method SendEvent(BroEvent) providing the event instance as the parameter. This function returns true when the event could be sent right away or was queued for later delivery; otherwise, false is returned on error. If the event gets queued, this does not indicate an error – it is likely that the connection was just not ready to send the event at this point. Whenever you call SendEvent(BroEvent), Broccoli attempts to send as much of an existing event queue as possible. Again, the event is copied internally to make it easier for you to send the same event repeatedly:
connection.SendEvent(ev);
Note |
---|
You don't have to create a BroEvent object if you don't intend on reusing it. The BroConnection
overloads for the SendEvent method include one that takes an args style parameter array:
C# connection.SendEvent("report_conns", new BroValue(0, BroType.Int), "desthost.destdomain.com", new BroPort(80, "tcp"), new BroValue(1000, BroType.Count)); |
The BroEventQueue object is always referenced from the source BroConnection using the read-only BroConnection.EventQueue property, for example:
connection.EventQueue.Length;
Receiving events is a little more work because you need to:
The BroccoliSharp library internally handles creating a callback for the Broccoli API to use for any events to be received. Under the covers, BroccoliSharp will use the Broccoli Compact Argument Passing mechanism and expose the received events through normal .NET event handling mechanisms so as to simplify receiving Bro events for the developer. As a result, all a developer needs to do is to create a handler for the BroConnection.ReceivedEvent and then attach to this event. The function signature for these events looks like delegate void ReceivedEventSignature(object sender, BroEventArgs e):
// Establish event handler for Bro events connection.ReceivedEvent += connection_ReceivedEvent; // Bro event handler - called when a new Bro event is received static void connection_ReceivedEvent(object sender, BroEventArgs e) { if (e.EventName == "foo") { // Code to handle foo event... } }
In order to register to receive an event, use BroConnection.RegisterForEvent()and provide it the name of the Bro event you want to receive. You can optionally also provide user definable data that will be provided to the event handler any time an event with this name is received.
Note |
---|
Any user data associated with the event is maintained in managed memory and does not cross into the Broccoli API. |
Continuing our example, we will now want to process the connection reports that contain the responses to our prior defined remote_conns event. Let's assume those responses look as follows:
event report_conn(req_id: int, conn: connection);
connection.RegisterForEvent("report_conn");
Events are received in a BroEventArgs object that is passed to the event handler that is attached to the connection object's ReceivedEvent. All received events can be distinguished by keying off the BroEventArgs.EventName value, for example:
// Bro event handler static void connection_ReceivedEvent(object sender, BroEventArgs e) { // Raised when a new Bro event is received switch (e.EventName) { case "foo": // Handle foo event Console.WriteLine("Received event \"foo\" with {0} parameters at {1}", e.Parameters.Length, e.EventTime); for (int i = 0; i < e.Parameters.Length; i++) Console.WriteLine(" Event \"foo\" parameter[{0}] = {1}", i, e.Parameters[i]); break; default: Console.WriteLine("Received unexpected event \"{0}\".", e.EventName); break; } }
Note |
---|
In addition to attaching to the ReceivedEvent, you can also use the use the
RegisterForEvent(String, ActionBroEventArgs, Object) overload that accepts a
Delegate as the desired event handler. You can use this in-lieu of creating a single common
event handler for all registered events:
C# // Register to receive the pong event connection.RegisterForEvent("pong", e => { BroRecord pongData = e.Parameters[0]; DateTime dst_time = pongData["dst_time"]; DateTime src_time = pongData["src_time"]; Console.WriteLine("pong event from {0}: seq={1}, time={2}/{3} s", s_hostName, pongData["seq"], (dst_time - src_time).TotalSeconds, (BroTime.Now - src_time).TotalSeconds); }); |
BroccoliSharp will now know what to do with the requested events when they arrive. What's left to do is to let the remote Bro know that you would like to receive the events for which you registered. If you haven't yet called Connect, then there is nothing to do, since calling the connect function will request the registered events anyway. If you are already connected, you can still request events. To do so, simply call the BroConnection.RequestEvents() method.
This mechanism also implies that no unrequested events will be delivered to us (and if that happened for whatever reason, the event would simply be dropped on the floor).
Note |
---|
Currently you cannot un-request events, nor can you request events based on predicates on the values of the events' arguments. |
At this point the remote Bro will start sending you the requested events once they are triggered - however, this does not automatically raise the connection's ReceivedEvent. What is left to do is to trigger processing of the queued events in the connection which will start dispatching them to the event handlers.
To start raising the events and firing event handlers with received event data, you can choose among two approaches: explicit polling or using events on the source Socket to know when data has arrived for processing. Explicit polling is particularly straightforward: all you need to do is call the BroConnection.ProcessInput() function from a timer or loop, which will then go off and check if any events have arrived and if so, dispatch them accordingly. Note that the ProcessInput function does not block – if no events have arrived, then the call will return immediately. For more fine-grained control over your I/O handling, you can use a source Socket to determine when data has arrived and then invoke ProcessInput - one way to do this would be to use asynchronous sockets.
As a side note, if you don't process arriving events frequently enough, then TCP's flow control will start to slow down the sender until eventually events will queue up and be dropped at the sending end.
Note |
---|
When using your own Socket, it is recommended to use the BroConnection constructors that accept sockets. It is also possible to get a handle to the socket that the Broccoli API will create when using the Bro constructor that accepts a host-name. You can do this by accessing the BroConnection.FileDescriptor property, but using the descriptor from within a .NET application is outside the scope of this documentation. |
This example shows how to create and use a BroConnection:
using System; using System.Net.Sockets; using BroccoliSharp; namespace Example { class Program { static void Main(string[] args) { using (BroConnection connection = new BroConnection("bro.yourorg.com:1234")) { // Establish event handler for received Bro events connection.ReceivedEvent += connection_ReceivedEvent; // Register for event "foo" connection.RegisterForEvent("foo"); // Connect to remote Bro connection.Connect(); Console.WriteLine("Peer class = " + connection.PeerClass); // Create a new event BroEvent bar = new BroEvent("bar"); bar.AddParameter("Text parameter"); bar.AddParameter(true); bar.AddParameter("192.168.1.1", BroType.IpAddr); bar.AddParameter(new BroPort(80, ProtocolType.Tcp)); bar.AddParameter(2, BroType.Enum, "transport_proto"); bar.AddParameter(BroTime.Now); // Send the event bool result = connection.SendEvent(bar); Console.WriteLine("Event \"bar\" {0}", result ? "was sent or queued for later delivery" : "failed to send or queue"); // Process any incoming events... connection.ProcessInput(); Console.ReadLine(); // Unregister from event "foo" connection.UnregisterForEvent("foo"); } } // Bro event handler static void connection_ReceivedEvent(object sender, BroEventArgs e) { // Raised when a new Bro event is received switch (e.EventName) { case "foo": // Handle foo event Console.WriteLine("Received event \"foo\" with {0} parameters at {1}", e.Parameters.Length, e.EventTime); for (int i = 0; i < e.Parameters.Length; i++) Console.WriteLine(" Event \"foo\" parameter[{0}] = {1}", i, e.Parameters[i]); break; default: Console.WriteLine("Received unexpected event \"{0}\".", e.EventName); break; } } } }
This example shows a "ping" working with the broping.bro script:
using System; using System.Threading; using BroccoliSharp; namespace BroPing { class Program { static int Main(string[] args) { if (args.Length != 1) { Console.WriteLine("Usage:"); Console.WriteLine(" BroPing host:port"); return 1; } try { string hostName = args[0]; Console.WriteLine("Attempting to establish Bro connection to \"{0}\"...", hostName); // Create the connection object using (BroConnection connection = new BroConnection(hostName)) { // Register to receive the pong event connection.RegisterForEvent("pong", e => { BroValue[] values = e.Parameters; DateTime src_time = values[0]; DateTime dst_time = values[1]; Console.WriteLine("pong event from {0}: seq={1}, time={2}/{3} s", hostName, values[2], (dst_time - src_time).TotalSeconds, (BroTime.Now - src_time).TotalSeconds); }); connection.Connect(); Console.WriteLine("Bro connection established. Starting ping cycle, press any key to cancel..."); int seq = 0; while (!Console.KeyAvailable) { // Send ping connection.SendEvent("ping", BroTime.Now, new BroValue(seq++, BroType.Count)); // Process any received responses connection.ProcessInput(); // Wait one second between pings Thread.Sleep(1000); } } return 0; } catch (Exception ex) { Console.Write("Exception: {0}{1}", ex.Message, Environment.NewLine); return 1; } } } }