FR_SBUFF_SET_RETURN(in, &our_in);
}
-/** Read one line of attribute/value pairs into a list.
- *
- * The line may specify multiple attributes separated by commas.
- *
- * @note If the function returns #T_INVALID, an error has occurred and
- * @note the valuepair list should probably be freed.
- *
- * @param[in] ctx for talloc
- * @param[in] parent parent DA to start referencing from
- * @param[in] parent_vp vp where we place the result
- * @param[in] buffer to read valuepairs from.
- * @param[in] end end of the buffer
- * @param[in] list where the parsed fr_pair_ts will be appended.
- * @param[in,out] token The last token we parsed
- * @param[in] depth the nesting depth for FR_TYPE_GROUP
- * @param[in,out] relative_vp for relative attributes
- * @return
- * - <= 0 on failure.
- * - The number of bytes of name consumed on success.
- */
-static ssize_t pair_parse_legacy(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, fr_pair_t *parent_vp, char const *buffer, char const *end,
- fr_pair_list_t *list, fr_token_t *token, unsigned int depth, fr_pair_t **relative_vp)
-{
- fr_pair_t *vp = NULL;
- fr_pair_t *my_relative_vp;
- char const *p, *next, *op_p;
- fr_token_t quote, last_token = T_INVALID;
- fr_dict_attr_t const *internal = NULL;
- fr_pair_list_t *my_list = list;
- TALLOC_CTX *my_ctx;
- char rhs[1024];
-
- if (fr_dict_internal()) internal = fr_dict_root(fr_dict_internal());
- if (!internal && !parent) return 0;
- if (internal == parent) internal = NULL;
-
- /*
- * Zero data, or empty line.
- */
- if ((buffer == end) || (buffer[0] == 0)) {
- *token = T_EOL;
- return 0;
- }
-
-#ifndef NDEBUG
- if (parent_vp) {
- fr_assert(ctx == parent_vp);
- fr_assert(list == &parent_vp->vp_group);
- }
-#endif
-
- p = buffer;
- while (true) {
- bool is_raw = false;
- ssize_t slen;
- fr_token_t op;
- fr_dict_attr_t const *da, *my_parent;
- fr_dict_attr_t const *da_unknown = NULL;
- fr_dict_attr_err_t err;
-
- fr_skip_whitespace(p);
-
- /*
- * Stop at the end of the input, returning
- * whatever token was last read.
- */
- if (!*p) break;
-
- if (*p == '#') {
- last_token = T_EOL;
- break;
- }
-
- /*
- * Stop at '}', too, if we're inside of a group.
- */
- if ((depth > 0) && (*p == '}')) {
- last_token = T_RCBRACE;
- break;
- }
-
- /*
- * Relative attributes can only exist if there's a relative VP parent.
- */
- if (*p == '.') {
- p++;
-
- if (!*relative_vp) {
- fr_strerror_const("The '.Attribute' syntax can only be used if the previous attribute is structural, and the line ends with ','");
- goto error;
- }
-
- my_parent = (*relative_vp)->da;
- my_list = &(*relative_vp)->vp_group;
- my_ctx = *relative_vp;
- } else {
- /*
- * Be nice to people who expect to see '&' everywhere.
- */
- if (*p == '&') p++;
-
- /*
- * We can find an attribute from the parent, but if the path is fully specified,
- * then we reset any relative VP. So that the _next_ line we parse cannot use
- * ".foo = bar" to get a relative attribute which was used when parsing _this_
- * line.
- */
- my_parent = parent;
- *relative_vp = NULL;
- my_list = list;
- my_ctx = ctx;
-
- /*
- * Raw attributes get a special parser.
- */
- if (strncmp(p, "raw.", 4) == 0) {
- p += 4;
- is_raw = true;
- }
- }
-
- /*
- * Parse the name.
- */
- slen = fr_dict_attr_by_oid_substr(&err, &da, my_parent, &FR_SBUFF_IN(p, (end - p)), &bareword_terminals);
- if (err == FR_DICT_ATTR_NOTFOUND) {
- if (is_raw) {
- /*
- * We have something like raw.KNOWN.26, let's go parse the unknown OID
- * portion, starting from where the parsing failed.
- */
- if (((slen > 0) && (p[slen] == '.') && isdigit((int) p[slen + 1])) ||
- ((slen == 0) && isdigit((int) *p))) {
- char const *q = p + slen + (slen > 0);
-
- slen = fr_dict_unknown_afrom_oid_substr(NULL, &da_unknown, da, &FR_SBUFF_IN(q, (end - q)));
- if (slen < 0) goto error;
-
- p = q;
- da = da_unknown;
- goto check_for_operator;
- }
-
- goto notfound;
- }
-
- /*
- * We have an internal dictionary, look up the attribute there. Note that we
- * can't have raw internal attributes.
- */
- if (internal) {
- slen = fr_dict_attr_by_oid_substr(&err, &da, internal,
- &FR_SBUFF_IN(p, (end - p)), &bareword_terminals);
- }
- }
- if (err != FR_DICT_ATTR_OK) {
- if (slen < 0) slen = -slen;
- p += slen;
-
- /*
- * Regenerate the error message so that it's for the correct parent.
- */
- if (err == FR_DICT_ATTR_NOTFOUND) {
- uint8_t const *q;
-
- if (!fr_dict_attr_allowed_chars[(unsigned char) *p]) {
- fr_strerror_printf("Invalid character '%c' in attribute name at %s", *p, p);
- } else {
- notfound:
- for (q = (uint8_t const *) p; q < (uint8_t const *) end && fr_dict_attr_allowed_chars[*q]; q++) {
- /* nothing */
- }
- fr_strerror_printf("Unknown attribute \"%.*s\" for parent \"%s\"", (int) (q - ((uint8_t const *) p)), p, my_parent->name);
- }
- }
- error:
- fr_dict_unknown_free(&da_unknown);
- *token = T_INVALID;
- return -(p - buffer);
- }
-
- /*
- * If we force it to be raw, then only do that if it's not already unknown.
- */
- if (is_raw && !da_unknown) {
- da_unknown = fr_dict_unknown_afrom_da(ctx, da);
- if (!da_unknown) goto error;
- da = da_unknown;
- }
-
- check_for_operator:
-#ifdef STATIC_ANALYZER
- if (!da) goto error;
-#endif
-
- next = p + slen;
-
- rhs[0] = '\0';
-
- p = next;
- fr_skip_whitespace(p);
- op_p = p;
-
- /*
- * There must be an operator here.
- */
- op = gettoken(&p, rhs, sizeof(rhs), false);
- if ((op < T_EQSTART) || (op > T_EQEND)) {
- fr_strerror_const("Expecting operator");
- goto error;
- }
-
- fr_skip_whitespace(p);
-
- if (parent_vp || (*relative_vp && ((*relative_vp)->da == da->parent))) {
- vp = fr_pair_afrom_da(my_ctx, da);
- if (!vp) goto error;
- fr_pair_append(my_list, vp);
-
- } else if (*relative_vp) {
-
- if (op != T_OP_ADD_EQ) {
- fr_strerror_const("Relative attributes can only use '+=' for the operator");
- p = op_p;
- goto error;
- }
-
- vp = fr_pair_afrom_da_depth_nested(my_ctx, my_list, da, (*relative_vp)->da->depth);
- if (!vp) goto error;
-
- } else if (op != T_OP_ADD_EQ) {
- fr_assert(op != T_OP_PREPEND);
-
- vp = fr_pair_afrom_da_nested(my_ctx, my_list, da);
- if (!vp) goto error;
-
- } else {
- if (fr_pair_append_by_da_parent(my_ctx, &vp, my_list, da) < 0) goto error;
- }
-
- vp->op = op;
-
- /*
- * Peek ahead for structural elements which are raw. If the caller wants to parse them
- * as a set of raw octets, then swap the data type to be octets.
- */
- if (is_raw && (p[0] == '0') && (p[1] == 'x') && (da->type != FR_TYPE_OCTETS)) {
- fr_dict_unknown_free(&da_unknown);
-
- da_unknown = fr_dict_unknown_attr_afrom_da(vp, vp->da);
- if (!da_unknown) goto error;
-
- fr_assert(da_unknown->type == FR_TYPE_OCTETS);
-
- if (fr_pair_reinit_from_da(NULL, vp, da_unknown) < 0) goto error;
-
- da = vp->da;
- da_unknown = NULL; /* already parented from vp */
- }
-
- /*
- * Allow grouping attributes.
- */
- switch (vp->vp_type) {
- case FR_TYPE_NON_LEAF:
- if ((op != T_OP_EQ) && (op != T_OP_CMP_EQ)) {
- fr_strerror_printf("Group list for %s MUST use '=' as the operator", da->name);
- goto error;
- }
-
- if (*p != '{') {
- fr_strerror_printf("Group list for %s MUST start with '{'", da->name);
- goto error;
- }
- p++;
-
- /*
- * Parse nested attributes, but the
- * attributes here are relative to each
- * other, and not to our parent relative VP.
- */
- my_relative_vp = NULL;
-
- slen = pair_parse_legacy(vp, vp->da, vp, p, end, &vp->vp_group, &last_token, depth + 1, &my_relative_vp);
- if (slen <= 0) {
- goto error;
- }
-
- if (last_token != T_RCBRACE) {
- failed_group:
- fr_strerror_const("Failed to end group list with '}'");
- goto error;
- }
-
- p += slen;
- fr_skip_whitespace(p);
- if (*p != '}') goto failed_group;
- p++;
-
- /*
- * Cache which VP is now the one for
- * relative references.
- */
- *relative_vp = vp;
- break;
-
- case FR_TYPE_LEAF:
- /*
- * Get the RHS thing.
- */
- quote = gettoken(&p, rhs, sizeof(rhs), false);
- if (quote == T_EOL) {
- fr_strerror_const("Failed to get value");
- goto error;
- }
-
- switch (quote) {
- case T_DOUBLE_QUOTED_STRING:
- case T_SINGLE_QUOTED_STRING:
- case T_BACK_QUOTED_STRING:
- case T_BARE_WORD:
- break;
-
- default:
- fr_strerror_printf("Failed to find expected value on right hand side in %s", da->name);
- goto error;
- }
-
- fr_skip_whitespace(p);
-
- /*
- * Regular expressions get sanity checked by pair_make().
- *
- * @todo - note that they will also be escaped,
- * so we may need to fix that later.
- */
- if ((vp->op == T_OP_REG_EQ) || (vp->op == T_OP_REG_NE)) {
- if (fr_pair_value_bstrndup(vp, rhs, strlen(rhs), false) < 0) goto error;
-
- } else if ((vp->op == T_OP_CMP_TRUE) || (vp->op == T_OP_CMP_FALSE)) {
- /*
- * We don't care what the value is, so
- * ignore it.
- */
- break;
- }
-
- if (fr_pair_value_from_str(vp, rhs, strlen(rhs),
- fr_value_unescape_by_quote[quote], false) < 0) goto error;
- break;
- }
-
- /*
- * Free the unknown attribute, we don't need it any more.
- */
- fr_dict_unknown_free(&da_unknown);
-
- fr_assert(vp != NULL);
-
- PAIR_VERIFY(vp);
-
- /*
- * Now look for EOL, hash, etc.
- */
- if (!*p || (*p == '#') || (*p == '\n')) {
- last_token = T_EOL;
- break;
- }
-
- fr_skip_whitespace(p);
-
- /*
- * Stop at '}', too, if we're inside of a group.
- */
- if ((depth > 0) && (*p == '}')) {
- last_token = T_RCBRACE;
- break;
- }
-
- if (*p != ',') {
- fr_strerror_printf("Expected ',', got '%c' at offset %zu", *p, p - buffer);
- goto error;
- }
- p++;
- last_token = T_COMMA;
- }
-
- /*
- * And return the last token which we read.
- */
- *token = last_token;
- return p - buffer;
-}
-
-/** Read one line of attribute/value pairs into a list.
- *
- * The line may specify multiple attributes separated by commas.
- *
- * @note If the function returns #T_INVALID, an error has occurred and
- * @note the valuepair list should probably be freed.
- *
- * @param[in] ctx for talloc
- * @param[in] parent parent attribute for resolution
- * @param[in] buffer to read valuepairs from.
- * @param[in] len length of the buffer
- * @param[in] list where the parsed fr_pair_ts will be appended.
- * @return the last token parsed, or #T_INVALID
- */
-fr_token_t fr_pair_list_afrom_str(TALLOC_CTX *ctx, fr_dict_attr_t const *parent, char const *buffer, size_t len, fr_pair_list_t *list)
-{
- fr_token_t token;
- fr_pair_t *relative_vp = NULL;
- fr_pair_list_t tmp_list;
-
- fr_pair_list_init(&tmp_list);
-
- if (pair_parse_legacy(ctx, parent, NULL, buffer, buffer + len, &tmp_list, &token, 0, &relative_vp) < 0) {
- fr_pair_list_free(&tmp_list);
- return T_INVALID;
- }
-
- fr_pair_list_append(list, &tmp_list);
-
- return token;
-}
-
/** Read valuepairs from the fp up to End-Of-File.
*
* @param[in] ctx for talloc