Farsight's Advanced Exchange Access: The C Programming API, Part One




This article is first in a multi-part blog series intended to introduce and acquaint the reader with Farsight Security's AXA C API. This article introduces the libaxa C programming API and showcases a simple working example program.

This article assumes the reader is familiar with AXA and related technologies. To brush up, the following Farsight Security Blog articles are recommended reading:

This article, geared towards intermediate-level C programmers, is by no means an exhaustive API reference. For that, the reader is directed to the accompanying Doxygen-based API manual. This article covers libaxa version 1.1.2.


sratesttool is a Unix command-line utility capable of the following:

  • Connecting to an SRA server
  • Enabling an SIE channel
  • Setting an AXA watch
  • Streaming watch hits to the console

Throughout this short series, we'll examine how all of this done. In this first article, we'll cover the main driver which contains the initialization code and controls the main program flow.

To wit:

    $ sratesttool -s tls:user@sraserver,1021 -c 255 -w ch=255
    Farsight Security SRA Test Tool
    connecting to tls:user@sraserver,1021...
    parsing watch: ch=255...
    parse ch=255 OK
    setting watch on server...
    watch set OK
    parsing channel: 255...
    parse 255 OK
    enabling channel on server...
    channel enabled OK
    NMSG watch hit, channel: 255 @ 2015-07-29T19:13:21.111788988
    NMSG watch hit, channel: 255 @ 2015-07-29T19:13:21.612488985
    NMSG watch hit, channel: 255 @ 2015-07-29T19:13:22.113173007
    NMSG watch hit, channel: 255 @ 2015-07-29T19:13:22.613782882
    NMSG watch hit, channel: 255 @ 2015-07-29T19:13:23.114434957
    NMSG watch hit, channel: 255 @ 2015-07-29T19:13:23.615050077
    NMSG watch hit, channel: 255 @ 2015-07-29T19:13:24.115665912
    NMSG watch hit, channel: 255 @ 2015-07-29T19:13:24.616344928
    NMSG watch hit, channel: 255 @ 2015-07-29T19:13:25.117007970
    NMSG watch hit, channel: 255 @ 2015-07-29T19:13:25.617640972
    ^C10 total watch hits

Please note sratesttool is not a full implementation of the AXA protocol. It is meant as a tutorial on how to stand up a simple connection to the SRA server, issue a few commands, and stream data. Sratesttool is only written to work for TLS and many libaxa code paths are not taken nor is robust error checking performed. It is intended as an entry-level program to get the reader familiar with the AXA protocol and the libaxa API.


The code for sratesttool is contained in a single source file that can be compiled into a fully functional program. To build it, you'll need to link against the libaxa library. You can find the source code here and if you're a Debian-user, the Debian package here.

While we will cover the source code across several articles, the full source code is available for download from Farsight Security's blog-code GitHub page.

The code walk-through begins below.


The first section contains the source code license and the standard C header file include progression:

 * Copyright (c) 2015 by Farsight Security, Inc.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *    http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * See the License for the specific language governing permissions and
 * limitations under the License.

#include <sysexits.h>
#include <unistd.h>
#include <signal.h>
#include <limits.h>

#include <axa/client.h>
#include <axa/axa_endian.h>
Server-specific Functions

The following server-specific functions wrap libaxa functions. We'll explore all of them in detail later, for now it is sufficient to understand what they do:

  • srvr_connect(): Connects to the server.
  • srvr_cmd(): Send a command to the server and wait for a response.
  • srvr_wait_resp(): Wait for a response from the server.
  • srvr_process(): Process a message from the server.
  • srvr_disconnect(): Disconnects from the server.
/* connect to server
 * returns true on success, false on failure
static bool srvr_connect(void);

/* send an AXA command to the server and wait for the response
 * returns true on success, false on failure
static bool srvr_cmd(axa_tag_t,             /* AXA tag or AXA_TAG_NONE */
                        axa_p_op_t,         /* AXA opcode (original) */
                        const void *,       /* body of message to send */
                        size_t,             /* length of message */
                        axa_p_op_t);        /* expected response opcode */

/* wait for a response from the server
 * returns true on success, false on failure
static bool srvr_wait_resp(axa_p_op_t,      /* expected response opcode */
                        axa_p_op_t);        /* original opcode*/

/* process a message from the server */
static void srvr_process(void);

/* disconnect from server and print a message */
static void srvr_disconnect(const char *,   /* message to print */
                        ...)                /* optional varargs */
Miscellaneous Functions

Miscellaneous functions are declared next:

/* stop everything and shutdown */
static void stop(int                        /* exitcode */
/* signal handler */
static void sigterm(int                     /* signal number of caught sig */
/* print usage */
static void usage(const char *              /* program name */
Global Variables

There is a small collection of global data:

  • static axa_client_t client: Opaque blob, contains all client state.
  • static const char *server_str: The transport/username/server string.
  • static const char *watch_str: The watch string.
  • static const char *channel_str: The channel string.
  • static int terminated: When this value is > 0, it's time to quit
  • static uint64_t hits: Number of watch hits sratesttool has observed.
static axa_client_t client;
static const char *server_str;
static const char *watch_str;
static const char *channel_str;
static int terminated;
static uint64_t hits;
Main Function Argument Processing

The initial stanza of main function is standard and uninteresting command-line argument processing:

main(int argc, char **argv)
    int opt;
    axa_emsg_t emsg;
    const char *cp;

    while ((opt = getopt(argc, argv, "c:hs:w:")) >= 0)
        switch (opt)
            case 'c':
                channel_str = optarg;
            case 'h':
                return (EXIT_SUCCESS);
            case 's':
                server_str = optarg;
            case 'w':
                watch_str = optarg;
                return (EX_USAGE);
    if (server_str == NULL || channel_str == NULL || watch_str == NULL)
        fprintf(stderr, "-s, -c, -w are all required\n");
        return (EX_USAGE);
AXA Initialization

Next, we encounter the first libaxa API function calls. The first stanza sets up the AXA logging substructure. One of the conveniences libaxa offers is an opaque "three stream" logging / syslog interface. With libaxa, you have the following logging streams:

  • trace: Tracing stream for server-side error messages
  • error: Error stream for client-side error messages
  • accounting: Accounting information (packet sent/loss/transit/limit totals)

Sratesttool does not use the syslog interface and as such initializes the loggers to emit messages only to stderr. For more information, see the Doxygen manual for axa_parse_log_opt().

After the logging initialization, we initialize the AXA client context. This is an opaque structure that contains all of the state required to open and maintain an SRA server connection.

Next comes the signal processing. In order to provide an orderly exit from signals that cause the termination of the sratesttool process with garbage collection and a statistics report, we catch and hand them off to our simple signal handler.

    /* set the global program name (for logging) */
    /* set the tracing stream to emit to stderr only */
    AXA_ASSERT(axa_parse_log_opt(&emsg, "trace,off,stderr"));
    /* set the error stream to emit to stderr only */
    AXA_ASSERT(axa_parse_log_opt(&emsg, "error,off,stderr"));
    /* set the accounting stream to emit to stderr only */
    AXA_ASSERT(axa_parse_log_opt(&emsg, "acct,off,stderr"));

    /* initialize the AXA syslog interface */

    /* ensure all stdio FDs are open and ready for business */

    /* initialize the client context (including AXA IO engine) */

    /* catch and handle these signals for graceful exit */
    signal(SIGPIPE, SIG_IGN);
    signal(SIGHUP, sigterm);
    signal(SIGTERM, sigterm);
    signal(SIGINT, sigterm);

    axa_trace_msg("Farsight Security SRA Test Tool\n");
Server Connection

At this point, our AXA client is initialized and sratesttool is ready for business. It then reaches out and tries to make a connection to the server. If something goes wrong, sratesttol will bail. More robust implementations such as {sra,rad}tool, {sra,rad}tunnel and axaclientd will attempt to reconnect using an exponential back off and retry algorithm.

A lot of core libaxa code is contained in the srvr_connect() function including:

  • Opening the client connection to the server.
  • Parsing the watch string and setting the watch on the server.
  • Parsing the channel string and enabling the channel on the server.
  • Starting the data stream.

In the next article we will explore all of these in detail.

    /* try (once) to connect to the SRA server and bail on any error */
    if (!srvr_connect())
Process Data

We now descend into the infinite event loop. The first check will always be to see if sratesttool needs to exit (as the result of an asynchronous event such as the user hitting ctrl-c at the console).

The call to axa_io_wait() tells libaxa to simply wait up to 10ms for input from the server. Internally, libaxa polls its list of file descriptors to look for input from the server. If the timer expires and no data is available, axa_io_wait() will return AXA_IO_BUSY which just returns control to the top of the loop. If data is waiting to be read, AXA_IO_OK will be returned and srvr_process() will be called to handle it (covered in detail later). For sratesttool, most of the time, this should be watch hits. If something goes wrong when polling, AXA_IO_ERR is returned and sratesttool will disconnect and quit.

Also of note, axa_io_wait() is called without keepalive functionality (not needed for this example) and without tunnel debugging (only used for SSH-based connections).

    /* event loop where we continuously wait/send data/wait */
    for (;;)
        if (terminated != 0)
        /* wait up to 10ms for data from the server */
        switch (axa_io_wait(&emsg,
                    &client.io,         /* address of AXA IO engine */
                    10,                 /* wait up to this many ms */
                    false,              /* wake up to send a keepalive */
                    false))             /* pay attention to tunnel msgs */
            /* AXA IO error */
            case AXA_IO_ERR:
                srvr_disconnect("%s", emsg.c);
            /* incomplete response, poll() and try again */
            case AXA_IO_BUSY:
            /* operation has finished, time to process the result */
            case AXA_IO_OK:
                AXA_FAIL("impossible axa_io_wait() result");

Coming up

The next article in the AXA C API series will continue the discussion of sratesttool and cover some of the core srvr_* functions.

Mike Schiffman is a Packet Esotericist for Farsight Security, Inc.

Read the next part in this series: Farsight's Advanced Exchange Access: The C Programming API, Part Two