Coverage Report

Created: 2026-05-11 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/server/request.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
/**
18
 * $Id: 4ab9c9fb47727b3ec532ad67adc3ed94ee7e1b6e $
19
 *
20
 * @brief Functions for allocating requests and storing internal data in them.
21
 * @file src/lib/server/request.c
22
 *
23
 * @copyright 2015 The FreeRADIUS server project
24
 */
25
RCSID("$Id: 4ab9c9fb47727b3ec532ad67adc3ed94ee7e1b6e $")
26
27
#include <freeradius-devel/server/request.h>
28
#include <freeradius-devel/server/request_data.h>
29
#include <freeradius-devel/unlang/interpret.h>
30
31
#include <freeradius-devel/util/debug.h>
32
#include <freeradius-devel/util/atexit.h>
33
34
static fr_dict_t const *dict_freeradius;
35
36
extern fr_dict_autoload_t request_dict[];
37
fr_dict_autoload_t request_dict[] = {
38
  { .out = &dict_freeradius, .proto = "freeradius" },
39
  DICT_AUTOLOAD_TERMINATOR
40
};
41
42
fr_dict_attr_t const *request_attr_root;
43
fr_dict_attr_t const *request_attr_request;
44
fr_dict_attr_t const *request_attr_reply;
45
fr_dict_attr_t const *request_attr_control;
46
fr_dict_attr_t const *request_attr_state;
47
fr_dict_attr_t const *request_attr_local;
48
49
extern fr_dict_attr_autoload_t request_dict_attr[];
50
fr_dict_attr_autoload_t request_dict_attr[] = {
51
  { .out = &request_attr_root, .name = "root", .type = FR_TYPE_GROUP, .dict = &dict_freeradius },
52
  { .out = &request_attr_request, .name = "request", .type = FR_TYPE_GROUP, .dict = &dict_freeradius },
53
  { .out = &request_attr_reply, .name = "reply", .type = FR_TYPE_GROUP, .dict = &dict_freeradius },
54
  { .out = &request_attr_control, .name = "control", .type = FR_TYPE_GROUP, .dict = &dict_freeradius },
55
  { .out = &request_attr_state, .name = "session-state", .type = FR_TYPE_GROUP, .dict = &dict_freeradius },
56
  { .out = &request_attr_local, .name = "local-variables", .type = FR_TYPE_GROUP, .dict = &dict_freeradius },
57
  DICT_AUTOLOAD_TERMINATOR
58
};
59
60
#ifndef NDEBUG
61
static int _state_ctx_free(fr_pair_t *state)
62
0
{
63
0
  DEBUG4("state-ctx %p freed", state);
64
65
0
  return 0;
66
0
}
67
#endif
68
69
static inline void CC_HINT(always_inline) request_log_init_orphan(request_t *request)
70
0
{
71
  /*
72
   *  These may be changed later by request_pre_handler
73
   */
74
0
  request->log.lvl = fr_debug_lvl;  /* Default to global debug level */
75
0
  if (!request->log.dst) {
76
0
    request->log.dst = talloc_zero(request, log_dst_t);
77
0
  } else {
78
0
    memset(request->log.dst, 0, sizeof(*request->log.dst));
79
0
  }
80
0
  request->log.dst->func = vlog_request;
81
0
  request->log.dst->uctx = &default_log;
82
0
  request->log.dst->lvl = fr_debug_lvl;
83
0
}
84
85
/** Prepend another logging destination to the list.
86
 *
87
88
 * @param request the request
89
 * @param log_dst the logging destination
90
 * @param lvl   the new request debug lvl
91
 */
92
void request_log_prepend(request_t *request, fr_log_t *log_dst, fr_log_lvl_t lvl)
93
0
{
94
0
  log_dst_t *dst;
95
96
0
  if (lvl == L_DBG_LVL_DISABLE) {
97
0
    while (request->log.dst) {
98
0
      dst = request->log.dst->next;
99
0
      talloc_free(request->log.dst);
100
0
      request->log.dst = dst;
101
0
    }
102
0
    request->log.lvl = L_DBG_LVL_OFF;
103
0
    return;
104
0
  }
105
106
  /*
107
   *  Remove a particular log destination.
108
   */
109
0
  if (lvl == L_DBG_LVL_OFF) {
110
0
    log_dst_t **last;
111
112
0
    last = &request->log.dst;
113
0
    while (*last) {
114
0
      dst = *last;
115
0
      if (((fr_log_t *)dst->uctx)->parent == log_dst) {
116
0
        *last = dst->next;
117
0
        talloc_free(dst);
118
0
        if (!request->log.dst) request->log.lvl = L_DBG_LVL_OFF;
119
0
        return;
120
0
      }
121
122
0
      last = &(dst->next);
123
0
    }
124
125
0
    return;
126
0
  }
127
128
  /*
129
   *  Change the debug level of an existing destination.
130
   */
131
0
  for (dst = request->log.dst; dst != NULL; dst = dst->next) {
132
0
    if (((fr_log_t *)dst->uctx)->parent == log_dst) {
133
0
      dst->lvl = lvl;
134
0
      if (lvl > request->log.lvl) request->log.lvl = lvl;
135
0
      return;
136
0
    }
137
0
  }
138
139
  /*
140
   *  Not found, add a new log destination.
141
   */
142
0
  MEM(dst = talloc_zero(request, log_dst_t));
143
144
0
  dst->func = vlog_request;
145
0
  dst->uctx = log_dst;
146
147
0
  dst->lvl = lvl;
148
0
  if (lvl > request->log.lvl) request->log.lvl = lvl;
149
0
  dst->next = request->log.dst;
150
151
0
  request->log.dst = dst;
152
0
}
153
154
static inline void CC_HINT(always_inline) request_log_init_child(request_t *child, request_t const *parent)
155
0
{
156
  /*
157
   *  Copy debug information.
158
   */
159
0
  memcpy(&(child->log), &(parent->log), sizeof(child->log));
160
0
  child->log.indent.unlang = 0; /* Apart from the indent which we reset */
161
0
  child->log.indent.module = 0; /* Apart from the indent which we reset */
162
0
  child->log.lvl = parent->log.lvl;
163
0
}
164
165
static inline void CC_HINT(always_inline) request_log_init_detachable(request_t *child, request_t const *parent)
166
0
{
167
0
  request_log_init_child(child, parent);
168
169
  /*
170
   *  Ensure that we use our own version of the logging
171
   *  information, and not the original request one.
172
   */
173
0
  child->log.dst = talloc_zero(child, log_dst_t);
174
0
  memcpy(child->log.dst, parent->log.dst, sizeof(*child->log.dst));
175
0
}
176
177
static inline CC_HINT(always_inline) int request_detachable_init(request_t *child, request_t *parent)
178
0
{
179
  /*
180
   *  Associate the child with the parent, using the child's
181
   *  pointer as a unique identifier.  Free it if the parent
182
   *  goes away, but don't persist it across
183
   *  challenge-response boundaries.
184
   */
185
0
  if (request_data_talloc_add(parent, child, 0, request_t, child, true, true, false) < 0) return -1;
186
187
0
  return 0;
188
0
}
189
190
static inline CC_HINT(always_inline) int request_child_init(request_t *child, request_t *parent)
191
0
{
192
0
  child->number = parent->child_number++;
193
0
  if (!child->proto_dict) {
194
0
    child->proto_dict = parent->proto_dict;
195
0
    child->local_dict = parent->proto_dict;
196
0
  }
197
198
0
  if ((parent->seq_start == 0) || (parent->number == parent->seq_start)) {
199
0
    child->name = talloc_typed_asprintf(child, "%s.%" PRIu64, parent->name, child->number);
200
0
  } else {
201
0
    child->name = talloc_typed_asprintf(child, "(%s,%" PRIu64 ").%" PRIu64,
202
0
               parent->name, parent->seq_start, child->number);
203
0
  }
204
0
  child->seq_start = 0; /* children always start with their own sequence */
205
0
  child->parent = parent;
206
207
  /*
208
   *  For new server support.
209
   *
210
   *  FIXME: Key instead off of a "virtual server" data structure.
211
   *
212
   *  FIXME: Permit different servers for inner && outer sessions?
213
   */
214
0
  child->packet = fr_packet_alloc(child, true);
215
0
  if (!child->packet) {
216
0
    talloc_free(child);
217
0
    return -1;
218
0
  }
219
220
0
  child->reply = fr_packet_alloc(child, false);
221
0
  if (!child->reply) {
222
0
    talloc_free(child);
223
0
    return -1;
224
0
  }
225
226
0
  return 0;
227
0
}
228
229
/** Setup logging and other fields for a request
230
 *
231
 * @param[in] file    the request was allocated in.
232
 * @param[in] line    the request was allocated on.
233
 * @param[in] request   to (re)-initialise.
234
 * @param[in] type    of request to initialise.
235
 * @param[in] args    Other optional arguments.
236
 */
237
int _request_init(char const *file, int line,
238
      request_t *request, request_type_t type,
239
      request_init_args_t const *args)
240
0
{
241
0
  fr_dict_t const *dict;
242
243
  /*
244
   *  Sanity checks for different requests types
245
   */
246
0
  switch (type) {
247
0
  case REQUEST_TYPE_EXTERNAL:
248
0
    fr_assert(args);
249
250
0
    if (!fr_cond_assert_msg(!args->parent, "External requests must NOT have a parent")) return -1;
251
252
0
    fr_assert(args->namespace);
253
254
0
    dict = args->namespace;
255
0
    break;
256
257
0
  case REQUEST_TYPE_INTERNAL:
258
0
    if (!args || !args->namespace) {
259
0
      dict = fr_dict_internal();
260
0
    } else {
261
0
      dict = args->namespace;
262
0
    }
263
0
    break;
264
265
0
  case REQUEST_TYPE_DETACHED:
266
0
    fr_assert_fail("Detached requests should start as type == REQUEST_TYPE_INTERNAL, "
267
0
             "args->detachable and be detached later");
268
0
    return -1;
269
270
  /* Quiet GCC */
271
0
  default:
272
0
    fr_assert_fail("Invalid request type");
273
0
    return -1;
274
0
  }
275
276
0
  *request = (request_t){
277
0
#ifndef NDEBUG
278
0
    .magic = REQUEST_MAGIC,
279
0
#endif
280
0
    .type = type,
281
0
    .master_state = REQUEST_ACTIVE,
282
0
    .proto_dict = fr_dict_proto_dict(dict),
283
0
    .local_dict = dict,
284
0
    .component = "<pre-core>",
285
0
    .flags = {
286
0
      .detachable = args && args->detachable,
287
0
    },
288
0
    .alloc_file = file,
289
0
    .alloc_line = line
290
0
  };
291
292
293
  /*
294
   *  Initialise the stack
295
   */
296
0
  MEM(request->stack = unlang_interpret_stack_alloc(request));
297
298
  /*
299
   *  Initialise the request data list
300
   */
301
0
  request_data_list_init(&request->data);
302
303
0
  {
304
0
    fr_pair_t *vp = NULL, *pair_root;
305
306
    /*
307
     *  Alloc the pair root this is a
308
     *  special pair which does not
309
     *  free its children when it is
310
     *  freed.
311
     */
312
0
    pair_root = fr_pair_root_afrom_da(request, request_attr_root);
313
0
    if (unlikely(!pair_root)) return -1;
314
0
    request->pair_root = pair_root;
315
316
    /*
317
     *  Copy all the pair lists over into
318
     *  the request.  We then check for
319
     *  the any uninitialised lists and
320
     *  create them locally.
321
     */
322
0
    if (args) memcpy(&request->pair_list, &args->pair_list, sizeof(request->pair_list));
323
324
0
#define list_init(_ctx, _list) \
325
0
  do { \
326
0
    vp = fr_pair_afrom_da(_ctx, request_attr_##_list); \
327
0
    if (unlikely(!vp)) { \
328
0
      talloc_free(pair_root); \
329
0
      memset(&request->pair_list, 0, sizeof(request->pair_list)); \
330
0
      return -1; \
331
0
    } \
332
0
    fr_pair_append(&pair_root->children, vp); \
333
0
    request->pair_list._list = vp; \
334
0
  } while(0)
335
336
0
    if (!request->pair_list.request) list_init(request->pair_root, request);
337
0
    if (!request->pair_list.reply) list_init(request->pair_root, reply);
338
0
    if (!request->pair_list.control) list_init(request->pair_root, control);
339
0
    if (!request->pair_list.local) list_init(request->pair_root, local);
340
0
    if (!request->pair_list.state) {
341
0
      list_init(NULL, state);
342
0
#ifndef NDEBUG
343
0
      talloc_set_destructor(request->pair_list.state, _state_ctx_free);
344
0
#endif
345
0
    }
346
0
  }
347
348
  /*
349
   *  Initialise packets and additional
350
   *  fields if this is going to be a
351
   *  child request.
352
   */
353
0
  if (args && args->parent) {
354
0
    if (request_child_init(request, args->parent) < 0) return -1;
355
356
0
    if (args->detachable) {
357
0
      if (request_detachable_init(request, args->parent) < 0) return -1;
358
0
      request_log_init_detachable(request, args->parent);
359
0
    } else {
360
0
      request_log_init_child(request, args->parent);
361
0
    }
362
0
  } else {
363
0
    request_log_init_orphan(request);
364
0
  }
365
366
  /*
367
   *  This is only used by src/lib/io/worker.c
368
   */
369
0
  fr_dlist_entry_init(&request->listen_entry);
370
371
0
  return 0;
372
0
}
373
374
/** Callback for slabs to deinitialise the request
375
 *
376
 * Does not need to be called for local requests.
377
 *
378
 * @param[in] request   deinitialise
379
 * @return
380
 *  - 0 in the request was deinitialised.
381
 *  - -1 if the request is in an unexpected state.
382
 */
383
int request_slab_deinit(request_t *request)
384
0
{
385
0
  fr_assert_msg(!fr_timer_armed(request->timeout),
386
0
          "alloced %s:%i: %s still in the  timeout sublist",
387
0
          request->alloc_file,
388
0
          request->alloc_line,
389
0
          request->name ? request->name : "(null)");
390
0
  fr_assert_msg(!fr_heap_entry_inserted(request->runnable),
391
0
          "alloced %s:%i: %s still in the runnable heap ID %i",
392
0
          request->alloc_file,
393
0
          request->alloc_line,
394
0
          request->name ? request->name : "(null)", request->runnable);
395
396
0
  RDEBUG3("Request deinitialising (%p)", request);
397
398
  /*
399
   *  state_ctx is parented separately.
400
   */
401
0
  if (request->session_state_ctx) TALLOC_FREE(request->session_state_ctx);
402
403
  /*
404
   *  Zero out everything.
405
   */
406
0
  memset(request, 0, sizeof(*request));
407
408
0
#ifndef NDEBUG
409
0
  request->component = "free_list";
410
0
  request->runnable = FR_HEAP_INDEX_INVALID;
411
0
  request->magic = 0x01020304;  /* set the request to be nonsense */
412
0
#endif
413
414
0
  return 0;
415
0
}
416
417
static inline CC_HINT(always_inline) request_t *request_alloc_pool(TALLOC_CTX *ctx)
418
0
{
419
0
  request_t *request;
420
421
  /*
422
   *  Only allocate requests in the NULL
423
   *  ctx.  There's no scenario where it's
424
   *  appropriate to allocate them in a
425
   *  pool, and using a strict talloc
426
   *  hierarchy means that child requests
427
   *  cannot be returned to a free list
428
   *  and would have to be freed.
429
   */
430
0
  MEM(request = talloc_pooled_object(ctx, request_t,
431
0
             REQUEST_POOL_NUM_OBJECTS,
432
0
             REQUEST_POOL_SIZE));
433
0
  fr_assert(ctx != request);
434
435
0
  return request;
436
0
}
437
438
static int _request_local_free(request_t *request)
439
0
{
440
0
  RDEBUG4("Local request freed (%p)", request);
441
442
  /*
443
   *  Ensure anything that might reference the request is
444
   *  freed before it is.
445
   */
446
0
  talloc_free_children(request);
447
448
  /*
449
   *  state_ctx is parented separately.
450
   *
451
   *  The reason why it's OK to do this, is if the state attributes
452
   *  need to persist across requests, they will already have been
453
   *  moved to a fr_state_entry_t, with the state pointers in the
454
   *  request being set to NULL, before the request is freed/
455
   *
456
   *  Note also that we do NOT call TALLOC_FREE(), which
457
   *  sets state_ctx=NULL.  We don't control the order in
458
   *  which talloc frees the children.  And the parents
459
   *  state_ctx pointer needs to stick around so that all of
460
   *  the children can check it.
461
   *
462
   *  If this assertion hits, it means that someone didn't
463
   *  call fr_state_store_in_parent()
464
   */
465
0
  if (request->session_state_ctx) {
466
0
    fr_assert(!request->parent || (request->session_state_ctx != request->parent->session_state_ctx));
467
468
0
    talloc_free(request->session_state_ctx);
469
0
  }
470
471
0
#ifndef NDEBUG
472
0
  request->magic = 0x01020304;  /* set the request to be nonsense */
473
0
#endif
474
475
0
  return 0;
476
0
}
477
478
/** Allocate a request that's not in the free list
479
 *
480
 * This can be useful if modules need a persistent request for their own purposes
481
 * which needs to be outside of the normal free list, so that it can be freed
482
 * when the module requires, not when the thread destructor runs.
483
 */
484
request_t *_request_local_alloc(char const *file, int line, TALLOC_CTX *ctx,
485
        request_type_t type, request_init_args_t const *args)
486
0
{
487
0
  request_t *request;
488
489
0
  request = request_alloc_pool(ctx);
490
0
  if (_request_init(file, line, request, type, args) < 0) return NULL;
491
492
0
  talloc_set_destructor(request, _request_local_free);
493
494
0
  return request;
495
0
}
496
497
/** Replace the session_state_ctx with a new one.
498
 *
499
 *  NOTHING should rewrite request->session_state_ctx.
500
 *
501
 *  It's now a pair, and is stored in request->pair_root.
502
 *  So it's wrong for anyone other than this function to play games with it.
503
 *
504
 * @param[in] request to replace the state of.
505
 * @param[in] new_state state to assign to the request.
506
 *      May be NULL in which case a new_state state will
507
 *      be alloced and assigned.
508
 *
509
 * @return the fr_pair_t containing the old state list.
510
 */
511
fr_pair_t *request_state_replace(request_t *request, fr_pair_t *new_state)
512
{
513
  fr_pair_t *old = request->session_state_ctx;
514
515
  fr_assert(request->session_state_ctx != NULL);
516
  fr_assert(request->session_state_ctx != new_state);
517
518
  fr_pair_remove(&request->pair_root->children, old);
519
520
  /*
521
   *  Save (or delete) the existing state, and re-initialize
522
   *  it with a brand new one.
523
   */
524
  if (!new_state) MEM(new_state = fr_pair_afrom_da(NULL, request_attr_state));
525
526
  request->session_state_ctx = new_state;
527
528
  fr_pair_append(&request->pair_root->children, new_state);
529
530
  return old;
531
}
532
533
/** Unlink a subrequest from its parent
534
 *
535
 * @note This should be used for requests in preparation for freeing them.
536
 *
537
 * @param[in] child   request to unlink.
538
 * @return
539
 *   - 0 on success.
540
 *   - -1 on failure.
541
 */
542
int request_detach(request_t *child)
543
0
{
544
0
  request_t *request = child->parent;
545
546
  /*
547
   *  Already detached or not detachable
548
   */
549
0
  if (request_is_detached(child)) return 0;
550
551
0
  if (!request_is_detachable(child)) {
552
0
    fr_strerror_const("Request is not detachable");
553
0
    return -1;
554
0
  }
555
556
  /*
557
   *  Unlink the child from the parent.
558
   */
559
0
  request_data_get(request, child, 0);
560
561
0
  child->parent = NULL;
562
563
  /*
564
   *  Request is now detached
565
   */
566
0
  child->type = REQUEST_TYPE_DETACHED;
567
568
  /*
569
   *  ...and is no longer detachable.
570
   */
571
0
  child->flags.detachable = 0;
572
573
0
  return 0;
574
0
}
575
576
static int _request_global_free(UNUSED void *uctx)
577
0
{
578
0
  fr_dict_autofree(request_dict);
579
0
  return 0;
580
0
}
581
582
static int _request_global_init(UNUSED void *uctx)
583
0
{
584
0
  if (fr_dict_autoload(request_dict) < 0) {
585
0
    PERROR("%s", __FUNCTION__);
586
0
    return -1;
587
0
  }
588
0
  if (fr_dict_attr_autoload(request_dict_attr) < 0) {
589
0
    PERROR("%s", __FUNCTION__);
590
0
    fr_dict_autofree(request_dict);
591
0
    return -1;
592
0
  }
593
0
  return 0;
594
0
}
595
596
int request_global_init(void)
597
0
{
598
0
  int ret;
599
0
  fr_atexit_global_once_ret(&ret, _request_global_init, _request_global_free, NULL);
600
0
  return ret;
601
0
}
602
603
#ifdef WITH_VERIFY_PTR
604
/*
605
 *  Verify a packet.
606
 */
607
static void packet_verify(char const *file, int line,
608
        request_t const *request, fr_packet_t const *packet, fr_pair_list_t *list, char const *type)
609
0
{
610
0
  TALLOC_CTX *parent;
611
612
0
  fr_fatal_assert_msg(packet, "CONSISTENCY CHECK FAILED %s[%i]: fr_packet_t %s pointer was NULL",
613
0
          file, line, type);
614
615
0
  parent = talloc_parent(packet);
616
0
  if (parent != request) {
617
0
    fr_log_talloc_report(packet);
618
0
    if (parent) fr_log_talloc_report(parent);
619
620
621
0
    fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%i]: Expected fr_packet_t %s to be parented "
622
0
             "by %p (%s), but parented by %p (%s)",
623
0
             file, line, type, request, talloc_get_name(request),
624
0
             parent, parent ? talloc_get_name(parent) : "NULL");
625
0
  }
626
627
  /*
628
   *  Enforce nesting at the top level.  This catches minor programming bugs in the server core.
629
   *
630
   *  If we care more, we could do these checks recursively.  But the tmpl_tokenize code already
631
   *  enforces parent / child namespaces.  So the end user shouldn't be able to break the parenting.
632
   *
633
   *  This code really only checks for programming bugs where the C code creates a pair, and then
634
   *  adds it to the wrong list.  This was happening during the transition from flat to nested, as
635
   *  the code was in the middle of being fixed.  It should only happen now if the programmer
636
   *  forgets, and uses the wrong APIs.
637
   */
638
0
  fr_pair_list_foreach(list, vp) {
639
0
    if (vp->da->flags.is_raw) continue;
640
641
0
    if (vp->da->flags.internal) continue;
642
643
0
    if (vp->da->depth > 1) {
644
0
      fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%i]: Expected fr_pair_t %s to be parented "
645
0
             "by (%s), but it is instead at the top-level %s list",
646
0
               file, line, vp->da->name, vp->da->parent->name, type);
647
0
    }
648
0
  }
649
650
0
  PACKET_VERIFY(packet);
651
0
}
652
653
/*
654
 *  Catch horrible talloc errors.
655
 */
656
void request_verify(char const *file, int line, request_t const *request)
657
0
{
658
0
  request_data_t *rd = NULL;
659
660
0
  fr_fatal_assert_msg(request, "CONSISTENCY CHECK FAILED %s[%i]: request_t pointer was NULL", file, line);
661
662
0
  (void) talloc_get_type_abort_const(request, request_t);
663
664
0
  fr_assert(request->magic == REQUEST_MAGIC);
665
666
0
  (void)talloc_get_type_abort(request->request_ctx, fr_pair_t);
667
0
  fr_pair_list_verify(file, line, request->request_ctx, &request->request_pairs, true);
668
0
  (void)talloc_get_type_abort(request->reply_ctx, fr_pair_t);
669
0
  fr_pair_list_verify(file, line, request->reply_ctx, &request->reply_pairs, true);
670
0
  (void)talloc_get_type_abort(request->control_ctx, fr_pair_t);
671
0
  fr_pair_list_verify(file, line, request->control_ctx, &request->control_pairs, true);
672
0
  (void)talloc_get_type_abort(request->session_state_ctx, fr_pair_t);
673
674
0
#ifndef NDEBUG
675
0
  {
676
0
    TALLOC_CTX *parent = talloc_parent(request->session_state_ctx);
677
678
0
    fr_assert_msg((parent == NULL) || (parent == talloc_null_ctx()),
679
0
            "session_state_ctx must not be parented by another chunk, but is parented by %s",
680
0
            talloc_get_name(talloc_parent(request->session_state_ctx)));
681
0
  }
682
0
#endif
683
684
0
  fr_pair_list_verify(file, line, request->session_state_ctx, &request->session_state_pairs, true);
685
0
  fr_pair_list_verify(file, line, request->local_ctx, &request->local_pairs, true);
686
687
0
  fr_assert(request->proto_dict != NULL);
688
0
  fr_assert(request->local_dict != NULL);
689
690
0
  if (request->packet) {
691
0
    packet_verify(file, line, request, request->packet, &request->request_pairs, "request");
692
0
  }
693
0
  if (request->reply) {
694
0
    packet_verify(file, line, request, request->reply, &request->reply_pairs, "reply");
695
0
  }
696
697
0
  if (request->async) {
698
0
    (void) talloc_get_type_abort(request->async, fr_async_t);
699
0
    fr_assert(talloc_parent(request->async) == request);
700
0
  }
701
702
0
  while ((rd = fr_dlist_next(&request->data, rd))) {
703
0
    (void) talloc_get_type_abort(rd, request_data_t);
704
705
0
    if (request_data_persistable(rd)) {
706
0
      fr_assert(request->session_state_ctx);
707
0
      fr_assert(talloc_parent(rd) == request->session_state_ctx);
708
0
    } else {
709
      fr_assert(talloc_parent(rd) == request);
710
0
    }
711
0
  }
712
0
}
713
#endif