2 * evaluate.c Evaluate complex conditions
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2007 The FreeRADIUS server project
21 * Copyright 2007 Alan DeKok <aland@deployingradius.com>
24 #include <freeradius-devel/ident.h>
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29 #include <freeradius-devel/rad_assert.h>
37 * For POSIX Regular expressions.
38 * (0) Means no extended regular expressions.
39 * REG_EXTENDED means use extended regular expressions.
42 #define REG_EXTENDED (0)
52 static int all_digits(const char *string)
54 const char *p = string;
58 while (isdigit((int) *p)) p++;
63 static const char *filler = "????????????????????????????????????????????????????????????????";
65 static const char *expand_string(char *buffer, size_t sizeof_buffer,
67 FR_TOKEN value_type, const char *value)
75 case T_SINGLE_QUOTED_STRING:
78 case T_BACK_QUOTED_STRING:
79 result = radius_exec_program(value, request, 1,
80 buffer, sizeof_buffer, NULL,
87 * The result should be ASCII.
89 for (p = buffer; *p != '\0'; p++) {
97 case T_DOUBLE_QUOTED_STRING:
98 if (!strchr(value, '%')) return value;
100 radius_xlat(buffer, sizeof_buffer, value, request, NULL);
108 static FR_TOKEN getregex(const char **ptr, char *buffer, size_t buflen,
111 const char *p = *ptr;
114 if (*p != '/') return T_OP_INVALID;
116 *pcflags = REG_EXTENDED;
120 if (buflen <= 1) break;
126 * Check for case insensitivity
130 *pcflags |= REG_ICASE;
160 * FIXME: add 'x' and 'u'
164 if ((p[1] >= '0') && (p[1] <= '9') &&
165 (sscanf(p, "%3o", &x) == 1)) {
184 return T_DOUBLE_QUOTED_STRING;
188 static const FR_NAME_NUMBER modreturn_table[] = {
189 { "reject", RLM_MODULE_REJECT },
190 { "fail", RLM_MODULE_FAIL },
191 { "ok", RLM_MODULE_OK },
192 { "handled", RLM_MODULE_HANDLED },
193 { "invalid", RLM_MODULE_INVALID },
194 { "userlock", RLM_MODULE_USERLOCK },
195 { "notfound", RLM_MODULE_NOTFOUND },
196 { "noop", RLM_MODULE_NOOP },
197 { "updated", RLM_MODULE_UPDATED },
202 int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
204 const char *vp_name = name;
205 REQUEST *myrequest = request;
207 VALUE_PAIR *vps = NULL;
212 * Allow for tunneled sessions.
214 if (strncmp(vp_name, "outer.", 6) == 0) {
215 if (!myrequest->parent) return TRUE;
217 myrequest = myrequest->parent;
220 if (strncmp(vp_name, "request:", 8) == 0) {
222 vps = myrequest->packet->vps;
224 } else if (strncmp(vp_name, "reply:", 6) == 0) {
226 vps = myrequest->reply->vps;
229 } else if (strncmp(vp_name, "proxy-request:", 14) == 0) {
231 if (request->proxy) vps = myrequest->proxy->vps;
233 } else if (strncmp(vp_name, "proxy-reply:", 12) == 0) {
235 if (request->proxy_reply) vps = myrequest->proxy_reply->vps;
238 } else if (strncmp(vp_name, "config:", 7) == 0) {
240 vps = myrequest->config_items;
242 } else if (strncmp(vp_name, "control:", 8) == 0) {
244 vps = myrequest->config_items;
247 } else if (strncmp(vp_name, "coa:", 4) == 0) {
250 if (myrequest->coa &&
251 (myrequest->coa->proxy->code == PW_COA_REQUEST)) {
252 vps = myrequest->coa->proxy->vps;
255 } else if (strncmp(vp_name, "coa-reply:", 10) == 0) {
258 if (myrequest->coa && /* match reply with request */
259 (myrequest->coa->proxy->code == PW_COA_REQUEST) &&
260 (myrequest->coa->proxy_reply)) {
261 vps = myrequest->coa->proxy_reply->vps;
264 } else if (strncmp(vp_name, "disconnect:", 11) == 0) {
267 if (myrequest->coa &&
268 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
269 vps = myrequest->coa->proxy->vps;
272 } else if (strncmp(vp_name, "disconnect-reply:", 17) == 0) {
275 if (myrequest->coa && /* match reply with request */
276 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
277 (myrequest->coa->proxy_reply)) {
278 vps = myrequest->coa->proxy_reply->vps;
283 vps = myrequest->packet->vps;
286 da = dict_attrbyname(vp_name);
287 if (!da) return FALSE; /* not a dictionary name */
290 * May not may not be found, but it *is* a known name.
292 *vp_p = pairfind(vps, da->attr);
298 * *presult is "did comparison match or not"
300 static int radius_do_cmp(REQUEST *request, int *presult,
301 FR_TOKEN lt, const char *pleft, FR_TOKEN token,
302 FR_TOKEN rt, const char *pright,
303 int cflags, int modreturn)
307 VALUE_PAIR *vp = NULL;
311 cflags = cflags; /* -Wunused */
314 rt = rt; /* -Wunused */
316 if (lt == T_BARE_WORD) {
318 * Maybe check the last return code.
320 if (token == T_OP_CMP_TRUE) {
324 * Looks like a return code, treat is as such.
326 isreturn = fr_str2int(modreturn_table, pleft, -1);
327 if (isreturn != -1) {
328 *presult = (modreturn == isreturn);
334 * Bare words on the left can be attribute names.
336 if (radius_get_vp(request, pleft, &vp)) {
340 * VP exists, and that's all we're looking for.
342 if (token == T_OP_CMP_TRUE) {
343 *presult = (vp != NULL);
351 * The attribute on the LHS may
352 * have been a dynamically
353 * registered callback. i.e. it
354 * doesn't exist as a VALUE_PAIR.
355 * If so, try looking for it.
357 da = dict_attrbyname(pleft);
358 if (da && radius_find_compare(da->attr)) {
359 VALUE_PAIR *check = pairmake(pleft, pright, token);
360 *presult = (radius_callback_compare(request, NULL, check, NULL, NULL) == 0);
361 RDEBUG3(" Callback returns %d",
367 RDEBUG2(" (Attribute %s was not found)",
374 * Regex comparisons treat everything as
377 if ((token == T_OP_REG_EQ) ||
378 (token == T_OP_REG_NE)) {
379 vp_prints_value(buffer, sizeof(buffer), vp, 0);
385 memcpy(&myvp, vp, sizeof(myvp));
386 if (!pairparsevalue(&myvp, pright)) {
387 RDEBUG2("Failed parsing \"%s\": %s",
388 pright, fr_strerror());
392 myvp.operator = token;
393 *presult = paircmp(&myvp, vp);
394 RDEBUG3(" paircmp -> %d", *presult);
396 } /* else it's not a VP in a list */
407 if (!all_digits(pright)) {
408 RDEBUG2(" (Right field is not a number at: %s)", pright);
412 if (!all_digits(pleft)) {
413 RDEBUG2(" (Left field is not a number at: %s)", pleft);
420 lint = rint = 0; /* quiet the compiler */
427 * Check for truth or falsehood.
429 if (all_digits(pleft)) {
431 result = (lint != 0);
434 result = (*pleft != '\0');
440 result = (strcmp(pleft, pright) == 0);
444 result = (strcmp(pleft, pright) != 0);
448 result = (lint >= rint);
452 result = (lint > rint);
456 result = (lint <= rint);
460 result = (lint < rint);
467 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
470 * Include substring matches.
472 compare = regcomp(®, pright, cflags);
477 regerror(compare, ®, errbuf, sizeof(errbuf));
478 DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
483 compare = regexec(®, pleft,
484 REQUEST_MAX_REGEX + 1,
489 * Add new %{0}, %{1}, etc.
491 if (compare == 0) for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
494 free(request_data_get(request, request,
495 REQUEST_DATA_REGEX | i));
499 * We MAY have %{2} without %{1}.
501 if (rxmatch[i].rm_so == -1) continue;
504 * Copy substring into allocated buffer
506 r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1);
507 memcpy(r, pleft + rxmatch[i].rm_so,
508 rxmatch[i].rm_eo - rxmatch[i].rm_so);
509 r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
511 request_data_add(request, request,
512 REQUEST_DATA_REGEX | i,
515 result = (compare == 0);
522 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
525 * Include substring matches.
527 compare = regcomp(®, pright, cflags);
532 regerror(compare, ®, errbuf, sizeof(errbuf));
533 DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
538 compare = regexec(®, pleft,
539 REQUEST_MAX_REGEX + 1,
543 result = (compare != 0);
549 RDEBUG4(">>> NOT IMPLEMENTED %d", token);
559 int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
560 const char **ptr, int evaluate_it, int *presult)
562 int found_condition = FALSE;
565 int evaluate_next_condition = evaluate_it;
567 const char *q, *start;
568 FR_TOKEN token, lt, rt;
569 char left[1024], right[1024], comp[4];
570 const char *pleft, *pright;
571 char xleft[1024], xright[1024];
574 if (!ptr || !*ptr || (depth >= 64)) {
575 radlog(L_ERR, "Internal sanity check failed in evaluate condition");
584 while ((*p == ' ') || (*p == '\t')) p++;
589 if (!found_condition && (*p == '!')) {
591 * Don't change the results if we're not
592 * evaluating the condition.
594 if (evaluate_next_condition) {
595 RDEBUG4(">>> INVERT");
600 while ((*p == ' ') || (*p == '\t')) p++;
606 if (!found_condition && (*p == '(')) {
607 const char *end = p + 1;
610 * Evaluate the condition, bailing out on
613 RDEBUG4(">>> RECURSING WITH ... %s", end);
614 if (!radius_evaluate_condition(request, modreturn,
616 evaluate_next_condition,
622 if (evaluate_next_condition) {
623 RDEBUG2("%.*s Converting !%s -> %s",
625 (result != FALSE) ? "TRUE" : "FALSE",
626 (result == FALSE) ? "TRUE" : "FALSE");
627 result = (result == FALSE);
633 * Start from the end of the previous
637 RDEBUG4(">>> AFTER RECURSION ... %s", end);
639 while ((*p == ' ') || (*p == '\t')) p++;
642 radlog(L_ERR, "No closing brace");
646 if (*p == ')') p++; /* eat closing brace */
647 found_condition = TRUE;
649 while ((*p == ' ') || (*p == '\t')) p++;
653 * At EOL or closing brace, update && return.
655 if (found_condition && (!*p || (*p == ')'))) break;
665 if (found_condition) {
667 * (A && B) means "evaluate B
668 * only if A was true"
670 if ((p[0] == '&') && (p[1] == '&')) {
671 if (!result) evaluate_next_condition = FALSE;
673 found_condition = FALSE;
674 continue; /* go back to the start */
678 * (A || B) means "evaluate B
679 * only if A was false"
681 if ((p[0] == '|') && (p[1] == '|')) {
682 if (result) evaluate_next_condition = FALSE;
684 found_condition = FALSE;
696 if (found_condition) {
697 radlog(L_ERR, "Consecutive conditions at %s", p);
701 RDEBUG4(">>> LOOKING AT %s", p);
705 * Look for common errors.
707 if ((p[0] == '%') && (p[1] == '{')) {
708 radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
713 * Look for WORD1 op WORD2
715 lt = gettoken(&p, left, sizeof(left));
716 if ((lt != T_BARE_WORD) &&
717 (lt != T_DOUBLE_QUOTED_STRING) &&
718 (lt != T_SINGLE_QUOTED_STRING) &&
719 (lt != T_BACK_QUOTED_STRING)) {
720 radlog(L_ERR, "Expected string or numbers at: %s", p);
725 if (evaluate_next_condition) {
726 pleft = expand_string(xleft, sizeof(xleft), request,
729 radlog(L_ERR, "Failed expanding string at: %s",
736 * Peek ahead, to see if it's:
740 * or something else, such as
748 while ((*q == ' ') || (*q == '\t')) q++;
751 * If the next thing is:
758 * Then WORD is just a test for existence.
759 * Remember that and skip ahead.
761 if (!*q || (*q == ')') ||
762 ((q[0] == '&') && (q[1] == '&')) ||
763 ((q[0] == '|') && (q[1] == '|'))) {
764 token = T_OP_CMP_TRUE;
775 token = gettoken(&p, comp, sizeof(comp));
776 if ((token < T_OP_NE) || (token > T_OP_CMP_EQ) ||
777 (token == T_OP_CMP_TRUE)) {
778 radlog(L_ERR, "Expected comparison at: %s", comp);
783 * Look for common errors.
785 if ((p[0] == '%') && (p[1] == '{')) {
786 radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
794 if ((token == T_OP_REG_EQ) ||
795 (token == T_OP_REG_NE)) {
796 rt = getregex(&p, right, sizeof(right), &cflags);
797 if (rt != T_DOUBLE_QUOTED_STRING) {
798 radlog(L_ERR, "Expected regular expression at: %s", p);
803 rt = gettoken(&p, right, sizeof(right));
805 if ((rt != T_BARE_WORD) &&
806 (rt != T_DOUBLE_QUOTED_STRING) &&
807 (rt != T_SINGLE_QUOTED_STRING) &&
808 (rt != T_BACK_QUOTED_STRING)) {
809 radlog(L_ERR, "Expected string or numbers at: %s", p);
814 if (evaluate_next_condition) {
815 pright = expand_string(xright, sizeof(xright), request,
818 radlog(L_ERR, "Failed expanding string at: %s",
824 RDEBUG4(">>> %d:%s %d %d:%s",
825 lt, pleft, token, rt, pright);
828 if (evaluate_next_condition) {
832 if (!radius_do_cmp(request, &result, lt, pleft, token,
833 rt, pright, cflags, modreturn)) {
836 RDEBUG4(">>> Comparison returned %d", result);
839 RDEBUG4(">>> INVERTING result");
840 result = (result == FALSE);
843 RDEBUG2("%.*s Evaluating %s(%.*s) -> %s",
845 invert ? "!" : "", p - start, start,
846 (result != FALSE) ? "TRUE" : "FALSE");
849 RDEBUG4(">>> GOT result %d", result);
852 * Not evaluating it. We may be just
855 } else if (request) {
856 RDEBUG2("%.*s Skipping %s(%.*s)",
858 invert ? "!" : "", p - start, start);
861 found_condition = TRUE;
862 } /* loop over the input condition */
864 if (!found_condition) {
865 radlog(L_ERR, "Syntax error. Expected condition at %s", p);
869 RDEBUG4(">>> AT EOL -> %d", result);
871 if (evaluate_it) *presult = result;
876 static void fix_up(REQUEST *request)
880 request->username = NULL;
881 request->password = NULL;
883 for (vp = request->packet->vps; vp != NULL; vp = vp->next) {
884 if ((vp->attribute == PW_USER_NAME) &&
885 !request->username) {
886 request->username = vp;
888 } else if (vp->attribute == PW_STRIPPED_USER_NAME) {
889 request->username = vp;
891 } else if (vp->attribute == PW_USER_PASSWORD) {
892 request->password = vp;
899 * The pairmove() function in src/lib/valuepair.c does all sorts of
900 * extra magic that we don't want here.
902 * FIXME: integrate this with the code calling it, so that we
903 * only paircopy() those attributes that we're really going to
906 void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
908 int i, j, count, from_count, to_count, tailto;
909 VALUE_PAIR *vp, *next, **last;
910 VALUE_PAIR **from_list, **to_list;
914 * Set up arrays for editing, to remove some of the
915 * O(N^2) dependencies. This also makes it easier to
916 * insert and remove attributes.
918 * It also means that the operators apply ONLY to the
919 * attributes in the original list. With the previous
920 * implementation of pairmove(), adding two attributes
921 * via "+=" and then "=" would mean that the second one
922 * wasn't added, because of the existence of the first
923 * one in the "to" list. This implementation doesn't
926 * Also, the previous implementation did NOT implement
927 * "-=" correctly. If two of the same attributes existed
928 * in the "to" list, and you tried to subtract something
929 * matching the *second* value, then the pairdelete()
930 * function was called, and the *all* attributes of that
931 * number were deleted. With this implementation, only
932 * the matching attributes are deleted.
935 for (vp = from; vp != NULL; vp = vp->next) count++;
936 from_list = rad_malloc(sizeof(*from_list) * count);
938 for (vp = *to; vp != NULL; vp = vp->next) count++;
939 to_list = rad_malloc(sizeof(*to_list) * count);
942 * Move the lists to the arrays, and break the list
946 for (vp = from; vp != NULL; vp = next) {
948 from_list[from_count++] = vp;
953 for (vp = *to; vp != NULL; vp = next) {
955 to_list[to_count++] = vp;
959 edited = rad_malloc(sizeof(*edited) * to_count);
960 memset(edited, 0, sizeof(*edited) * to_count);
962 RDEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count);
965 * Now that we have the lists initialized, start working
968 for (i = 0; i < from_count; i++) {
971 RDEBUG4("::: Examining %s", from_list[i]->name);
974 * Attribute should be appended, OR the "to" list
975 * is empty, and we're supposed to replace or
976 * "add if not existing".
978 if (from_list[i]->operator == T_OP_ADD) goto append;
981 for (j = 0; j < to_count; j++) {
982 if (edited[j] || !to_list[j] || !from_list[i]) continue;
985 * Attributes aren't the same, skip them.
987 if (from_list[i]->attribute != to_list[j]->attribute) {
992 * We don't use a "switch" statement here
993 * because we want to break out of the
994 * "for" loop over 'j' in most cases.
998 * Over-write the FIRST instance of the
999 * matching attribute name. We free the
1000 * one in the "to" list, and move over
1001 * the one in the "from" list.
1003 if (from_list[i]->operator == T_OP_SET) {
1004 RDEBUG4("::: OVERWRITING %s FROM %d TO %d",
1005 to_list[j]->name, i, j);
1006 pairfree(&to_list[j]);
1007 to_list[j] = from_list[i];
1008 from_list[i] = NULL;
1014 * Add the attribute only if it does not
1015 * exist... but it exists, so we stop
1018 if (from_list[i]->operator == T_OP_EQ) {
1024 * Delete every attribute, independent
1027 if (from_list[i]->operator == T_OP_CMP_FALSE) {
1032 * Delete all matching attributes from
1035 if ((from_list[i]->operator == T_OP_SUB) ||
1036 (from_list[i]->operator == T_OP_CMP_EQ) ||
1037 (from_list[i]->operator == T_OP_LE) ||
1038 (from_list[i]->operator == T_OP_GE)) {
1040 int old_op = from_list[i]->operator;
1043 * Check for equality.
1045 from_list[i]->operator = T_OP_CMP_EQ;
1048 * If equal, delete the one in
1051 rcode = radius_compare_vps(NULL, from_list[i],
1054 * We may want to do more
1055 * subtractions, so we re-set the
1056 * operator back to it's original
1059 from_list[i]->operator = old_op;
1063 if (rcode != 0) goto delete;
1069 RDEBUG4("::: DELETING %s FROM %d TO %d",
1070 from_list[i]->name, i, j);
1071 pairfree(&to_list[j]);
1077 * Enforce <=. If it's
1082 RDEBUG4("::: REPLACING %s FROM %d TO %d",
1083 from_list[i]->name, i, j);
1084 pairfree(&to_list[j]);
1085 to_list[j] = from_list[i];
1086 from_list[i] = NULL;
1093 RDEBUG4("::: REPLACING %s FROM %d TO %d",
1094 from_list[i]->name, i, j);
1095 pairfree(&to_list[j]);
1096 to_list[j] = from_list[i];
1097 from_list[i] = NULL;
1106 rad_assert(0 == 1); /* panic! */
1110 * We were asked to add it if it didn't exist,
1111 * and it doesn't exist. Move it over to the
1112 * tail of the "to" list, UNLESS it was already
1113 * moved by another operator.
1115 if (!found && from_list[i]) {
1116 if ((from_list[i]->operator == T_OP_EQ) ||
1117 (from_list[i]->operator == T_OP_LE) ||
1118 (from_list[i]->operator == T_OP_GE) ||
1119 (from_list[i]->operator == T_OP_SET)) {
1121 RDEBUG4("::: APPENDING %s FROM %d TO %d",
1122 from_list[i]->name, i, tailto);
1123 to_list[tailto++] = from_list[i];
1124 from_list[i] = NULL;
1130 * Delete attributes in the "from" list.
1132 for (i = 0; i < from_count; i++) {
1133 if (!from_list[i]) continue;
1135 pairfree(&from_list[i]);
1139 RDEBUG4("::: TO in %d out %d", to_count, tailto);
1142 * Re-chain the "to" list.
1147 for (i = 0; i < tailto; i++) {
1148 if (!to_list[i]) continue;
1150 RDEBUG4("::: to[%d] = %s", i, to_list[i]->name);
1153 * Mash the operator to a simple '='. The
1154 * operators in the "to" list aren't used for
1155 * anything. BUT they're used in the "detail"
1156 * file and debug output, where we don't want to
1157 * see the operators.
1159 to_list[i]->operator = T_OP_EQ;
1162 last = &(*last)->next;
1165 rad_assert(request != NULL);
1166 rad_assert(request->packet != NULL);
1169 * Fix dumb cache issues
1171 if (to == &request->packet->vps) {
1173 } else if (request->parent && (to == &request->parent->packet->vps)) {
1174 fix_up(request->parent);
1184 * Copied shamelessly from conffile.c, to simplify the API for
1187 typedef enum conf_type {
1188 CONF_ITEM_INVALID = 0,
1195 struct conf_item *next;
1196 struct conf_part *parent;
1198 const char *filename;
1199 CONF_ITEM_TYPE type;
1206 FR_TOKEN value_type;
1210 * Add attributes to a list.
1212 int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
1213 VALUE_PAIR *input_vps, const char *name)
1216 VALUE_PAIR *newlist, *vp;
1217 VALUE_PAIR **output_vps = NULL;
1218 REQUEST *myrequest = request;
1220 if (!request || !cs) return RLM_MODULE_INVALID;
1223 * If we are an inner tunnel session, permit the
1224 * policy to update the outer lists directly.
1226 if (strncmp(name, "outer.", 6) == 0) {
1227 if (!request->parent) return RLM_MODULE_NOOP;
1229 myrequest = request->parent;
1233 if (strcmp(name, "request") == 0) {
1234 output_vps = &myrequest->packet->vps;
1236 } else if (strcmp(name, "reply") == 0) {
1237 output_vps = &myrequest->reply->vps;
1240 } else if (strcmp(name, "proxy-request") == 0) {
1241 if (request->proxy) output_vps = &myrequest->proxy->vps;
1243 } else if (strcmp(name, "proxy-reply") == 0) {
1244 if (request->proxy_reply) output_vps = &request->proxy_reply->vps;
1247 } else if (strcmp(name, "config") == 0) {
1248 output_vps = &myrequest->config_items;
1250 } else if (strcmp(name, "control") == 0) {
1251 output_vps = &myrequest->config_items;
1254 } else if (strcmp(name, "coa") == 0) {
1255 if (!myrequest->coa) {
1256 request_alloc_coa(myrequest);
1257 myrequest->coa->proxy->code = PW_COA_REQUEST;
1260 if (myrequest->coa &&
1261 (myrequest->coa->proxy->code == PW_COA_REQUEST)) {
1262 output_vps = &myrequest->coa->proxy->vps;
1265 } else if (strcmp(name, "coa-reply") == 0) {
1266 if (myrequest->coa && /* match reply with request */
1267 (myrequest->coa->proxy->code == PW_COA_REQUEST) &&
1268 (myrequest->coa->proxy_reply)) {
1269 output_vps = &myrequest->coa->proxy_reply->vps;
1272 } else if (strcmp(name, "disconnect") == 0) {
1273 if (!myrequest->coa) {
1274 request_alloc_coa(myrequest);
1275 if (myrequest->coa) myrequest->coa->proxy->code = PW_DISCONNECT_REQUEST;
1278 if (myrequest->coa &&
1279 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
1280 output_vps = &myrequest->coa->proxy->vps;
1283 } else if (strcmp(name, "disconnect-reply") == 0) {
1284 if (myrequest->coa && /* match reply with request */
1285 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
1286 (myrequest->coa->proxy_reply)) {
1287 output_vps = &myrequest->coa->proxy_reply->vps;
1292 return RLM_MODULE_INVALID;
1295 if (!output_vps) return RLM_MODULE_NOOP; /* didn't update the list */
1297 newlist = paircopy(input_vps);
1299 RDEBUG2("Out of memory");
1300 return RLM_MODULE_FAIL;
1304 for (ci=cf_item_find_next(cs, NULL);
1306 ci=cf_item_find_next(cs, ci)) {
1310 * This should never happen.
1312 if (cf_item_is_section(ci)) {
1314 return RLM_MODULE_INVALID;
1317 cp = cf_itemtopair(ci);
1320 if (debug_flag && radius_find_compare(vp->attribute)) {
1321 DEBUG("WARNING: You are modifying the value of virtual attribute %s. This is not supported.", vp->name);
1326 * The VP && CF lists should be in sync. If they're
1329 if (vp->flags.do_xlat) {
1333 value = expand_string(buffer, sizeof(buffer), request,
1334 cp->value_type, cp->value);
1337 return RLM_MODULE_INVALID;
1340 if (!pairparsevalue(vp, value)) {
1341 RDEBUG2("ERROR: Failed parsing value \"%s\" for attribute %s: %s",
1342 value, vp->name, fr_strerror());
1344 return RLM_MODULE_FAIL;
1346 vp->flags.do_xlat = 0;
1351 radius_pairmove(request, output_vps, newlist);
1353 return RLM_MODULE_UPDATED;