Coverage Report

Created: 2025-12-14 06:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/util/dict_print.c
Line
Count
Source
1
/*
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
6
 *
7
 *   This program 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
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the Free Software
14
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15
 */
16
17
/** Print dictionary attributes, flags, etc...
18
 *
19
 * @file src/lib/util/dict_print.c
20
 *
21
 * @copyright 2019 The FreeRADIUS server project
22
 */
23
RCSID("$Id: b36816f2104baa8c131107f058a8cc2d83e55cbc $")
24
25
#include <freeradius-devel/util/dict_priv.h>
26
#include <freeradius-devel/util/print.h>
27
#include <freeradius-devel/util/proto.h>
28
29
ssize_t fr_dict_attr_flags_print(fr_sbuff_t *out, fr_dict_t const *dict, fr_type_t type, fr_dict_attr_flags_t const *flags)
30
0
{
31
0
  fr_sbuff_t  our_out = FR_SBUFF(out);
32
33
0
#define FLAG_SET(_flag) if (flags->_flag) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, STRINGIFY(_flag)",")
34
35
0
  FLAG_SET(is_root);
36
0
  FLAG_SET(is_unknown);
37
0
  FLAG_SET(is_raw);
38
0
  FLAG_SET(is_alias);
39
0
  FLAG_SET(has_alias);
40
0
  FLAG_SET(internal);
41
0
  FLAG_SET(array);
42
0
  FLAG_SET(has_value);
43
0
  FLAG_SET(counter);
44
0
  FLAG_SET(name_only);
45
46
0
  if (dict && !flags->extra && flags->subtype) {
47
0
    FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%u", flags->subtype);
48
0
    FR_SBUFF_IN_CHAR_RETURN(&our_out, ',');
49
0
  }
50
51
0
  if (flags->length) {
52
0
    switch (type) {
53
0
    case FR_TYPE_FIXED_SIZE:
54
      /*
55
       *  Bit fields are in the dicts as various
56
       *  `uint*` types.  But with special flags
57
       *  saying they're bit fields.
58
       */
59
0
      if (flags->extra && (flags->subtype == FLAG_BIT_FIELD)) {
60
0
        FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "bit[%u],", flags->length);
61
0
      }
62
0
      break;
63
64
0
    default:
65
0
      FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "length=%i,", flags->length);
66
0
      break;
67
0
    }
68
0
  }
69
0
  if (flags->extra) {
70
0
    switch (flags->subtype) {
71
0
    case FLAG_KEY_FIELD:
72
0
      FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "key,");
73
0
      break;
74
75
0
    case FLAG_BIT_FIELD:
76
0
      break;
77
78
0
    case FLAG_LENGTH_UINT8:
79
0
      FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "length=uint8,");
80
0
      break;
81
82
0
    case FLAG_LENGTH_UINT16:
83
0
      FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "length=uint16,");
84
0
      break;
85
86
0
    default:
87
0
      break;
88
0
    }
89
0
  }
90
91
  /*
92
   *  Print out the date precision.
93
   */
94
0
  if ((type == FR_TYPE_DATE) || (type == FR_TYPE_TIME_DELTA)) {
95
0
    FR_SBUFF_IN_STRCPY_RETURN(&our_out,
96
0
            fr_table_str_by_value(fr_time_precision_table, flags->flag_time_res, "?"));
97
0
    FR_SBUFF_IN_CHAR_RETURN(&our_out, ',');
98
0
    if (flags->is_unsigned) FR_SBUFF_IN_CHAR_RETURN(&our_out, 'u');
99
0
    FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "int%d", flags->length << 3);
100
0
  }
101
102
  /*
103
   *  Remove trailing commas.
104
   */
105
0
  fr_sbuff_trim(&our_out, (bool[UINT8_MAX + 1]){ [','] = true });
106
107
  /*
108
   *  Ensure that the buffer is initialized.
109
   */
110
0
  if (fr_sbuff_used(&our_out) == 0) FR_SBUFF_IN_CHAR_RETURN(&our_out, '\0');
111
112
0
  FR_SBUFF_SET_RETURN(out, &our_out);
113
0
}
114
115
/** Build the da_stack for the specified DA and encode the path by name in OID form
116
 *
117
 * @param[out] out    Where to write the OID.
118
 * @param[in] ancestor    If not NULL, only print OID portion between ancestor and da.
119
 * @param[in] da    to print OID string for.
120
 * @param[in] numeric   print the OID components as numbers, not attribute names.
121
 * @return
122
 *  - >0 The number of bytes written to the buffer.
123
 *  - <= 0 The number of bytes we would have needed to write the
124
 *        next OID component.
125
 */
126
ssize_t fr_dict_attr_oid_print(fr_sbuff_t *out,
127
             fr_dict_attr_t const *ancestor, fr_dict_attr_t const *da, bool numeric)
128
0
{
129
0
  int     i;
130
0
  int     depth = 0;
131
0
  fr_da_stack_t   da_stack;
132
0
  fr_sbuff_t    our_out = FR_SBUFF(out);
133
134
  /*
135
   *  If the ancestor and the DA match, there's
136
   *  no OID string to print.
137
   */
138
0
  if ((ancestor == da) || (da->depth == 0)) return 0;
139
140
0
  if (ancestor && (ancestor->flags.is_root)) ancestor = NULL;
141
142
0
  fr_proto_da_stack_build(&da_stack, da);
143
144
  /*
145
   *  We may have swapped from a known to an unknown
146
   *  attribute.  We still print out the unknown one.
147
   */
148
0
  if (ancestor && da->flags.is_unknown) {
149
0
    fr_assert(da->depth > ancestor->depth);
150
151
0
    ancestor = da_stack.da[ancestor->depth - 1];
152
0
  }
153
154
0
  if (ancestor) {
155
0
    if (da_stack.da[ancestor->depth - 1] != ancestor) {
156
0
      fr_strerror_printf("Attribute '%s' is not a descendent of \"%s\"", da->name, ancestor->name);
157
0
      return 0;
158
0
    }
159
0
    depth = ancestor->depth;
160
0
  }
161
162
  /*
163
   *  We don't print the ancestor, we print the OID
164
   *  between it and the da.
165
   */
166
0
  if (numeric) {
167
0
    FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%u", da_stack.da[depth]->attr);
168
0
    for (i = depth + 1; i < (int)da->depth; i++) {
169
0
      FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
170
0
      FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%u", da_stack.da[i]->attr);
171
0
    }
172
0
  } else {
173
0
    FR_SBUFF_IN_STRCPY_RETURN(&our_out, da_stack.da[depth]->name);
174
0
    for (i = depth + 1; i < (int)da->depth; i++) {
175
0
      FR_SBUFF_IN_CHAR_RETURN(&our_out, '.');
176
0
      FR_SBUFF_IN_STRCPY_RETURN(&our_out, da_stack.da[i]->name);
177
0
    }
178
0
  }
179
0
  FR_SBUFF_SET_RETURN(out, &our_out);
180
0
}
181
182
typedef struct {
183
  FILE      *fp;
184
  fr_dict_t const   *dict;
185
  fr_dict_attr_t const  *da;    //!< where we started
186
  char      prefix[256];
187
  char      flags[256];
188
  char      oid[256];
189
  unsigned int    start_depth;
190
} fr_dict_attr_debug_t;
191
192
static int dict_attr_debug(fr_dict_attr_t const *da, void *uctx)
193
0
{
194
0
  fr_dict_attr_debug_t    *ctx = uctx;
195
0
  fr_hash_iter_t      iter;
196
0
  fr_dict_enum_value_t const  *enumv;
197
0
  fr_dict_attr_ext_enumv_t  *ext;
198
199
  /*
200
   *  Don't print it twice.
201
   */
202
0
  if (da == ctx->da) return 0;
203
204
0
  fr_dict_attr_flags_print(&FR_SBUFF_OUT(ctx->flags, sizeof(ctx->flags)),
205
0
            ctx->dict, da->type, &da->flags);
206
207
0
  snprintf(ctx->prefix, sizeof(ctx->prefix),
208
0
     "[%02u] 0x%016" PRIxPTR "%*s - ",
209
0
     da->depth,
210
0
     (unsigned long)da,
211
0
     (da->depth - ctx->start_depth) * 4, "");
212
213
0
  fprintf(ctx->fp, "%s%s(%u) %s %s\n",
214
0
    ctx->prefix,
215
0
    da->name,
216
0
    da->attr,
217
0
    fr_type_to_str(da->type),
218
0
    ctx->flags);
219
220
0
  dict_attr_ext_debug(ctx->prefix, da); /* Print all the extension debug info */
221
222
0
  ext = fr_dict_attr_ext(da, FR_DICT_ATTR_EXT_ENUMV);
223
0
  if (!ext || !ext->name_by_value) return 0;
224
225
0
  for (enumv = fr_hash_table_iter_init(ext->name_by_value, &iter);
226
0
       enumv;
227
0
       enumv = fr_hash_table_iter_next(ext->name_by_value, &iter)) {
228
0
        char *value = fr_asprintf(NULL, "%pV", enumv->value);
229
230
0
    fprintf(ctx->fp, "%s    %s -> %s\n",
231
0
      ctx->prefix,
232
0
      enumv->name,
233
0
      value);
234
0
    talloc_free(value);
235
0
  }
236
237
0
  return 0;
238
0
}
239
240
void fr_dict_namespace_debug(FILE *fp, fr_dict_attr_t const *da)
241
0
{
242
0
  fr_dict_attr_debug_t    uctx = {
243
0
    .fp = fp,
244
0
    .dict = fr_dict_by_da(da),
245
0
    .start_depth = da->depth,
246
0
  };
247
0
  fr_hash_table_t   *namespace;
248
0
  fr_hash_iter_t    iter;
249
0
  fr_dict_attr_t    *our_da;
250
251
0
  namespace = dict_attr_namespace(da);
252
0
  if (!namespace) {
253
0
    fprintf(fp, "%s does not have namespace\n", da->name);
254
0
    return;
255
0
  }
256
257
0
  for (our_da = fr_hash_table_iter_init(namespace, &iter);
258
0
       our_da;
259
0
       our_da = fr_hash_table_iter_next(namespace, &iter)) {
260
0
    dict_attr_debug(our_da, &uctx);
261
0
  }
262
0
}
263
264
void fr_dict_attr_debug(FILE *fp, fr_dict_attr_t const *da)
265
0
{
266
0
  fr_dict_attr_debug_t  uctx = {
267
0
    .fp = fp,
268
0
    .dict = fr_dict_by_da(da),
269
0
    .start_depth = da->depth,
270
0
  };
271
272
0
  dict_attr_debug(da, &uctx);
273
0
  uctx.da = da;
274
275
0
  (void)fr_dict_walk(da, dict_attr_debug, &uctx);
276
0
}
277
278
void fr_dict_debug(FILE *fp, fr_dict_t const *dict)
279
0
{
280
0
  fr_dict_attr_debug(fp, fr_dict_root(dict));
281
0
}
282
283
static int dict_attr_export(fr_dict_attr_t const *da, void *uctx)
284
0
{
285
0
  fr_dict_attr_debug_t    *ctx = uctx;
286
287
0
  (void) fr_dict_attr_oid_print(&FR_SBUFF_OUT(ctx->prefix, sizeof(ctx->prefix)),
288
0
              NULL, da, false);
289
0
  (void) fr_dict_attr_oid_print(&FR_SBUFF_OUT(ctx->oid, sizeof(ctx->oid)),
290
0
              NULL, da, true);
291
292
0
  *ctx->flags = 0;  /* some attributes don't have flags */
293
0
  fr_dict_attr_flags_print(&FR_SBUFF_OUT(ctx->flags, sizeof(ctx->flags)),
294
0
         ctx->dict, da->type, &da->flags);
295
0
  fprintf(ctx->fp, "ATTRIBUTE\t%-40s\t%-20s\t%s\t%s\n",
296
0
    ctx->prefix,
297
0
    ctx->oid,
298
0
    fr_type_to_str(da->type),
299
0
    ctx->flags);
300
301
0
  return 0;
302
0
}
303
304
static void fr_dict_attr_export(FILE *fp, fr_dict_attr_t const *da)
305
0
{
306
0
  fr_dict_attr_debug_t  uctx = {
307
0
    .fp = fp,
308
0
    .dict = fr_dict_by_da(da),
309
0
    .start_depth = da->depth
310
0
  };
311
312
0
  dict_attr_export(da, &uctx);
313
0
  (void)fr_dict_walk(da, dict_attr_export, &uctx);
314
0
}
315
316
/** Export in the standard form: ATTRIBUTE name oid flags
317
 *
318
 */
319
void fr_dict_export(FILE *fp, fr_dict_t const *dict)
320
0
{
321
0
  fr_dict_attr_export(fp, fr_dict_root(dict));
322
0
}
323
324
void fr_dict_alias_export(FILE *fp, fr_dict_attr_t const *parent)
325
0
{
326
0
  fr_hash_table_t   *namespace;
327
0
  fr_hash_iter_t    iter;
328
0
  fr_dict_attr_t    *da;
329
0
  char buffer   [256];
330
331
0
  namespace = dict_attr_namespace(parent);
332
0
  if (!namespace) {
333
0
    fprintf(fp, "%s does not have namespace\n", parent->name);
334
0
    return;
335
0
  }
336
337
0
  for (da = fr_hash_table_iter_init(namespace, &iter);
338
0
       da;
339
0
       da = fr_hash_table_iter_next(namespace, &iter)) {
340
0
    fr_dict_attr_t const *ref;
341
342
0
    if (!da->flags.is_alias) continue;
343
344
0
    if (!fr_type_is_leaf(da->type)) continue;
345
346
0
    ref = fr_dict_attr_ref(da);
347
0
    if (!ref) continue;
348
349
0
    if (da->depth == ref->depth) continue;
350
351
#ifdef STATIC_ANALYZER
352
    buffer[0] = '\0';
353
#endif
354
355
0
    (void) fr_dict_attr_oid_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)),
356
0
                NULL, ref, false);
357
358
0
    fprintf(fp, "%-40s\t%s\n", da->name, buffer);
359
0
  }
360
0
}