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 static int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
205 const char *vp_name = name;
206 REQUEST *myrequest = request;
208 VALUE_PAIR *vps = NULL;
213 * Allow for tunneled sessions.
215 if (strncmp(vp_name, "outer.", 6) == 0) {
216 if (!myrequest->parent) return TRUE;
218 myrequest = myrequest->parent;
221 if (strncmp(vp_name, "request:", 8) == 0) {
223 vps = myrequest->packet->vps;
225 } else if (strncmp(vp_name, "reply:", 6) == 0) {
227 vps = myrequest->reply->vps;
230 } else if (strncmp(vp_name, "proxy-request:", 14) == 0) {
232 if (request->proxy) vps = myrequest->proxy->vps;
234 } else if (strncmp(vp_name, "proxy-reply:", 12) == 0) {
236 if (request->proxy_reply) vps = myrequest->proxy_reply->vps;
239 } else if (strncmp(vp_name, "config:", 7) == 0) {
241 vps = myrequest->config_items;
243 } else if (strncmp(vp_name, "control:", 8) == 0) {
245 vps = myrequest->config_items;
248 } else if (strncmp(vp_name, "coa:", 4) == 0) {
251 if (myrequest->coa &&
252 (myrequest->coa->proxy->code == PW_COA_REQUEST)) {
253 vps = myrequest->coa->proxy->vps;
256 } else if (strncmp(vp_name, "coa-reply:", 10) == 0) {
259 if (myrequest->coa && /* match reply with request */
260 (myrequest->coa->proxy->code == PW_COA_REQUEST) &&
261 (myrequest->coa->proxy_reply)) {
262 vps = myrequest->coa->proxy_reply->vps;
265 } else if (strncmp(vp_name, "disconnect:", 11) == 0) {
268 if (myrequest->coa &&
269 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
270 vps = myrequest->coa->proxy->vps;
273 } else if (strncmp(vp_name, "disconnect-reply:", 17) == 0) {
276 if (myrequest->coa && /* match reply with request */
277 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
278 (myrequest->coa->proxy_reply)) {
279 vps = myrequest->coa->proxy_reply->vps;
284 vps = myrequest->packet->vps;
287 da = dict_attrbyname(vp_name);
288 if (!da) return FALSE; /* not a dictionary name */
291 * May not may not be found, but it *is* a known name.
293 *vp_p = pairfind(vps, da->attr);
299 * *presult is "did comparison match or not"
301 static int radius_do_cmp(REQUEST *request, int *presult,
302 FR_TOKEN lt, const char *pleft, FR_TOKEN token,
303 FR_TOKEN rt, const char *pright,
304 int cflags, int modreturn)
308 VALUE_PAIR *vp = NULL;
312 cflags = cflags; /* -Wunused */
315 rt = rt; /* -Wunused */
317 if (lt == T_BARE_WORD) {
319 * Maybe check the last return code.
321 if (token == T_OP_CMP_TRUE) {
325 * Looks like a return code, treat is as such.
327 isreturn = fr_str2int(modreturn_table, pleft, -1);
328 if (isreturn != -1) {
329 *presult = (modreturn == isreturn);
335 * Bare words on the left can be attribute names.
337 if (radius_get_vp(request, pleft, &vp)) {
341 * VP exists, and that's all we're looking for.
343 if (token == T_OP_CMP_TRUE) {
344 *presult = (vp != NULL);
352 * The attribute on the LHS may
353 * have been a dynamically
354 * registered callback. i.e. it
355 * doesn't exist as a VALUE_PAIR.
356 * If so, try looking for it.
358 da = dict_attrbyname(pleft);
359 if (da && radius_find_compare(da->attr)) {
360 VALUE_PAIR *check = pairmake(pleft, pright, token);
361 *presult = (radius_callback_compare(request, NULL, check, NULL, NULL) == 0);
363 if (*presult) return TRUE;
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);
395 } /* else it's not a VP in a list */
406 if (!all_digits(pright)) {
407 RDEBUG2(" (Right field is not a number at: %s)", pright);
411 if (!all_digits(pleft)) {
412 RDEBUG2(" (Left field is not a number at: %s)", pleft);
419 lint = rint = 0; /* quiet the compiler */
426 * Check for truth or falsehood.
428 if (all_digits(pleft)) {
430 result = (lint != 0);
433 result = (*pleft != '\0');
439 result = (strcmp(pleft, pright) == 0);
443 result = (strcmp(pleft, pright) != 0);
447 result = (lint >= rint);
451 result = (lint > rint);
455 result = (lint <= rint);
459 result = (lint < rint);
466 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
469 * Include substring matches.
471 compare = regcomp(®, pright, cflags);
476 regerror(compare, ®, errbuf, sizeof(errbuf));
477 DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
482 compare = regexec(®, pleft,
483 REQUEST_MAX_REGEX + 1,
488 * Add new %{0}, %{1}, etc.
490 if (compare == 0) for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
493 free(request_data_get(request, request,
494 REQUEST_DATA_REGEX | i));
498 * We MAY have %{2} without %{1}.
500 if (rxmatch[i].rm_so == -1) continue;
503 * Copy substring into allocated buffer
505 r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1);
506 memcpy(r, pleft + rxmatch[i].rm_so,
507 rxmatch[i].rm_eo - rxmatch[i].rm_so);
508 r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
510 request_data_add(request, request,
511 REQUEST_DATA_REGEX | i,
514 result = (compare == 0);
521 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
524 * Include substring matches.
526 compare = regcomp(®, pright, cflags);
531 regerror(compare, ®, errbuf, sizeof(errbuf));
532 DEBUG("ERROR: Failed compiling regular expression: %s", errbuf);
537 compare = regexec(®, pleft,
538 REQUEST_MAX_REGEX + 1,
542 result = (compare != 0);
548 RDEBUG4(">>> NOT IMPLEMENTED %d", token);
558 int radius_evaluate_condition(REQUEST *request, int modreturn, int depth,
559 const char **ptr, int evaluate_it, int *presult)
561 int found_condition = FALSE;
564 int evaluate_next_condition = evaluate_it;
566 const char *q, *start;
567 FR_TOKEN token, lt, rt;
568 char left[1024], right[1024], comp[4];
569 const char *pleft, *pright;
570 char xleft[1024], xright[1024];
573 if (!ptr || !*ptr || (depth >= 64)) {
574 radlog(L_ERR, "Internal sanity check failed in evaluate condition");
583 while ((*p == ' ') || (*p == '\t')) p++;
588 if (!found_condition && (*p == '!')) {
589 RDEBUG4(">>> INVERT");
593 while ((*p == ' ') || (*p == '\t')) p++;
599 if (!found_condition && (*p == '(')) {
600 const char *end = p + 1;
603 * Evaluate the condition, bailing out on
606 RDEBUG4(">>> RECURSING WITH ... %s", end);
607 if (!radius_evaluate_condition(request, modreturn,
609 evaluate_next_condition,
615 if (evaluate_next_condition)
616 RDEBUG2("%.*s Converting !%s -> %s",
618 (result != FALSE) ? "TRUE" : "FALSE",
619 (result == FALSE) ? "TRUE" : "FALSE");
621 result = (result == FALSE);
626 * Start from the end of the previous
630 RDEBUG4(">>> AFTER RECURSION ... %s", end);
632 while ((*p == ' ') || (*p == '\t')) p++;
635 radlog(L_ERR, "No closing brace");
639 if (*p == ')') p++; /* eat closing brace */
640 found_condition = TRUE;
642 while ((*p == ' ') || (*p == '\t')) p++;
646 * At EOL or closing brace, update && return.
648 if (found_condition && (!*p || (*p == ')'))) break;
658 if (found_condition) {
660 * (A && B) means "evaluate B
661 * only if A was true"
663 if ((p[0] == '&') && (p[1] == '&')) {
664 if (!result) evaluate_next_condition = FALSE;
666 found_condition = FALSE;
667 continue; /* go back to the start */
671 * (A || B) means "evaluate B
672 * only if A was false"
674 if ((p[0] == '|') && (p[1] == '|')) {
675 if (result) evaluate_next_condition = FALSE;
677 found_condition = FALSE;
689 if (found_condition) {
690 radlog(L_ERR, "Consecutive conditions at %s", p);
694 RDEBUG4(">>> LOOKING AT %s", p);
698 * Look for common errors.
700 if ((p[0] == '%') && (p[1] == '{')) {
701 radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
706 * Look for WORD1 op WORD2
708 lt = gettoken(&p, left, sizeof(left));
709 if ((lt != T_BARE_WORD) &&
710 (lt != T_DOUBLE_QUOTED_STRING) &&
711 (lt != T_SINGLE_QUOTED_STRING) &&
712 (lt != T_BACK_QUOTED_STRING)) {
713 radlog(L_ERR, "Expected string or numbers at: %s", p);
718 if (evaluate_next_condition) {
719 pleft = expand_string(xleft, sizeof(xleft), request,
722 radlog(L_ERR, "Failed expanding string at: %s",
729 * Peek ahead, to see if it's:
733 * or something else, such as
741 while ((*q == ' ') || (*q == '\t')) q++;
744 * If the next thing is:
751 * Then WORD is just a test for existence.
752 * Remember that and skip ahead.
754 if (!*q || (*q == ')') ||
755 ((q[0] == '&') && (q[1] == '&')) ||
756 ((q[0] == '|') && (q[1] == '|'))) {
757 token = T_OP_CMP_TRUE;
768 token = gettoken(&p, comp, sizeof(comp));
769 if ((token < T_OP_NE) || (token > T_OP_CMP_EQ) ||
770 (token == T_OP_CMP_TRUE) ||
771 (token == T_OP_CMP_FALSE)) {
772 radlog(L_ERR, "Expected comparison at: %s", comp);
777 * Look for common errors.
779 if ((p[0] == '%') && (p[1] == '{')) {
780 radlog(L_ERR, "Bare %%{...} is invalid in condition at: %s", p);
788 if ((token == T_OP_REG_EQ) ||
789 (token == T_OP_REG_NE)) {
790 rt = getregex(&p, right, sizeof(right), &cflags);
791 if (rt != T_DOUBLE_QUOTED_STRING) {
792 radlog(L_ERR, "Expected regular expression at: %s", p);
797 rt = gettoken(&p, right, sizeof(right));
799 if ((rt != T_BARE_WORD) &&
800 (rt != T_DOUBLE_QUOTED_STRING) &&
801 (rt != T_SINGLE_QUOTED_STRING) &&
802 (rt != T_BACK_QUOTED_STRING)) {
803 radlog(L_ERR, "Expected string or numbers at: %s", p);
808 if (evaluate_next_condition) {
809 pright = expand_string(xright, sizeof(xright), request,
812 radlog(L_ERR, "Failed expanding string at: %s",
818 RDEBUG4(">>> %d:%s %d %d:%s",
819 lt, pleft, token, rt, pright);
822 if (evaluate_next_condition) {
826 if (!radius_do_cmp(request, &result, lt, pleft, token,
827 rt, pright, cflags, modreturn)) {
832 RDEBUG4(">>> INVERTING result");
833 result = (result == FALSE);
836 RDEBUG2("%.*s Evaluating %s(%.*s) -> %s",
838 invert ? "!" : "", p - start, start,
839 (result != FALSE) ? "TRUE" : "FALSE");
842 RDEBUG4(">>> GOT result %d", result);
845 * Not evaluating it. We may be just
848 } else if (request) {
849 RDEBUG2("%.*s Skipping %s(%.*s)",
851 invert ? "!" : "", p - start, start);
854 found_condition = TRUE;
855 } /* loop over the input condition */
857 if (!found_condition) {
858 radlog(L_ERR, "Syntax error. Expected condition at %s", p);
862 RDEBUG4(">>> AT EOL -> %d", result);
864 if (evaluate_it) *presult = result;
869 static void fix_up(REQUEST *request)
873 request->username = NULL;
874 request->password = NULL;
876 for (vp = request->packet->vps; vp != NULL; vp = vp->next) {
877 if ((vp->attribute == PW_USER_NAME) &&
878 !request->username) {
879 request->username = vp;
881 } else if (vp->attribute == PW_STRIPPED_USER_NAME) {
882 request->username = vp;
884 } else if (vp->attribute == PW_USER_PASSWORD) {
885 request->password = vp;
892 * The pairmove() function in src/lib/valuepair.c does all sorts of
893 * extra magic that we don't want here.
895 * FIXME: integrate this with the code calling it, so that we
896 * only paircopy() those attributes that we're really going to
899 void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
901 int i, j, count, from_count, to_count, tailto;
902 VALUE_PAIR *vp, *next, **last;
903 VALUE_PAIR **from_list, **to_list;
907 * Set up arrays for editing, to remove some of the
908 * O(N^2) dependencies. This also makes it easier to
909 * insert and remove attributes.
911 * It also means that the operators apply ONLY to the
912 * attributes in the original list. With the previous
913 * implementation of pairmove(), adding two attributes
914 * via "+=" and then "=" would mean that the second one
915 * wasn't added, because of the existence of the first
916 * one in the "to" list. This implementation doesn't
919 * Also, the previous implementation did NOT implement
920 * "-=" correctly. If two of the same attributes existed
921 * in the "to" list, and you tried to subtract something
922 * matching the *second* value, then the pairdelete()
923 * function was called, and the *all* attributes of that
924 * number were deleted. With this implementation, only
925 * the matching attributes are deleted.
928 for (vp = from; vp != NULL; vp = vp->next) count++;
929 from_list = rad_malloc(sizeof(*from_list) * count);
931 for (vp = *to; vp != NULL; vp = vp->next) count++;
932 to_list = rad_malloc(sizeof(*to_list) * count);
935 * Move the lists to the arrays, and break the list
939 for (vp = from; vp != NULL; vp = next) {
941 from_list[from_count++] = vp;
946 for (vp = *to; vp != NULL; vp = next) {
948 to_list[to_count++] = vp;
952 edited = rad_malloc(sizeof(*edited) * to_count);
953 memset(edited, 0, sizeof(*edited) * to_count);
955 RDEBUG4("::: FROM %d TO %d MAX %d", from_count, to_count, count);
958 * Now that we have the lists initialized, start working
961 for (i = 0; i < from_count; i++) {
964 RDEBUG4("::: Examining %s", from_list[i]->name);
967 * Attribute should be appended, OR the "to" list
968 * is empty, and we're supposed to replace or
969 * "add if not existing".
971 if (from_list[i]->operator == T_OP_ADD) goto append;
974 for (j = 0; j < to_count; j++) {
975 if (edited[j] || !to_list[j]) continue;
978 * Attributes aren't the same, skip them.
980 if (from_list[i]->attribute != to_list[j]->attribute) {
985 * We don't use a "switch" statement here
986 * because we want to break out of the
987 * "for" loop over 'j' in most cases.
991 * Over-write the FIRST instance of the
992 * matching attribute name. We free the
993 * one in the "to" list, and move over
994 * the one in the "from" list.
996 if (from_list[i]->operator == T_OP_SET) {
997 RDEBUG4("::: OVERWRITING %s FROM %d TO %d",
998 to_list[j]->name, i, j);
999 pairfree(&to_list[j]);
1000 to_list[j] = from_list[i];
1001 from_list[i] = NULL;
1007 * Add the attribute only if it does not
1008 * exist... but it exists, so we stop
1011 if (from_list[i]->operator == T_OP_EQ) {
1017 * Delete every attribute, independent
1020 if (from_list[i]->operator == T_OP_CMP_FALSE) {
1025 * Delete all matching attributes from
1028 if ((from_list[i]->operator == T_OP_SUB) ||
1029 (from_list[i]->operator == T_OP_CMP_EQ) ||
1030 (from_list[i]->operator == T_OP_LE) ||
1031 (from_list[i]->operator == T_OP_GE)) {
1033 int old_op = from_list[i]->operator;
1036 * Check for equality.
1038 from_list[i]->operator = T_OP_CMP_EQ;
1041 * If equal, delete the one in
1044 rcode = radius_compare_vps(NULL, from_list[i],
1047 * We may want to do more
1048 * subtractions, so we re-set the
1049 * operator back to it's original
1052 from_list[i]->operator = old_op;
1056 if (rcode != 0) goto delete;
1062 RDEBUG4("::: DELETING %s FROM %d TO %d",
1063 from_list[i]->name, i, j);
1064 pairfree(&to_list[j]);
1070 * Enforce <=. If it's
1075 RDEBUG4("::: REPLACING %s FROM %d TO %d",
1076 from_list[i]->name, i, j);
1077 pairfree(&to_list[j]);
1078 to_list[j] = from_list[i];
1079 from_list[i] = NULL;
1086 RDEBUG4("::: REPLACING %s FROM %d TO %d",
1087 from_list[i]->name, i, j);
1088 pairfree(&to_list[j]);
1089 to_list[j] = from_list[i];
1090 from_list[i] = NULL;
1099 rad_assert(0 == 1); /* panic! */
1103 * We were asked to add it if it didn't exist,
1104 * and it doesn't exist. Move it over to the
1105 * tail of the "to" list, UNLESS it was already
1106 * moved by another operator.
1108 if (!found && from_list[i]) {
1109 if ((from_list[i]->operator == T_OP_EQ) ||
1110 (from_list[i]->operator == T_OP_LE) ||
1111 (from_list[i]->operator == T_OP_GE) ||
1112 (from_list[i]->operator == T_OP_SET)) {
1114 RDEBUG4("::: APPENDING %s FROM %d TO %d",
1115 from_list[i]->name, i, tailto);
1116 to_list[tailto++] = from_list[i];
1117 from_list[i] = NULL;
1123 * Delete attributes in the "from" list.
1125 for (i = 0; i < from_count; i++) {
1126 if (!from_list[i]) continue;
1128 pairfree(&from_list[i]);
1132 RDEBUG4("::: TO in %d out %d", to_count, tailto);
1135 * Re-chain the "to" list.
1140 for (i = 0; i < tailto; i++) {
1141 if (!to_list[i]) continue;
1143 RDEBUG4("::: to[%d] = %s", i, to_list[i]->name);
1146 * Mash the operator to a simple '='. The
1147 * operators in the "to" list aren't used for
1148 * anything. BUT they're used in the "detail"
1149 * file and debug output, where we don't want to
1150 * see the operators.
1152 to_list[i]->operator = T_OP_EQ;
1155 last = &(*last)->next;
1158 rad_assert(request != NULL);
1159 rad_assert(request->packet != NULL);
1162 * Fix dumb cache issues
1164 if (to == &request->packet->vps) {
1166 } else if (request->parent && (to == &request->parent->packet->vps)) {
1167 fix_up(request->parent);
1177 * Copied shamelessly from conffile.c, to simplify the API for
1180 typedef enum conf_type {
1181 CONF_ITEM_INVALID = 0,
1188 struct conf_item *next;
1189 struct conf_part *parent;
1191 const char *filename;
1192 CONF_ITEM_TYPE type;
1199 FR_TOKEN value_type;
1203 * Add attributes to a list.
1205 int radius_update_attrlist(REQUEST *request, CONF_SECTION *cs,
1206 VALUE_PAIR *input_vps, const char *name)
1209 VALUE_PAIR *newlist, *vp;
1210 VALUE_PAIR **output_vps = NULL;
1211 REQUEST *myrequest = request;
1213 if (!request || !cs) return RLM_MODULE_INVALID;
1216 * If we are an inner tunnel session, permit the
1217 * policy to update the outer lists directly.
1219 if (strncmp(name, "outer.", 6) == 0) {
1220 if (!request->parent) return RLM_MODULE_NOOP;
1222 myrequest = request->parent;
1226 if (strcmp(name, "request") == 0) {
1227 output_vps = &myrequest->packet->vps;
1229 } else if (strcmp(name, "reply") == 0) {
1230 output_vps = &myrequest->reply->vps;
1233 } else if (strcmp(name, "proxy-request") == 0) {
1234 if (request->proxy) output_vps = &myrequest->proxy->vps;
1236 } else if (strcmp(name, "proxy-reply") == 0) {
1237 if (request->proxy_reply) output_vps = &request->proxy_reply->vps;
1240 } else if (strcmp(name, "config") == 0) {
1241 output_vps = &myrequest->config_items;
1243 } else if (strcmp(name, "control") == 0) {
1244 output_vps = &myrequest->config_items;
1247 } else if (strcmp(name, "coa") == 0) {
1248 if (!myrequest->coa) {
1249 request_alloc_coa(myrequest);
1250 myrequest->coa->proxy->code = PW_COA_REQUEST;
1253 if (myrequest->coa &&
1254 (myrequest->coa->proxy->code == PW_COA_REQUEST)) {
1255 output_vps = &myrequest->coa->proxy->vps;
1258 } else if (strcmp(name, "coa-reply") == 0) {
1259 if (myrequest->coa && /* match reply with request */
1260 (myrequest->coa->proxy->code == PW_COA_REQUEST) &&
1261 (myrequest->coa->proxy_reply)) {
1262 output_vps = &myrequest->coa->proxy_reply->vps;
1265 } else if (strcmp(name, "disconnect") == 0) {
1266 if (!myrequest->coa) {
1267 request_alloc_coa(myrequest);
1268 if (myrequest->coa) myrequest->coa->proxy->code = PW_DISCONNECT_REQUEST;
1271 if (myrequest->coa &&
1272 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
1273 output_vps = &myrequest->coa->proxy->vps;
1276 } else if (strcmp(name, "disconnect-reply") == 0) {
1277 if (myrequest->coa && /* match reply with request */
1278 (myrequest->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
1279 (myrequest->coa->proxy_reply)) {
1280 output_vps = &myrequest->coa->proxy_reply->vps;
1285 return RLM_MODULE_INVALID;
1288 if (!output_vps) return RLM_MODULE_NOOP; /* didn't update the list */
1290 newlist = paircopy(input_vps);
1292 RDEBUG2("Out of memory");
1293 return RLM_MODULE_FAIL;
1297 for (ci=cf_item_find_next(cs, NULL);
1299 ci=cf_item_find_next(cs, ci)) {
1303 * This should never happen.
1305 if (cf_item_is_section(ci)) {
1307 return RLM_MODULE_INVALID;
1310 cp = cf_itemtopair(ci);
1313 * The VP && CF lists should be in sync. If they're
1316 if (vp->flags.do_xlat) {
1320 value = expand_string(buffer, sizeof(buffer), request,
1321 cp->value_type, cp->value);
1324 return RLM_MODULE_INVALID;
1327 if (!pairparsevalue(vp, value)) {
1328 RDEBUG2("ERROR: Failed parsing value \"%s\" for attribute %s: %s",
1329 value, vp->name, fr_strerror());
1331 return RLM_MODULE_FAIL;
1333 vp->flags.do_xlat = 0;
1338 radius_pairmove(request, output_vps, newlist);
1340 return RLM_MODULE_UPDATED;