Coverage Report

Created: 2026-09-28 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/unlang/caller.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: 9ef32bc60d0bd2bb89611ca29ce73ed35271d872 $
19
 *
20
 * @file unlang/caller.c
21
 * @brief Unlang "caller" keyword evaluation.  Used for setting allowed parent protocols
22
 *
23
 * @copyright 2020 The FreeRADIUS server project
24
 * @copyright 2021 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
25
 */
26
RCSID("$Id: 9ef32bc60d0bd2bb89611ca29ce73ed35271d872 $")
27
28
#include <freeradius-devel/server/state.h>
29
30
#include "caller_priv.h"
31
#include "group_priv.h"
32
33
static unlang_action_t unlang_caller(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
34
0
{
35
0
  unlang_group_t      *g = unlang_generic_to_group(frame->instruction);
36
0
  unlang_caller_t     *gext = unlang_group_to_caller(g);
37
38
0
  fr_assert(!unlang_list_empty(&g->children)); /* otherwise the compilation is broken */
39
40
  /*
41
   *  No parent, or the dictionaries don't match.  Ignore it.
42
   */
43
0
  if (!request->parent || (request->parent->proto_dict != gext->dict)) {
44
0
    RDEBUG2("... parent does not match 'caller %s', ignoring contents ...", fr_dict_root(gext->dict)->name);
45
0
    return UNLANG_ACTION_EXECUTE_NEXT;
46
0
  }
47
48
  /*
49
   *  The dictionary matches.  Go recurse into its children.
50
   */
51
0
  return unlang_group(p_result, request, frame);
52
0
}
53
54
55
static unlang_t *unlang_compile_caller(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci)
56
0
{
57
0
  CONF_SECTION      *cs = cf_item_to_section(ci);
58
0
  unlang_t      *c;
59
60
0
  unlang_group_t      *g;
61
0
  unlang_caller_t     *gext;
62
63
0
  fr_token_t      type;
64
0
  char const          *name;
65
0
  fr_dict_t const     *dict;
66
0
  unlang_compile_ctx_t    unlang_ctx2;
67
0
  tmpl_rules_t      parent_rules, t_rules;
68
69
0
  fr_dict_autoload_talloc_t *dict_ref = NULL;
70
71
0
  name = cf_section_name2(cs);
72
0
  if (!name) {
73
0
    cf_log_err(cs, "You MUST specify a protocol name for 'caller <protocol> { ... }'");
74
0
  print_url:
75
0
    cf_log_err(ci, DOC_KEYWORD_REF(caller));
76
0
    return NULL;
77
0
  }
78
79
0
  type = cf_section_name2_quote(cs);
80
0
  if (type != T_BARE_WORD) {
81
0
    cf_log_err(cs, "The argument to 'caller' cannot be a quoted string or a dynamic value");
82
0
    goto print_url;
83
0
  }
84
85
0
  dict = fr_dict_by_protocol_name(name);
86
0
  if (!dict) {
87
0
    dict_ref = fr_dict_autoload_talloc(NULL, &dict, name);
88
0
    if (!dict_ref) {
89
0
      cf_log_perr(cs, "Unknown protocol '%s'", name);
90
0
      goto print_url;
91
0
    }
92
0
  }
93
94
  /*
95
   *  Create a new parent context with the new dictionary.
96
   */
97
0
  memcpy(&parent_rules, unlang_ctx->rules, sizeof(parent_rules));
98
0
  memcpy(&t_rules, unlang_ctx->rules, sizeof(t_rules));
99
0
  parent_rules.attr.dict_def = dict;
100
0
  t_rules.parent = &parent_rules;
101
102
  /*
103
   *  We don't want to modify the context we were passed, so
104
   *  we just clone it
105
   */
106
0
  memcpy(&unlang_ctx2, unlang_ctx, sizeof(unlang_ctx2));
107
0
  unlang_ctx2.rules = &t_rules;
108
0
  unlang_ctx2.section_name1 = "caller";
109
0
  unlang_ctx2.section_name2 = name;
110
111
0
  c = unlang_compile_section(parent, &unlang_ctx2, cs, UNLANG_TYPE_CALLER);
112
0
  if (!c) {
113
0
    talloc_free(dict_ref);
114
0
    return NULL;
115
0
  }
116
117
  /*
118
   *  Set the virtual server name, which tells unlang_call()
119
   *  which virtual server to call.
120
   */
121
0
  g = unlang_generic_to_group(c);
122
0
  gext = unlang_group_to_caller(g);
123
0
  gext->dict = dict;
124
125
0
  if (dict_ref) {
126
    /*
127
     *  Parent the dictionary reference correctly now that we
128
     *  have the section with the dependency.  This should
129
     *  be fast as dict_ref has no siblings.
130
     */
131
0
    talloc_steal(gext, dict_ref);
132
0
  }
133
134
0
  if (unlang_list_empty(&g->children)) {
135
0
    talloc_free(c);
136
0
    return UNLANG_IGNORE;
137
0
  }
138
139
0
  return c;
140
0
}
141
142
void unlang_caller_init(void)
143
4
{
144
4
  unlang_register(&(unlang_op_t){
145
4
      .name = "caller",
146
4
      .type = UNLANG_TYPE_CALLER,
147
4
      .flag = UNLANG_OP_FLAG_DEBUG_BRACES,
148
149
4
      .compile = unlang_compile_caller,
150
4
      .interpret = unlang_caller,
151
152
4
      .unlang_size = sizeof(unlang_caller_t),
153
4
      .unlang_name = "unlang_caller_t",
154
4
    });
155
4
}