Fixup doxygen comments
[freeradius.git] / src / main / valuepair.c
1 /** Valuepair functions that are radiusd-specific and as such do not belong in
2  * the library.
3  *
4  * @file main/valuepair.c
5  *
6  * @ingroup AVP
7  *
8  * Version:     $Id$
9  *
10  *   This program is free software; you can redistribute it and/or modify
11  *   it under the terms of the GNU General Public License as published by
12  *   the Free Software Foundation; either version 2 of the License, or
13  *   (at your option) any later version.
14  *
15  *   This program is distributed in the hope that it will be useful,
16  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *   GNU General Public License for more details.
19  *
20  *   You should have received a copy of the GNU General Public License
21  *   along with this program; if not, write to the Free Software
22  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  *
24  * Copyright 2000,2006  The FreeRADIUS server project
25  * Copyright 2000  Alan DeKok <aland@ox.org>
26  */
27
28 #include <freeradius-devel/ident.h>
29 RCSID("$Id$")
30
31 #include <freeradius-devel/radiusd.h>
32 #include <freeradius-devel/rad_assert.h>
33
34 #ifdef HAVE_PCREPOSIX_H
35 #include <pcreposix.h>
36 #else
37 #ifdef HAVE_REGEX_H
38 #       include <regex.h>
39
40 /*
41  *  For POSIX Regular expressions.
42  *  (0) Means no extended regular expressions.
43  *  REG_EXTENDED means use extended regular expressions.
44  */
45 #ifndef REG_EXTENDED
46 #define REG_EXTENDED (0)
47 #endif
48
49 #ifndef REG_NOSUB
50 #define REG_NOSUB (0)
51 #endif
52 #endif
53 #endif
54
55 const FR_NAME_NUMBER pair_lists[] = {
56         { "request",            PAIR_LIST_REQUEST },
57         { "reply",              PAIR_LIST_REPLY },
58         { "config",             PAIR_LIST_CONTROL },
59         { "control",            PAIR_LIST_CONTROL },
60 #ifdef WITH_PROXY
61         { "proxy-request",      PAIR_LIST_PROXY_REQUEST },
62         { "proxy-reply",        PAIR_LIST_PROXY_REPLY },
63 #endif
64 #ifdef WITH_COA
65         { "coa",                PAIR_LIST_COA },
66         { "coa-reply",          PAIR_LIST_COA_REPLY },
67         { "disconnect",         PAIR_LIST_DM },
68         { "disconnect-reply",   PAIR_LIST_DM_REPLY },
69 #endif
70         {  NULL , -1 }
71 };
72
73 struct cmp {
74         unsigned int attribute;
75         unsigned int otherattr;
76         void *instance; /* module instance */
77         RAD_COMPARE_FUNC compare;
78         struct cmp *next;
79 };
80 static struct cmp *cmp;
81
82 /** Compares check and vp by value.
83  *
84  * Does not call any per-attribute comparison function, but does honour
85  * check.operator. Basically does "vp.value check.op check.value".
86  *
87  * @param request Current request
88  * @param check rvalue, and operator
89  * @param vp lvalue
90  */
91 int radius_compare_vps(REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
92 {
93         int ret = -2;
94
95         /*
96          *      Check for =* and !* and return appropriately
97          */
98         if (check->operator == T_OP_CMP_TRUE)  return 0;
99         if (check->operator == T_OP_CMP_FALSE) return 1;
100
101 #ifdef HAVE_REGEX_H
102         if (check->operator == T_OP_REG_EQ) {
103                 int i, compare;
104                 regex_t reg;
105                 char name[1024];
106                 char value[1024];
107                 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
108
109                 snprintf(name, sizeof(name), "%%{%s}", check->name);
110                 radius_xlat(value, sizeof(value), name, request, NULL);
111
112                 /*
113                  *      Include substring matches.
114                  */
115                 compare = regcomp(&reg, check->vp_strvalue, REG_EXTENDED);
116                 if (compare != 0) {
117                         char buffer[256];
118                         regerror(compare, &reg, buffer, sizeof(buffer));
119
120                         RDEBUG("Invalid regular expression %s: %s",
121                                check->vp_strvalue, buffer);
122                         return -1;
123                 }
124                 compare = regexec(&reg, value,  REQUEST_MAX_REGEX + 1,
125                                   rxmatch, 0);
126                 regfree(&reg);
127
128                 /*
129                  *      Add %{0}, %{1}, etc.
130                  */
131                 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
132                         char *p;
133                         char buffer[sizeof(check->vp_strvalue)];
134
135                         /*
136                          *      Didn't match: delete old
137                          *      match, if it existed.
138                          */
139                         if ((compare != 0) ||
140                             (rxmatch[i].rm_so == -1)) {
141                                 p = request_data_get(request, request,
142                                                      REQUEST_DATA_REGEX | i);
143                                 if (p) {
144                                         free(p);
145                                         continue;
146                                 }
147
148                                 /*
149                                  *      No previous match
150                                  *      to delete, stop.
151                                  */
152                                 break;
153                         }
154
155                         /*
156                          *      Copy substring into buffer.
157                          */
158                         memcpy(buffer, value + rxmatch[i].rm_so,
159                                rxmatch[i].rm_eo - rxmatch[i].rm_so);
160                         buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
161
162                         /*
163                          *      Copy substring, and add it to
164                          *      the request.
165                          *
166                          *      Note that we don't check
167                          *      for out of memory, which is
168                          *      the only error we can get...
169                          */
170                         p = strdup(buffer);
171                         request_data_add(request, request,
172                                          REQUEST_DATA_REGEX | i,
173                                          p, free);
174                 }
175                 if (compare == 0) return 0;
176                 return -1;
177         }
178
179         if (check->operator == T_OP_REG_NE) {
180                 int compare;
181                 regex_t reg;
182                 char name[1024];
183                 char value[1024];
184                 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
185
186                 snprintf(name, sizeof(name), "%%{%s}", check->name);
187                 radius_xlat(value, sizeof(value), name, request, NULL);
188
189                 /*
190                  *      Include substring matches.
191                  */
192                 compare = regcomp(&reg, (char *)check->vp_strvalue,
193                                   REG_EXTENDED);
194                 if (compare != 0) {
195                         char buffer[256];
196                         regerror(compare, &reg, buffer, sizeof(buffer));
197
198                         RDEBUG("Invalid regular expression %s: %s",
199                                check->vp_strvalue, buffer);
200                         return -1;
201                 }
202                 compare = regexec(&reg, value,  REQUEST_MAX_REGEX + 1,
203                                   rxmatch, 0);
204                 regfree(&reg);
205
206                 if (compare != 0) return 0;
207                 return -1;
208
209         }
210 #endif
211
212         /*
213          *      Tagged attributes are equal if and only if both the
214          *      tag AND value match.
215          */
216         if (check->flags.has_tag) {
217                 ret = ((int) vp->flags.tag) - ((int) check->flags.tag);
218                 if (ret != 0) return ret;
219         }
220
221         /*
222          *      Not a regular expression, compare the types.
223          */
224         switch(check->type) {
225 #ifdef ASCEND_BINARY
226                 /*
227                  *      Ascend binary attributes can be treated
228                  *      as opaque objects, I guess...
229                  */
230                 case PW_TYPE_ABINARY:
231 #endif
232                 case PW_TYPE_OCTETS:
233                         if (vp->length != check->length) {
234                                 ret = 1; /* NOT equal */
235                                 break;
236                         }
237                         ret = memcmp(vp->vp_strvalue, check->vp_strvalue,
238                                      vp->length);
239                         break;
240
241                 case PW_TYPE_STRING:
242                         ret = strcmp((char *)vp->vp_strvalue,
243                                      (char *)check->vp_strvalue);
244                         break;
245
246                 case PW_TYPE_BYTE:
247                 case PW_TYPE_SHORT:
248                 case PW_TYPE_INTEGER:
249                         ret = vp->vp_integer - check->vp_integer;
250                         break;
251
252                 case PW_TYPE_INTEGER64:
253                         /*
254                          *      Don't want integer overflow!
255                          */
256                         if (vp->vp_integer64 < check->vp_integer64) {
257                                 ret = -1;
258                         } else if (vp->vp_integer64 > check->vp_integer64) {
259                                 ret = +1;
260                         } else {
261                                 ret = 0;
262                         }
263                         break;
264
265                 case PW_TYPE_SIGNED:
266                         if (vp->vp_signed < check->vp_signed) {
267                                 ret = -1;
268                         } else if (vp->vp_signed > check->vp_signed) {
269                                 ret = +1;
270                         } else {
271                                 ret = 0;
272                         }
273                         break;
274
275                 case PW_TYPE_DATE:
276                         ret = vp->vp_date - check->vp_date;
277                         break;
278
279                 case PW_TYPE_IPADDR:
280                         ret = ntohl(vp->vp_ipaddr) - ntohl(check->vp_ipaddr);
281                         break;
282
283                 case PW_TYPE_IPV6ADDR:
284                         ret = memcmp(&vp->vp_ipv6addr, &check->vp_ipv6addr,
285                                      sizeof(vp->vp_ipv6addr));
286                         break;
287
288                 case PW_TYPE_IPV6PREFIX:
289                         ret = memcmp(&vp->vp_ipv6prefix, &check->vp_ipv6prefix,
290                                      sizeof(vp->vp_ipv6prefix));
291                         break;
292
293                 case PW_TYPE_IFID:
294                         ret = memcmp(&vp->vp_ifid, &check->vp_ifid,
295                                      sizeof(vp->vp_ifid));
296                         break;
297
298                 default:
299                         break;
300         }
301
302         return ret;
303 }
304
305
306 /** Compare check and vp. May call the attribute compare function.
307  *
308  * Unlike radius_compare_vps() this function will call any attribute-specific
309  * comparison function.
310  *
311  * @param req Current request
312  * @param request value pairs in the reqiest
313  * @param check
314  * @param check_pairs
315  * @param reply_pairs value pairs in the reply
316  * @return 
317  */
318 int radius_callback_compare(REQUEST *req, VALUE_PAIR *request,
319                             VALUE_PAIR *check, VALUE_PAIR *check_pairs,
320                             VALUE_PAIR **reply_pairs)
321 {
322         struct cmp *c;
323
324         /*
325          *      Check for =* and !* and return appropriately
326          */
327         if (check->operator == T_OP_CMP_TRUE)  return 0;
328         if (check->operator == T_OP_CMP_FALSE) return 1;
329
330         /*
331          *      See if there is a special compare function.
332          *
333          *      FIXME: use new RB-Tree code.
334          */
335         for (c = cmp; c; c = c->next) {
336                 if ((c->attribute == check->attribute) &&
337                     (check->vendor == 0)) {
338                         return (c->compare)(c->instance, req, request, check,
339                                 check_pairs, reply_pairs);
340                 }
341         }
342
343         if (!request) return -1; /* doesn't exist, don't compare it */
344
345         return radius_compare_vps(req, check, request);
346 }
347
348
349 /** Find a comparison function for two attributes.
350  *
351  * @param attribute
352  */
353 int radius_find_compare(unsigned int attribute)
354 {
355         struct cmp *c;
356
357         for (c = cmp; c; c = c->next) {
358                 if (c->attribute == attribute) {
359                         return TRUE;
360                 }
361         }
362
363         return FALSE;
364 }
365
366
367 /** See what attribute we want to compare with.
368  *
369  * @param attribute
370  */
371 static int otherattr(unsigned int attribute)
372 {
373         struct cmp *c;
374
375         for (c = cmp; c; c = c->next) {
376                 if (c->attribute == attribute) {
377                         return c->otherattr;
378                 }
379         }
380
381         return attribute;
382 }
383
384 /** Register a function as compare function.
385  *
386  * @param attribute
387  * @param other_attr we want to compare with. Normally this is the
388  *      same as attribute.
389  * You can set this to:
390  *      - -1    The same as attribute.
391  *      - 0     Always call compare function, not tied to request attribute.
392  *      - >0    Attribute to compare with. For example, PW_GROUP in a check
393  *              item needs to be compared with PW_USER_NAME in the incoming request.
394  * @param func comparison function
395  * @param instance argument to comparison function
396  * @return 0
397  */
398 int paircompare_register(unsigned int attribute, int other_attr, 
399                          RAD_COMPARE_FUNC func, void *instance)
400 {
401         struct cmp *c;
402
403         paircompare_unregister(attribute, func);
404
405         c = rad_malloc(sizeof(struct cmp));
406
407         c->compare   = func;
408         c->attribute = attribute;
409         c->otherattr = other_attr;
410         c->instance  = instance;
411         c->next      = cmp;
412         cmp = c;
413
414         return 0;
415 }
416
417 /** Unregister comparison function for an attribute
418  *
419  * @param attribute attribute to unregister for.
420  * @param func comparison function to remove.
421  * @return Void.
422  */
423 void paircompare_unregister(unsigned int attribute, RAD_COMPARE_FUNC func)
424 {
425         struct cmp *c, *last;
426
427         last = NULL;
428         for (c = cmp; c; c = c->next) {
429                 if (c->attribute == attribute && c->compare == func) {
430                         break;
431                 }
432                 last = c;
433         }
434
435         if (c == NULL) return;
436
437         if (last != NULL) {
438                 last->next = c->next;
439         } else {
440                 cmp = c->next;
441         }
442
443         free(c);
444 }
445
446 /** Compare two pair lists except for the password information.
447  *
448  * For every element in "check" at least one matching copy must be present
449  * in "reply".
450  *
451  * @param req Current request
452  * @param request request valuepairs
453  * @param check check/control valuepairs
454  * @param[in,out] reply reply value pairs
455  *
456  * @return 0 on match.
457  */
458 int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
459                 VALUE_PAIR **reply)
460 {
461         VALUE_PAIR *check_item;
462         VALUE_PAIR *auth_item;
463         
464         int result = 0;
465         int compare;
466         int other;
467
468         for (check_item = check;
469              check_item != NULL;
470              check_item = check_item->next) {
471                 /*
472                  *      If the user is setting a configuration value,
473                  *      then don't bother comparing it to any attributes
474                  *      sent to us by the user.  It ALWAYS matches.
475                  */
476                 if ((check_item->operator == T_OP_SET) ||
477                     (check_item->operator == T_OP_ADD)) {
478                         continue;
479                 }
480
481                 switch (check_item->attribute) {
482                         /*
483                          *      Attributes we skip during comparison.
484                          *      These are "server" check items.
485                          */
486                         case PW_CRYPT_PASSWORD:
487                         case PW_AUTH_TYPE:
488                         case PW_AUTZ_TYPE:
489                         case PW_ACCT_TYPE:
490                         case PW_SESSION_TYPE:
491                         case PW_STRIP_USER_NAME:
492                                 continue;
493                                 break;
494
495                         /*
496                          *      IF the password attribute exists, THEN
497                          *      we can do comparisons against it.  If not,
498                          *      then the request did NOT contain a
499                          *      User-Password attribute, so we CANNOT do
500                          *      comparisons against it.
501                          *
502                          *      This hack makes CHAP-Password work..
503                          */
504                         case PW_USER_PASSWORD:
505                                 if (check_item->operator == T_OP_CMP_EQ) {
506                                         DEBUG("WARNING: Found User-Password == \"...\".");
507                                         DEBUG("WARNING: Are you sure you don't mean Cleartext-Password?");
508                                         DEBUG("WARNING: See \"man rlm_pap\" for more information.");
509                                 }
510                                 if (pairfind(request, PW_USER_PASSWORD, 0) == NULL) {
511                                         continue;
512                                 }
513                                 break;
514                 }
515
516                 /*
517                  *      See if this item is present in the request.
518                  */
519                 other = otherattr(check_item->attribute);
520
521                 auth_item = request;
522         try_again:
523                 if (other >= 0) {
524                         while (auth_item != NULL) {
525                                 if ((auth_item->attribute == 
526                                     (unsigned int) other) ||
527                                     (other == 0)) {
528                                         break;
529                                 }
530                                 auth_item = auth_item->next;
531                         }
532                 }
533
534                 /*
535                  *      Not found, it's not a match.
536                  */
537                 if (auth_item == NULL) {
538                         /*
539                          *      Didn't find it.  If we were *trying*
540                          *      to not find it, then we succeeded.
541                          */
542                         if (check_item->operator == T_OP_CMP_FALSE) {
543                                 continue;
544                         } else {
545                                 return -1;
546                         }
547                 }
548
549                 /*
550                  *      Else we found it, but we were trying to not
551                  *      find it, so we failed.
552                  */
553                 if (check_item->operator == T_OP_CMP_FALSE) {
554                         return -1;
555                 }
556
557
558                 /*
559                  *      We've got to xlat the string before doing
560                  *      the comparison.
561                  */
562                 if (check_item->flags.do_xlat) {
563                         int rcode;
564                         char buffer[sizeof(check_item->vp_strvalue)];
565
566                         check_item->flags.do_xlat = 0;
567                         rcode = radius_xlat(buffer, sizeof(buffer),
568                                             check_item->vp_strvalue,
569                                             req, NULL);
570
571                         /*
572                          *      Parse the string into a new value.
573                          */
574                         pairparsevalue(check_item, buffer);
575                 }
576
577                 /*
578                  *      OK it is present now compare them.
579                  */
580                 compare = radius_callback_compare(req, auth_item, check_item,
581                                                   check, reply);
582
583                 switch (check_item->operator) {
584                         case T_OP_EQ:
585                         default:
586                                 radlog(L_INFO,  "Invalid operator for item %s: "
587                                                 "reverting to '=='", check_item->name);
588
589                         case T_OP_CMP_TRUE:
590                         case T_OP_CMP_FALSE:
591                         case T_OP_CMP_EQ:
592                                 if (compare != 0) result = -1;
593                                 break;
594
595                         case T_OP_NE:
596                                 if (compare == 0) result = -1;
597                                 break;
598
599                         case T_OP_LT:
600                                 if (compare >= 0) result = -1;
601                                 break;
602
603                         case T_OP_GT:
604                                 if (compare <= 0) result = -1;
605                                 break;
606
607                         case T_OP_LE:
608                                 if (compare > 0) result = -1;
609                                 break;
610
611                         case T_OP_GE:
612                                 if (compare < 0) result = -1;
613                                 break;
614
615 #ifdef HAVE_REGEX_H
616                         case T_OP_REG_EQ:
617                         case T_OP_REG_NE:
618                                 if (compare != 0) result = -1;
619                                 break;
620 #endif
621                 } /* switch over the operator of the check item */
622
623                 /*
624                  *      This attribute didn't match, but maybe there's
625                  *      another of the same attribute, which DOES match.
626                  */
627                 if ((result != 0) && (other >= 0)) {
628                         auth_item = auth_item->next;
629                         result = 0;
630                         goto try_again;
631                 }
632
633         } /* for every entry in the check item list */
634
635         return result;
636 }
637
638 /** Move pairs, replacing/over-writing them, and doing xlat.
639  *
640  * Move attributes from one list to the other if not already present.
641  */
642 void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
643 {
644         VALUE_PAIR **tailto, *i, *j, *next;
645         VALUE_PAIR *tailfrom = NULL;
646         VALUE_PAIR *found;
647
648         /*
649          *      Point "tailto" to the end of the "to" list.
650          */
651         tailto = to;
652         for (i = *to; i; i = i->next) {
653                 tailto = &i->next;
654         }
655
656         /*
657          *      Loop over the "from" list.
658          */
659         for (i = *from; i; i = next) {
660                 next = i->next;
661
662                 /*
663                  *      Don't move 'fallthrough' over.
664                  */
665                 if (i->attribute == PW_FALL_THROUGH) {
666                         tailfrom = i;
667                         continue;
668                 }
669
670                 /*
671                  *      We've got to xlat the string before moving
672                  *      it over.
673                  */
674                 if (i->flags.do_xlat) {
675                         int rcode;
676                         char buffer[sizeof(i->vp_strvalue)];
677
678                         i->flags.do_xlat = 0;
679                         rcode = radius_xlat(buffer, sizeof(buffer),
680                                             i->vp_strvalue,
681                                             req, NULL);
682
683                         /*
684                          *      Parse the string into a new value.
685                          */
686                         pairparsevalue(i, buffer);
687                 }
688
689                 found = pairfind(*to, i->attribute, i->vendor);
690                 switch (i->operator) {
691
692                         /*
693                          *      If a similar attribute is found,
694                          *      delete it.
695                          */
696                         case T_OP_SUB:          /* -= */
697                                 if (found) {
698                                         if (!i->vp_strvalue[0] ||
699                                             (strcmp((char *)found->vp_strvalue,
700                                                     (char *)i->vp_strvalue) == 0)) {
701                                                 pairdelete(to, found->attribute,
702                                                         found->vendor);
703
704                                         /*
705                                          *      'tailto' may have been
706                                          *      deleted...
707                                          */
708                                         tailto = to;
709                                         for (j = *to; j; j = j->next) {
710                                                 tailto = &j->next;
711                                         }
712                                 }
713                         }
714                         tailfrom = i;
715                         continue;
716                         break;
717
718                         /*
719                          *      Add it, if it's not already there.
720                          */
721                         case T_OP_EQ:           /* = */
722                                 if (found) {
723                                         tailfrom = i;
724                                         continue; /* with the loop */
725                                 }
726                                 break;
727
728                         /*
729                          *      If a similar attribute is found,
730                          *      replace it with the new one.  Otherwise,
731                          *      add the new one to the list.
732                          */
733                         case T_OP_SET:          /* := */
734                                 if (found) {
735                                         VALUE_PAIR *vp;
736
737                                         vp = found->next;
738                                         memcpy(found, i, sizeof(*found));
739                                         found->next = vp;
740                                         tailfrom = i;
741                                         continue;
742                                 }
743                                 break;
744
745                         /*
746                          *      FIXME: Add support for <=, >=, <, >
747                          *
748                          *      which will mean (for integers)
749                          *      'make the attribute the smaller, etc'
750                          */
751
752                         /*
753                          *  Add the new element to the list, even
754                          *  if similar ones already exist.
755                          */
756                         default:
757                         case T_OP_ADD:          /* += */
758                                 break;
759                 }
760
761                 if (tailfrom) {
762                         tailfrom->next = next;
763                 } else {
764                         *from = next;
765                 }
766
767                 /*
768                  *      If ALL of the 'to' attributes have been deleted,
769                  *      then ensure that the 'tail' is updated to point
770                  *      to the head.
771                  */
772                 if (!*to) {
773                         tailto = to;
774                 }
775                 *tailto = i;
776                 if (i) {
777                         i->next = NULL;
778                         tailto = &i->next;
779                 }
780         } /* loop over the 'from' list */
781 }
782
783 /** Create a pair and add it to a particular list of VPs
784  *
785  * Note that this function ALWAYS returns. If we're OOM, then it causes the
786  * server to exit!
787  */
788 VALUE_PAIR *radius_paircreate(UNUSED REQUEST *request, VALUE_PAIR **vps,
789                               unsigned int attribute, unsigned int vendor, int type)
790 {
791         VALUE_PAIR *vp;
792
793         vp = paircreate(attribute, vendor, type);
794         if (!vp) {
795                 radlog(L_ERR, "No memory!");
796                 rad_assert("No memory" == NULL);
797                 _exit(1);
798         }
799
800         if (vps) pairadd(vps, vp);
801
802         return vp;
803 }
804
805 /** Create a pair, and add it to a particular list of VPs
806  *
807  * Note that this function ALWAYS returns.  If we're OOM, then it causes the
808  * server to exit!
809  *
810  * @param[in] request current request.
811  * @param[in] vps to modify.
812  * @param[in] attribute name.
813  * @param[in] value attribute value.
814  * @param[in] operator fr_tokens value.
815  * @return a new VALUE_PAIR.
816  */
817 VALUE_PAIR *radius_pairmake(UNUSED REQUEST *request, VALUE_PAIR **vps,
818                             const char *attribute, const char *value,
819                             int operator)
820 {
821         VALUE_PAIR *vp;
822
823         vp = pairmake(attribute, value, operator);
824         if (!vp) return NULL;
825
826         if (vps) pairadd(vps, vp);
827
828         return vp;
829 }
830
831 /** Print a single valuepair to stderr or error log.
832  *
833  * @param[in] vp list to print.
834  */
835 void debug_pair(VALUE_PAIR *vp)
836 {
837         if (!vp || !debug_flag || !fr_log_fp) return;
838
839         vp_print(fr_log_fp, vp);
840 }
841
842 /** Print a list of valuepairs to stderr or error log.
843  *
844  * @param[in] vp to print.
845  */
846 void debug_pair_list(VALUE_PAIR *vp)
847 {
848         if (!vp || !debug_flag || !fr_log_fp) return;
849
850         while (vp) {
851                 vp_print(fr_log_fp, vp);
852                 vp = vp->next;
853         }
854         fflush(fr_log_fp);
855 }
856
857 /** Resolve attribute pair_lists_t value to an attribute list.
858  * 
859  * The value returned is a pointer to the pointer of the HEAD of the list
860  * in the REQUEST. If the head of the list changes, the pointer will still
861  * be valid.
862  *
863  * @param[in] request containing the target lists.
864  * @param[in] list pair_list_t value to resolve to VALUE_PAIR list.
865  *      Will be NULL if list name couldn't be resolved.
866  */
867 VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list)
868 {       
869         switch (list) {
870                 case PAIR_LIST_UNKNOWN:
871                 default:
872                         break;
873
874                 case PAIR_LIST_REQUEST:
875                         return &request->packet->vps;
876
877                 case PAIR_LIST_REPLY:
878                         return &request->reply->vps;
879
880                 case PAIR_LIST_CONTROL:
881                         return &request->config_items;
882
883 #ifdef WITH_PROXY
884                 case PAIR_LIST_PROXY_REQUEST:
885                         return &request->proxy->vps;
886
887                 case PAIR_LIST_PROXY_REPLY:
888                         return &request->proxy_reply->vps;
889 #endif
890 #ifdef WITH_COA
891                 case PAIR_LIST_COA:
892                         if (request->coa &&
893                             (request->coa->proxy->code == PW_COA_REQUEST)) {
894                                 return &request->coa->proxy->vps;
895                         }
896                         break;
897
898                 case PAIR_LIST_COA_REPLY:
899                         if (request->coa && /* match reply with request */
900                             (request->coa->proxy->code == PW_COA_REQUEST) &&
901                             request->coa->proxy_reply) {
902                                 return &request->coa->proxy_reply->vps;
903                         }
904                         break;
905
906                 case PAIR_LIST_DM:
907                         if (request->coa &&
908                             (request->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
909                                 return &request->coa->proxy->vps;
910                         }
911                         break;
912
913                 case PAIR_LIST_DM_REPLY:
914                         if (request->coa && /* match reply with request */
915                             (request->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
916                             request->coa->proxy_reply) {
917                                 return &request->coa->proxy->vps;
918                         }
919                         break;
920 #endif
921         }
922         
923         return NULL;
924 }
925
926 /** Resolve attribute name to a list.
927  *
928  * Check the name string for qualifiers that specify a list and return
929  * an pair_lists_t value for that list. This value may be passed to
930  * radius_list, along with the current request, to get a pointer to the
931  * actual list in the request.
932  * 
933  * If qualifiers were consumed, write a new pointer into name to the
934  * char after the last qualifier to be consumed.
935  *
936  * radius_list_name should be called before passing a name string that
937  * may contain qualifiers to dict_attrbyname.
938  *
939  * @see dict_attrbyname
940  *
941  * @param[in,out] name of attribute.
942  * @param[in] unknown the list to return if no qualifiers were found.
943  * @return PAIR_LIST_UNKOWN if qualifiers couldn't be resolved to a list.
944  */
945 pair_lists_t radius_list_name(const char **name, pair_lists_t unknown)
946 {
947         const char *p = *name;
948         char *q;
949         
950         /* This should never be a NULL pointer or zero length string */
951         rad_assert(name && *name);
952
953         q = strrchr(p, ':');
954         if (!q) {
955                 return unknown;
956         }
957         
958         *name = (q + 1);
959         
960         return fr_substr2int(pair_lists, p, PAIR_LIST_UNKNOWN, (q - p));
961 }
962
963 /** Resolve attribute name to a request.
964  * 
965  * Check the name string for qualifiers that reference a parent request and
966  * write the pointer to this request to 'request'.
967  *
968  * If qualifiers were consumed, write a new pointer into name to the
969  * char after the last qualifier to be consumed.
970  *
971  * radius_ref_request should be called before radius_list_name.
972  *
973  * @see radius_list_name
974  * @param[in,out] name of attribute.
975  * @param[in,out] request current request.
976  * @return FALSE if qualifiers found but not in a tunnel, else TRUE.
977  */
978 int radius_ref_request(const char **name, REQUEST **request)
979 {
980         rad_assert(name && *name);
981         rad_assert(request && *request);
982         
983         if (strncmp(*name, "outer.", 6) == 0) {
984                 if (!(*request)->parent) {
985                         return FALSE;
986                 }
987                 *request = (*request)->parent;
988                 *name += 6;
989         }
990         
991         return TRUE;
992 }
993
994 /** Return a VP from the specified request.
995  *
996  * @param request current request.
997  * @param name attribute name including qualifiers.
998  * @param vp_p where to write the pointer to the resolved VP. 
999  *      Will be NULL if the attribute couldn't be resolved.
1000  * @return False if either the attribute or qualifier were invalid, else true
1001  */
1002 int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p)
1003 {
1004         VALUE_PAIR **vps;
1005         pair_lists_t list;
1006         
1007         const DICT_ATTR *da;
1008         
1009         *vp_p = NULL;
1010         
1011         if (!radius_ref_request(&name, &request)) {
1012                 RDEBUG("WARNING: Attribute name refers to outer request"
1013                        " but not in a tunnel.");
1014                 return TRUE;    /* Discuss, we don't actually know if
1015                                    the attrname was valid... */
1016         }
1017         
1018         list = radius_list_name(&name, PAIR_LIST_REQUEST);
1019         if (list == PAIR_LIST_UNKNOWN) {
1020                 RDEBUG("ERROR: Invalid list qualifier");
1021                 return FALSE;
1022         }
1023         
1024         da = dict_attrbyname(name);
1025         if (!da) {
1026                 RDEBUG("ERROR: Attribute \"%s\" unknown", name);
1027                 return FALSE;
1028         }
1029
1030         vps = radius_list(request, list);
1031         rad_assert(vps);
1032         
1033         /*
1034          *      May not may not be found, but it *is* a known name.
1035          */
1036         *vp_p = pairfind(*vps, da->attr, da->vendor);
1037         return TRUE;
1038 }