Coverage Report

Created: 2026-08-08 07:10

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