--- /dev/null
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file src/lib/util/encode.c
+ * @brief Generic functions for decoding protocols.
+ *
+ * @copyright 2022 Network RADIUS SAS (legal@networkradius.com)
+ */
+#include <freeradius-devel/io/test_point.h>
+#include <freeradius-devel/util/proto.h>
+#include <freeradius-devel/util/encode.h>
+
+/** Encode an array of values from the network
+ *
+ * @param[out] dbuff buffer to write the TLV to.
+ * @param[in] da_stack Describing nesting of options.
+ * @param[in] depth in the da_stack.
+ * @param[in,out] cursor Current attribute we're encoding.
+ * @param[in] encode_ctx Containing DHCPv4 dictionary.
+ * @return
+ * - >0 length of data encoded.
+ * - 0 if we ran out of space.
+ * - < 0 on error.
+ */
+ssize_t fr_pair_array_to_network(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, int depth,
+ fr_dcursor_t *cursor, void *encode_ctx, fr_proto_encode_value_t encode_value)
+{
+ ssize_t slen;
+ fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
+ fr_pair_t *vp;
+ fr_dict_attr_t const *da = da_stack->da[depth];
+
+ FR_PROTO_STACK_PRINT(da_stack, depth);
+
+ if (!fr_cond_assert_msg(da->flags.array,
+ "%s: Internal sanity check failed, attribute \"%s\" does not have array bit set",
+ __FUNCTION__, da->name)) return PAIR_ENCODE_FATAL_ERROR;
+
+ while (fr_dbuff_extend(&work_dbuff)) {
+ fr_dbuff_t element_dbuff = FR_DBUFF(&work_dbuff);
+
+ slen = encode_value(&element_dbuff, da_stack, depth, cursor, encode_ctx);
+ if (slen < 0) return slen;
+
+ fr_dbuff_set(&work_dbuff, &element_dbuff);
+
+ vp = fr_dcursor_current(cursor);
+ if (!vp || (vp->da != da)) break; /* Stop if we have an attribute of a different type */
+ }
+
+ return fr_dbuff_set(dbuff, &work_dbuff);
+}
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
-/** Protocol decoder support functions
+/** Protocol encoder support functions
*
* @file src/lib/util/encode.h
*
- * @copyright 2021 Network RADIUS SAS
+ * @copyright 2022 Network RADIUS SAS
*/
RCSIDH(encode_h, "$Id$")
/** Typedefs for simplifying the use and declaration of protocol encoders
*
*/
-typedef struct fr_proto_encode_ctx_s fr_proto_encode_ctx_t;
-
-typedef ssize_t (*fr_proto_encode_pair_t)(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth,
- fr_dcursor_t *cursor, fr_proto_encode_ctx_t *encode_ctx);
+typedef ssize_t (*fr_proto_encode_value_t)(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth,
+ fr_dcursor_t *cursor, void *encode_ctx);
#define PROTO_ENCODE_FUNC(_name) static ssize_t _name(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth, \
- fr_dcursor_t *cursor, fr_proto_encode_ctx_t *encode_ctx);
+ fr_dcursor_t *cursor, void *encode_ctx);
+
+ssize_t fr_pair_array_to_network(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, int depth,
+ fr_dcursor_t *cursor, void *encode_ctx, fr_proto_encode_value_t encode_value);
+
#ifdef __cplusplus
}
dl.c \
dns.c \
edit.c \
+ encode.c \
event.c \
ext.c \
fifo.c \
RCSID("$Id$")
#include <freeradius-devel/util/struct.h>
+#include <freeradius-devel/util/encode.h>
/** Convert a STRUCT to one or more VPs
*
* Call the protocol encoder for non-bit fields.
*/
fr_proto_da_stack_build(da_stack, child);
- len = encode_value(&work_dbuff, da_stack, depth + 1, cursor, encode_ctx);
+
+ if (child->flags.array) {
+ len = fr_pair_array_to_network(&work_dbuff, da_stack, depth + 1, cursor, encode_ctx, encode_value);
+ } else {
+ len = encode_value(&work_dbuff, da_stack, depth + 1, cursor, encode_ctx);
+ }
if (len < 0) return len;
vp = fr_dcursor_current(cursor);
} else {
+ redo:
/*
* Hack until we find all places that don't set data.enumv
*/
vp = fr_dcursor_next(cursor);
if (!vp || !vp->da->flags.internal) break;
} while (vp != NULL);
+
+ if (child->flags.array && (vp->da == child)) goto redo;
}
next:
#include <freeradius-devel/util/proto.h>
#include <freeradius-devel/util/struct.h>
#include <freeradius-devel/util/dns.h>
+#include <freeradius-devel/util/encode.h>
#include "dhcpv4.h"
#include "attrs.h"
fr_da_stack_t *da_stack, unsigned int depth,
fr_dcursor_t *cursor, void *encode_ctx);
-static ssize_t encode_array(fr_dbuff_t *dbuff,
- fr_da_stack_t *da_stack, int depth,
- fr_dcursor_t *cursor, void *encode_ctx);
-
-static ssize_t encode_value_trampoline(fr_dbuff_t *dbuff,
- fr_da_stack_t *da_stack, unsigned int depth,
- fr_dcursor_t *cursor, void *encode_ctx)
-{
- fr_dict_attr_t const *da = da_stack->da[depth];
-
- /*
- * Write out the option's value
- */
- if (da->flags.array) {
- return encode_array(dbuff, da_stack, depth, cursor, encode_ctx);
- }
-
- return encode_value(dbuff, da_stack, depth, cursor, encode_ctx);
-}
-
/** Write DHCP option value into buffer
*
* Does not include DHCP option length or number.
* Structures are special.
*/
if ((vp->da->type == FR_TYPE_STRUCT) || (da->type == FR_TYPE_STRUCT)) {
- slen = fr_struct_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value_trampoline, encode_tlv);
+ slen = fr_struct_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value, encode_tlv);
if (slen <= 0) return slen;
/*
}
-static ssize_t encode_array(fr_dbuff_t *dbuff,
- fr_da_stack_t *da_stack, int depth,
- fr_dcursor_t *cursor, void *encode_ctx)
-{
- ssize_t slen;
- fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
- fr_pair_t *vp;
- fr_dict_attr_t const *da = da_stack->da[depth];
-
- FR_PROTO_STACK_PRINT(da_stack, depth);
-
- if (!fr_cond_assert_msg(da->flags.array,
- "%s: Internal sanity check failed, attribute \"%s\" does not have array bit set",
- __FUNCTION__, da->name)) return PAIR_ENCODE_FATAL_ERROR;
-
- while (fr_dbuff_extend(&work_dbuff)) {
- fr_dbuff_t element_dbuff = FR_DBUFF(&work_dbuff);
-
- slen = encode_value(&element_dbuff, da_stack, depth, cursor, encode_ctx);
- if (slen < 0) return slen;
-
- fr_dbuff_set(&work_dbuff, &element_dbuff);
-
- vp = fr_dcursor_current(cursor);
- if (!vp || (vp->da != da)) break; /* Stop if we have an attribute of a different type */
- }
-
- return fr_dbuff_set(dbuff, &work_dbuff);
-}
-
/** Extend an encoded option in-place.
*
* @param[in] dbuff buffer containing the option
* Write out the option's value
*/
if (da->flags.array) {
- len = encode_array(&work_dbuff, da_stack, depth, cursor, encode_ctx);
+ len = fr_pair_array_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value);
if (len < 0) return -1;
} else if (da->parent && (da->parent->type != FR_TYPE_VENDOR)) {
#include <freeradius-devel/util/dns.h>
#include <freeradius-devel/util/proto.h>
#include <freeradius-devel/util/struct.h>
+#include <freeradius-devel/util/encode.h>
#include "dhcpv6.h"
#include "attrs.h"
}
-static inline ssize_t encode_array(fr_dbuff_t *dbuff,
- fr_da_stack_t *da_stack, int depth,
- fr_dcursor_t *cursor, void *encode_ctx);
-
-static ssize_t encode_value_trampoline(fr_dbuff_t *dbuff,
- fr_da_stack_t *da_stack, unsigned int depth,
- fr_dcursor_t *cursor, void *encode_ctx)
-{
- fr_dict_attr_t const *da = da_stack->da[depth];
-
- /*
- * Write out the option's value
- */
- if (da->flags.array) {
- return encode_array(dbuff, da_stack, depth, cursor, encode_ctx);
- }
-
- return encode_value(dbuff, da_stack, depth, cursor, encode_ctx);
-}
-
static ssize_t encode_value(fr_dbuff_t *dbuff,
fr_da_stack_t *da_stack, unsigned int depth,
fr_dcursor_t *cursor, void *encode_ctx)
* Pack multiple attributes into into a single option
*/
if ((vp->da->type == FR_TYPE_STRUCT) || (da->type == FR_TYPE_STRUCT)) {
- slen = fr_struct_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value_trampoline, encode_tlv);
+ slen = fr_struct_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value, encode_tlv);
if (slen <= 0) return slen;
/*
return fr_dbuff_set(dbuff, &work_dbuff);
}
-static inline ssize_t encode_array(fr_dbuff_t *dbuff,
- fr_da_stack_t *da_stack, int depth,
- fr_dcursor_t *cursor, void *encode_ctx)
-{
- ssize_t slen;
- fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
- fr_pair_t *vp;
- fr_dict_attr_t const *da = da_stack->da[depth];
-
- if (!fr_cond_assert_msg(da->flags.array,
- "%s: Internal sanity check failed, attribute \"%s\" does not have array bit set",
- __FUNCTION__, da->name)) return PAIR_ENCODE_FATAL_ERROR;
-
- while (fr_dbuff_extend(&work_dbuff)) {
- fr_dbuff_t element_dbuff = FR_DBUFF(&work_dbuff);
-
- slen = encode_value(&element_dbuff, da_stack, depth, cursor, encode_ctx);
- if (slen < 0) return slen;
-
- fr_dbuff_set(&work_dbuff, &element_dbuff);
-
- vp = fr_dcursor_current(cursor);
- if (!vp || (vp->da != da)) break; /* Stop if we have an attribute of a different type */
- }
-
- return fr_dbuff_set(dbuff, &work_dbuff);
-}
static ssize_t encode_vsio_hdr(fr_dbuff_t *dbuff,
fr_da_stack_t *da_stack, unsigned int depth,
* Write out the option's value
*/
if (da->flags.array) {
- len = encode_array(&work_dbuff, da_stack, depth, cursor, encode_ctx);
+ len = fr_pair_array_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value);
} else {
len = encode_value(&work_dbuff, da_stack, depth, cursor, encode_ctx);
}
#include <freeradius-devel/util/dns.h>
#include <freeradius-devel/util/proto.h>
#include <freeradius-devel/util/struct.h>
+#include <freeradius-devel/util/encode.h>
#include "dns.h"
#include "attrs.h"
}
-static inline ssize_t encode_array(fr_dbuff_t *dbuff,
- fr_da_stack_t *da_stack, int depth,
- fr_dcursor_t *cursor, void *encode_ctx);
-
-static ssize_t encode_value_trampoline(fr_dbuff_t *dbuff,
- fr_da_stack_t *da_stack, unsigned int depth,
- fr_dcursor_t *cursor, void *encode_ctx)
-{
- fr_dict_attr_t const *da = da_stack->da[depth];
-
- /*
- * Write out the option's value
- */
- if (da->flags.array) {
- return encode_array(dbuff, da_stack, depth, cursor, encode_ctx);
- }
-
- return encode_value(dbuff, da_stack, depth, cursor, encode_ctx);
-}
-
static ssize_t encode_value(fr_dbuff_t *dbuff,
fr_da_stack_t *da_stack, unsigned int depth,
fr_dcursor_t *cursor, void *encode_ctx)
fr_pair_dcursor_init(&child_cursor, &vp->vp_group);
- slen = fr_struct_to_network(&work_dbuff, da_stack, depth, &child_cursor, encode_ctx, encode_value_trampoline, encode_tlv);
+ slen = fr_struct_to_network(&work_dbuff, da_stack, depth, &child_cursor, encode_ctx, encode_value, encode_tlv);
if (slen < 0) return slen;
/*
* Flat-list
*/
if (da->type == FR_TYPE_STRUCT) {
- slen = fr_struct_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value_trampoline, encode_tlv);
+ slen = fr_struct_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value, encode_tlv);
if (slen <= 0) return slen;
/*
return fr_dbuff_set(dbuff, &work_dbuff);
}
-static inline ssize_t encode_array(fr_dbuff_t *dbuff,
- fr_da_stack_t *da_stack, int depth,
- fr_dcursor_t *cursor, void *encode_ctx)
-{
- ssize_t slen;
- fr_dbuff_t work_dbuff = FR_DBUFF(dbuff);
- fr_pair_t *vp;
- fr_dict_attr_t const *da = da_stack->da[depth];
-
- FR_PROTO_STACK_PRINT(da_stack, depth);
-
- if (!fr_cond_assert_msg(da->flags.array,
- "%s: Internal sanity check failed, attribute \"%s\" does not have array bit set",
- __FUNCTION__, da->name)) return PAIR_ENCODE_FATAL_ERROR;
-
- while (fr_dbuff_extend(&work_dbuff)) {
- fr_dbuff_t element_dbuff = FR_DBUFF(&work_dbuff);
-
- slen = encode_value(&element_dbuff, da_stack, depth, cursor, encode_ctx);
- if (slen < 0) return slen;
-
- fr_dbuff_set(&work_dbuff, &element_dbuff);
-
- vp = fr_dcursor_current(cursor);
- if (!vp || (vp->da != da)) break; /* Stop if we have an attribute of a different type */
- }
-
- return fr_dbuff_set(dbuff, &work_dbuff);
-}
-
static ssize_t encode_option_data(fr_dbuff_t *dbuff,
fr_da_stack_t *da_stack, unsigned int depth,
fr_dcursor_t *cursor, void *encode_ctx)
* Write out the option's value
*/
if (da->flags.array) {
- len = encode_array(&work_dbuff, da_stack, depth, cursor, encode_ctx);
+ len = fr_pair_array_to_network(&work_dbuff, da_stack, depth, cursor, encode_ctx, encode_value);
} else {
len = encode_value(&work_dbuff, da_stack, depth, cursor, encode_ctx);
}
fr_pair_dcursor_init(&child_cursor, &vp->vp_group);
- slen = fr_struct_to_network(&work_dbuff, &da_stack, 0, &child_cursor, encode_ctx, encode_value_trampoline, encode_tlv);
+ slen = fr_struct_to_network(&work_dbuff, &da_stack, 0, &child_cursor, encode_ctx, encode_value, encode_tlv);
if (slen <= 0) return slen;
(void) fr_dcursor_next(cursor);
} else {
- slen = fr_struct_to_network(&work_dbuff, &da_stack, 0, cursor, encode_ctx, encode_value_trampoline, encode_tlv);
+ slen = fr_struct_to_network(&work_dbuff, &da_stack, 0, cursor, encode_ctx, encode_value, encode_tlv);
if (slen <= 0) return slen;
}
fr_dcursor_t child_cursor;
fr_pair_dcursor_init(&child_cursor, &vp->vp_group);
- slen = fr_struct_to_network(&work_dbuff, da_stack, 0, &child_cursor, packet_ctx, encode_value_trampoline, encode_tlv);
+ slen = fr_struct_to_network(&work_dbuff, da_stack, 0, &child_cursor, packet_ctx, encode_value, encode_tlv);
if (slen <= 0) return slen;
count++;
fr_pair_dcursor_init(&child_cursor, &vp->vp_group);
fr_proto_da_stack_build(&da_stack, attr_dns_packet);
- slen = fr_struct_to_network(&work_dbuff, &da_stack, 0, &cursor, packet_ctx, encode_value_trampoline, NULL);
+ slen = fr_struct_to_network(&work_dbuff, &da_stack, 0, &cursor, packet_ctx, encode_value, NULL);
if (slen <= 0) return slen;
fr_assert(slen == DNS_HDR_LEN);