This article is the third and final in a multi-part blog series intended to
introduce and acquaint the reader with Farsight Security’s AXA C API. This
article concludes the introduction of the
libaxa C programming API.
This article assumes the reader is familiar with AXA and related technologies. To brush up, the following Farsight Security Blog articles are recommended reading:
- Farsight’s Advanced Exchange Access, Volume 1: Introduction to AXA
- Farsight’s Advanced Exchange Access, Volume 2: Introduction to Sratool
- Farsight’s Advanced Exchange Access, Volume 3: Introduction to Sratunnel
Additionally, if you haven’t yet, you’ll want to read the first and second articles in the series:
- Farsight’s Advanced Exchange Access, The C Programming API, Part One
- Farsight’s Advanced Exchange Access, The C Programming API, Part Two
We will pick up right where we left in the second article.
Wait for a Response From the Server
The main event loop where
sratesttool receives input from the SRA server and
decides what to do with it, called
srvr_wait_resp() is covered next. The
function can be broken down into three parts:
- Check to see if it’s time to quit
- Receive input from the server
- Determine what to do with the input
Check to See If It’s Time To Quit
The first thing
srvr_wait_resp() does is check to see if the global sentry
terminated” is set to true. For this to be the case, one of the
sratesttool was initialized to catch, has been caught. Most of the
time, this will be
SIGINT from the user pressing “ctrl-c” at the console. In
stop() (covered below) is called which begins the shutdown process.
Receive Input From the Server
If it’s not time to quit, control is passed to
axa_input(). This function
accepts the standard
emsg argument we’ve already seen, a pointer to a
client.io context, and the number of milliseconds to block and wait for input
which in this case is
INT_MAX (this can be considered an indefinite block
while waiting for data).
The result is switched and the following cases are evaluated:
AXA_IO_ERR: A fatal error occurred, dump the error and return
falsewhich will result in the upper layer terminating the program.
AXA_IO_BUSY: There was no input before the
poll()timer expired or some other non-fatal condition occurred. Control continues back up to the top.
AXA_IO_OK: Valid data was received, time to figure out what to do with it.
Determine What to Do With the Input
sratesttool evaluates the received AXA protocol message header’s
opcode inside of a large switch table. For posterity, all of the possible
opcodes are listed and in a more robust implementation, each code path would
likely be populated. In the case of
sratesttool only two server responses
AXA_P_OP_HELLO: Process the “hello” message from the server. The AXA protocol version used by the server is saved and the client attempts to adjust to a version it can understand.
AXA_P_OP_OK: Process a “result” message from the server. Here
sratesttoolensures that the opcode received is the expected one.
After a processing the response,
axa_recv_flush() is called to purge
free()) the AXA protocol message from the IO context.
If something went wrong,
axa_client_backoff() is called which will result
in the client closing the connection to the server and the shutting down of the
Disconnect From the Server
If something egregious went wrong,
sratesttool will emit an error message
axa_verror_msg() and shutdown the server connection via
axa_client_backoff(). As has been mentioned before, a more robust
implementation may make use of the internal
backoff timers and attempt a
Stop and Shutdown
stop() function is called any time the
interrupted sentinel evaluates
0. It performs an orderly shutdown, reports the number of watch hits,
and then exits the program.
Read SIE Data From The Server
The function that is called from the main event loop everytime SIE data is ready
axa_recv_buf() is called and the result is
evaluated. Important to note, the
axa_recv_buf() can block so if this is
undesirable, use of the other server read functions covered earlier such as
axa_input(). Once an
AXA_IO_OK is returned, control
passes to another switch table where the response opcode is evaluated.
Process the Watch Hit
The only case
sratesttool is interested in is
AXA_P_OP_WHIT. When the
received header opcode is a “watch hit”,
sratesttool next figures out the
type of watch hit being reported to the client, NMSG or IP. While
is not interested in the contents of the watch hit, it does need to extract
the timestamp, which is stored in different places depending on the type. Next,
a human readable string is constructed using the type of watch hit, the SIE
channel it occurred on, and the timestamp of the watch hit (including
hits counter is incremented and control proceeds to the
end of the switch table where, as seen above,
axa_recv_flush() is called to
purge the AXA protocol message from the IO context.
The signal handler simply sets the global sentinel
terminated to the integral
value of the signal. This allows the uppers layers to:
- Know when something asynchronous has happened and it’s time to quit
- Know which signal was sent to
If a signal is sent repeatedly, it is considered urgent and
reset the default signal handler via
signal(sig, SIG_DFL) which will
immediately terminate the program.
Finally, we conclude with a simple usage function instructing the user how to
In the last three articles, we’ve covered the internals of
very simple “hello world” SRA client. If you are interested in learning more,
please check out the AXA distribution
radtool which are more robust implementations of
SRA and RAD clients built on top of
Mike Schiffman is a Packet Esotericist for Farsight Security, Inc.