Coverage Report

Created: 2026-03-31 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/util/encode.c
Line
Count
Source
1
/*
2
 *   This library is free software; you can redistribute it and/or
3
 *   modify it under the terms of the GNU Lesser General Public
4
 *   License as published by the Free Software Foundation; either
5
 *   version 2.1 of the License, or (at your option) any later version.
6
 *
7
 *   This library is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10
 *   Lesser General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU Lesser General Public
13
 *   License along with this library; if not, write to the Free Software
14
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15
 */
16
17
/**
18
 * $Id: a99b1b9d8b86b4f27b6f371f0cfee7cd52eaea91 $
19
 *
20
 * @file src/lib/util/encode.c
21
 * @brief Generic functions for decoding protocols.
22
 *
23
 * @copyright 2022 Network RADIUS SAS (legal@networkradius.com)
24
 */
25
#include <freeradius-devel/io/test_point.h>
26
#include <freeradius-devel/util/proto.h>
27
#include <freeradius-devel/util/encode.h>
28
29
/** Encode an array of values from the network
30
 *
31
 * @param[out] dbuff    buffer to write the TLV to.
32
 * @param[in] da_stack    Describing nesting of options.
33
 * @param[in] depth   in the da_stack.
34
 * @param[in,out] cursor  Current attribute we're encoding.
35
 * @param[in] encode_ctx  Containing DHCPv4 dictionary.
36
 * @param[in] encode_value  Function to perform encoding of a single value.
37
 * @return
38
 *  - >0 length of data encoded.
39
 *  - <= 0 on error.
40
 */
41
ssize_t fr_pair_array_to_network(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, int depth,
42
         fr_dcursor_t *cursor, void *encode_ctx, fr_encode_dbuff_t encode_value)
43
0
{
44
0
  ssize_t     slen;
45
0
  fr_dbuff_t    work_dbuff = FR_DBUFF(dbuff);
46
0
  fr_pair_t   *vp;
47
0
  fr_dict_attr_t const  *da = da_stack->da[depth];
48
49
0
  FR_PROTO_STACK_PRINT(da_stack, depth);
50
51
0
  if (!fr_cond_assert_msg(da->flags.array,
52
0
        "%s: Internal sanity check failed, attribute \"%s\" does not have array bit set",
53
0
        __FUNCTION__, da->name)) return PAIR_ENCODE_FATAL_ERROR;
54
55
0
  while (fr_dbuff_extend(&work_dbuff)) {
56
0
    fr_dbuff_t  element_dbuff = FR_DBUFF(&work_dbuff);
57
58
    /*
59
     *  Encoding "no data" in an array doesn't make sense.
60
     */
61
0
    slen = encode_value(&element_dbuff, da_stack, depth, cursor, encode_ctx);
62
0
    if (slen <= 0) return slen;
63
64
0
    fr_dbuff_set(&work_dbuff, &element_dbuff);
65
66
0
    vp = fr_dcursor_current(cursor);
67
0
    if (!vp || (vp->da != da)) break;   /* Stop if we have an attribute of a different type */
68
0
  }
69
70
0
  return fr_dbuff_set(dbuff, &work_dbuff);
71
0
}
72
73
ssize_t fr_pair_cursor_to_network(fr_dbuff_t *dbuff,
74
          fr_da_stack_t *da_stack, unsigned int depth,
75
          fr_dcursor_t *cursor, void *encode_ctx, fr_encode_dbuff_t encode_pair)
76
0
{
77
0
  fr_dbuff_t    work_dbuff = FR_DBUFF(dbuff);
78
0
  fr_pair_t const   *vp;
79
0
  ssize_t     len;
80
81
0
  while (true) {
82
0
    FR_PROTO_STACK_PRINT(da_stack, depth);
83
84
0
    vp = fr_dcursor_current(cursor);
85
0
    fr_assert(!vp->da->flags.internal);
86
87
0
    len = encode_pair(&work_dbuff, da_stack, depth + 1, cursor, encode_ctx);
88
0
    if (len < 0) return len;
89
90
    /*
91
     *  If nothing updated the attribute, stop
92
     */
93
0
    if (!fr_dcursor_current(cursor) || (vp == fr_dcursor_current(cursor))) break;
94
95
0
    vp = fr_dcursor_current(cursor);
96
0
    if (!vp) break;
97
98
0
    fr_proto_da_stack_build(da_stack, vp->da);
99
0
  }
100
101
0
  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "Done cursor");
102
103
0
  return fr_dbuff_set(dbuff, &work_dbuff);
104
0
}
105
106
/** Encode a foreign reference to the network
107
 *
108
 * @param[out] dbuff    buffer to write the TLV to.
109
 * @param[in] da_stack    Describing nesting of options.
110
 * @param[in] depth   in the da_stack.
111
 * @param[in,out] cursor  Current attribute we're encoding.
112
 * @return
113
 *  - >0 length of data encoded.
114
 *  - 0 if we ran out of space.
115
 *  - < 0 on error.
116
 */
117
ssize_t fr_pair_ref_to_network(fr_dbuff_t *dbuff, fr_da_stack_t *da_stack, unsigned int depth,
118
             fr_dcursor_t *cursor)
119
0
{
120
0
  ssize_t     slen;
121
0
  fr_dict_attr_t const  *da;
122
0
  fr_pair_t const   *vp = fr_dcursor_current(cursor);
123
0
  fr_dbuff_t    work_dbuff = FR_DBUFF(dbuff);
124
125
0
  fr_dict_attr_t const *ref;
126
0
  fr_dict_protocol_t const *proto;
127
128
0
  FR_PROTO_STACK_PRINT(da_stack, depth);
129
130
0
  da = da_stack->da[depth];
131
0
  fr_assert(da->type == FR_TYPE_GROUP);
132
133
0
  ref = fr_dict_attr_ref(da);
134
0
  if (!ref) {
135
0
    fr_strerror_printf("Invalid attribute reference for %s", da->name);
136
0
    return 0;
137
0
  }
138
139
0
  proto = fr_dict_protocol(ref->dict);
140
0
  fr_assert(proto != NULL);
141
142
0
  if (!proto->encode) {
143
0
    fr_strerror_printf("Attribute %s -> %s does not have an encoder", da->name, ref->name);
144
0
    return 0;
145
0
  }
146
147
  /*
148
   *  The foreign functions don't take a cursor, so we have to update the cursor ourselves.
149
   */
150
0
  slen = proto->encode(&work_dbuff, &vp->vp_group);
151
0
  if (slen < 0) return slen;
152
153
0
  FR_PROTO_HEX_DUMP(fr_dbuff_start(&work_dbuff), fr_dbuff_used(&work_dbuff), "group ref");
154
155
0
  vp = fr_dcursor_next(cursor);
156
0
  fr_proto_da_stack_build(da_stack, vp ? vp->da : NULL);
157
158
0
  return fr_dbuff_set(dbuff, &work_dbuff);
159
0
}
160
161
/** Generic encode value.
162
 *
163
 */
164
ssize_t fr_pair_encode_value(fr_dbuff_t *dbuff, UNUSED fr_da_stack_t *da_stack, UNUSED unsigned int depth,
165
            fr_dcursor_t *cursor, UNUSED void *encode_ctx)
166
0
{
167
0
  fr_pair_t const   *vp = fr_dcursor_current(cursor);
168
0
  fr_dbuff_t    work_dbuff = FR_DBUFF(dbuff);
169
170
0
  if (!fr_type_is_leaf(vp->vp_type)) {
171
0
    FR_PROTO_TRACE("Cannot use generic encoder for data type %s", fr_type_to_str(vp->vp_type));
172
0
    fr_strerror_printf("Cannot encode data type %s", fr_type_to_str(vp->vp_type));
173
0
    return PAIR_ENCODE_FATAL_ERROR;
174
0
  }
175
176
0
  if (fr_value_box_to_network(&work_dbuff, &vp->data) <= 0) return PAIR_ENCODE_FATAL_ERROR;
177
178
0
  (void) fr_dcursor_next(cursor);
179
180
0
  return fr_dbuff_set(dbuff, &work_dbuff); 
181
0
}