OUR SITES NetworkRADIUS FreeRADIUS

Parsing with sbuffs

Introduction

The aim of parsing is to convert a token stream into some internal format we can operate on easily. The style of parsing used in FreeRADIUS is almost exclusively recursive descent parsing.

In recursive descent parsing functions are used to encapsulate grammar used for parsing a given token (or sequence of tokens). Each time we navigate to a deeper level in a syntax tree we call a parsing function which represents the grammar for that level. Whenever we find a terminal for that grammar (or an obvious syntax error) we return up the call stack to allow the caller to determine what to do.

Unfortunately, because we’re working in C some conventions must be applied by the developer in order to make parsing functions broadly compatible and callable from each other.

This document sets out standard function prototypes and conversions to ensure interoperability.

Public functions

Unlike private parse functions public functions can be called anywhere from within the FreeRADIUS code base. Public functions MUST conform to the set of conventions below.

  • On success the number of bytes must be returned. If consuming no bytes is valid, 0 should be returned.

  • On success the input fr_sbuff_t must be advanced by the number of bytes consumed.

  • On error the input fr_sbuff_t MUST NOT be advanced.

  • On error a negative integer representing the error location must be returned. This negative integer is indexed from 1 (not 0). For example a return value of -5 with an input string the lazy brown fox indicates the error occurred at l.

  • On success or error, any sbuff markers added to the input fr_sbuff_t must be released.

  • The return type of the function should be fr_slen_t, this indicates that the function conforms to the above conventions.

The easiest way to ensure these conventions are met is to initialise a new fr_sbuff_t from the input fr_sbuff_t as soon as the function is entered.

/** Looks for the sequence `octo`,`puss` in a given string
 *
 * If `puss` does not proceed after `octo`, this is a syntax error.
 * If `octo` isn't found, this is ignored, the caller should verify the return value
 * is `> 0` if it requires the `octo`,`puss` token sequence is present.
 */
fr_slen_t my_parse_function(fr_sbuff_t *in)
{
        /* our_in->start = in->p, our_in->p = in->p, our_in->end = in->end */
        fr_sbuff_t our_in = FR_SBUFF(in);

        /*
         *      Optional token sequence not found.
         */
        if (!fr_sbuff_adv_past_str_literal(&our_in, "octo")) return 0;

        /*
         *      Can't have an octo without a puss, are you crazy?!
         *
         *      Return the parse error as a negative integer and leave
         *      `in` unchanged.
         */
        if (!fr_sbuff_adv_past_str_literal(&our_in, "puss")) FR_SBUFF_RETURN_ERROR(&our_in); /* Returns -5 */

        /*
         *      Got an `octo` and a `puss`, inform the caller how
         *      much we consumed, and update `in`.
         */
        FR_SBUFF_RETURN_SET(in, &our_in); /* Returns 8 */
}

Private functions

Private functions (static functions in the same source file) don’t have the same requirements. There’s no reason for these functions to return the number of parsed bytes, and they can return 0 for success, or -1 for failure like other functions in the server.