2 * valuepair.c Functions to handle VALUE_PAIRs
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library 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 GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
20 * Copyright 2000 The FreeRADIUS server project
23 static const char rcsid[] = "$Id$";
27 #include <sys/types.h>
34 #include "libradius.h"
46 static const char *months[] = {
47 "jan", "feb", "mar", "apr", "may", "jun",
48 "jul", "aug", "sep", "oct", "nov", "dec" };
52 * Create a new valuepair.
54 VALUE_PAIR *paircreate(int attr, int type)
59 if ((vp = malloc(sizeof(VALUE_PAIR))) == NULL) {
60 librad_log("out of memory");
63 memset(vp, 0, sizeof(VALUE_PAIR));
65 vp->operator = T_OP_EQ;
69 * Dictionary type over-rides what the caller says.
71 if ((da = dict_attrbyvalue(attr)) != NULL) {
72 strcpy(vp->name, da->name);
74 } else if (VENDOR(attr) == 0) {
75 sprintf(vp->name, "Attr-%u", attr);
77 sprintf(vp->name, "Vendor-%u-Attr-%u",
78 VENDOR(attr), attr & 0xffff);
95 * Release the memory used by a list of attribute-value
96 * pairs, and sets the pair pointer to NULL.
98 void pairfree(VALUE_PAIR **pair_ptr)
100 VALUE_PAIR *next, *pair;
102 if (!pair_ptr) return;
105 while (pair != NULL) {
116 * Find the pair with the matching attribute
118 VALUE_PAIR * pairfind(VALUE_PAIR *first, int attr)
120 while(first && first->attribute != attr)
127 * Delete the pair(s) with the matching attribute
129 void pairdelete(VALUE_PAIR **first, int attr)
131 VALUE_PAIR *i, *next;
132 VALUE_PAIR **last = first;
134 for(i = *first; i; i = next) {
136 if (i->attribute == attr) {
146 * Add a pair at the end of a VALUE_PAIR list.
148 void pairadd(VALUE_PAIR **first, VALUE_PAIR *add)
152 if (*first == NULL) {
156 for(i = *first; i->next; i = i->next)
162 * Copy just a certain type of pairs.
164 VALUE_PAIR *paircopy2(VALUE_PAIR *vp, int attr)
166 VALUE_PAIR *first, *n, **last;
172 if (attr >= 0 && vp->attribute != attr) {
176 if ((n = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
177 librad_log("out of memory");
180 memcpy(n, vp, sizeof(VALUE_PAIR));
193 VALUE_PAIR *paircopy(VALUE_PAIR *vp)
195 return paircopy2(vp, -1);
200 * Move attributes from one list to the other
201 * if not already present.
203 void pairmove(VALUE_PAIR **to, VALUE_PAIR **from)
205 VALUE_PAIR **tailto, *i, *j, *next;
206 VALUE_PAIR *tailfrom = NULL;
208 int has_password = 0;
217 * First, see if there are any passwords here, and
218 * point "tailto" to the end of the "to" list.
221 for(i = *to; i; i = i->next) {
222 if (i->attribute == PW_PASSWORD ||
223 i->attribute == PW_CRYPT_PASSWORD)
229 * Loop over the "from" list.
231 for(i = *from; i; i = next) {
234 * If there was a password in the "to" list,
235 * do not move any other password from the
236 * "from" to the "to" list.
239 (i->attribute == PW_PASSWORD ||
240 i->attribute == PW_CRYPT_PASSWORD)) {
245 * If the attribute is already present in "to",
246 * do not move it from "from" to "to". We make
247 * an exception for "Hint" which can appear multiple
248 * times, and we never move "Fall-Through".
250 if (i->attribute == PW_FALL_THROUGH ||
251 (i->attribute != PW_HINT && i->attribute != PW_FRAMED_ROUTE)) {
253 found = pairfind(*to, i->attribute);
254 switch (i->operator) {
257 * If a similar attribute is found,
260 case T_OP_SUB: /* -= */
262 if (!i->strvalue[0] ||
263 (strcmp((char *)found->strvalue,
264 (char *)i->strvalue) == 0)){
265 pairdelete(to, found->attribute);
268 * 'tailto' may have been
272 for(j = *to; j; j = j->next) {
281 /* really HAVE_REGEX_H */
284 * Attr-Name =~ "s/find/replace/"
286 * Very bad code. Barely working,
292 (i->strvalue[0] == 's')) {
300 q = strchr(p + 1, *p);
301 if (!q || (q[strlen(q) - 1] != *p)) {
305 str = strdup(i->strvalue + 2);
308 q[strlen(q) - 1] = '\0';
310 regcomp(®, str, 0);
311 if (regexec(®, found->strvalue,
313 fprintf(stderr, "\"%s\" will have %d to %d replaced with %s\n",
314 found->strvalue, match[0].rm_so,
321 tailfrom = i; /* don't copy it over */
325 case T_OP_EQ: /* = */
327 * FIXME: Tunnel attributes with
328 * different tags are different
333 continue; /* with the loop */
338 * If a similar attribute is found,
339 * replace it with the new one. Otherwise,
340 * add the new one to the list.
342 case T_OP_SET: /* := */
344 pairdelete(to, found->attribute);
346 * 'tailto' may have been
350 for(j = *to; j; j = j->next) {
357 * Add the new element to the list, even
358 * if similar ones already exist.
361 case T_OP_ADD: /* += */
366 tailfrom->next = next;
371 * If ALL of the 'to' attributes have been deleted,
372 * then ensure that the 'tail' is updated to point
387 * Move one kind of attributes from one list to the other
389 void pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, int attr)
391 VALUE_PAIR *to_tail, *i, *next;
392 VALUE_PAIR *iprev = NULL;
395 * Find the last pair in the "to" list and put it in "to_tail".
399 for(i = *to; i; i = i->next)
404 for(i = *from; i; i = next) {
409 * If the attribute to move is NOT a VSA, then it
410 * ignores any attributes which do not match exactly.
412 if ((attr != PW_VENDOR_SPECIFIC) &&
413 (i->attribute != attr)) {
419 * If the attribute to move IS a VSA, then it ignores
420 * any non-VSA attribute.
422 if ((attr == PW_VENDOR_SPECIFIC) &&
423 (VENDOR(i->attribute) == 0)) {
429 * Remove the attribute from the "from" list.
437 * Add the attribute to the "to" list.
450 * Sort of strtok/strsep function.
452 static char *mystrtok(char **ptr, const char *sep)
458 while (**ptr && strchr(sep, **ptr))
463 while (**ptr && strchr(sep, **ptr) == NULL)
471 * Turn printable string into time_t
472 * Returns -1 on error, 0 on OK.
474 static int gettime(const char *valstr, time_t *lvalue)
484 tm = localtime_r(&t, &s_tm);
486 strNcpy(buf, valstr, sizeof(buf));
489 f[0] = mystrtok(&p, " \t");
490 f[1] = mystrtok(&p, " \t");
491 f[2] = mystrtok(&p, " \t");
492 if (!f[0] || !f[1] || !f[2]) return -1;
495 * The month is text, which allows us to find it easily.
498 for (i = 0; i < 3; i++) {
499 if (isalpha( (int) *f[i])) {
501 * Bubble the month to the front of the list
507 for (i = 0; i < 12; i++) {
508 if (strncasecmp(months[i], f[0], 3) == 0) {
516 /* month not found? */
517 if (tm->tm_mon == 12) return -1;
520 * The year may be in f[1], or in f[2]
522 tm->tm_year = atoi(f[1]);
523 tm->tm_mday = atoi(f[2]);
525 if (tm->tm_year >= 1900) {
530 * We can't use 2-digit years any more, they make it
531 * impossible to tell what's the day, and what's the year.
533 if (tm->tm_mday < 1900) return -1;
536 * Swap the year and the day.
539 tm->tm_year = tm->tm_mday - 1900;
544 * If the day is out of range, die.
546 if ((tm->tm_mday < 1) || (tm->tm_mday > 31)) {
551 * Returns -1 on error.
554 if (t == (time_t) -1) return -1;
562 * Parse a string value into a given VALUE_PAIR
564 VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value)
571 * Even for integers, dates and ip addresses we
572 * keep the original string in vp->strvalue.
574 strNcpy((char *)vp->strvalue, value, sizeof(vp->strvalue));
575 vp->length = strlen(vp->strvalue);
580 * Already handled above.
586 * FIXME: complain if hostname
587 * cannot be resolved, or resolve later!
589 if ((p = strrchr(value, '+')) != NULL && !p[1]) {
590 cs = s = strdup(value);
593 vp->flags.addport = 1;
598 vp->lvalue = librad_dodns ? ip_getaddr(cs) :
603 case PW_TYPE_INTEGER:
605 * If it starts with a digit, it must
606 * be a number (or a range).
608 * Note that ALL integers are unsigned!
610 if (isdigit((int) *value)) {
611 vp->lvalue = (uint32_t) strtoul(value, NULL, 10);
615 * Look for the named value for the given
618 else if ((dval = dict_valbyname(vp->attribute, value)) == NULL) {
619 librad_log("Unknown value %s for attribute %s",
623 vp->lvalue = dval->value;
629 if (gettime(value, (time_t *)&vp->lvalue) < 0) {
630 librad_log("failed to parse time string "
636 case PW_TYPE_ABINARY:
639 * Special case to convert filter to binary
641 if ( filterBinary( vp, value ) < 0 ) {
642 librad_log("failed to parse Ascend binary attribute: %s",
648 * If Ascend binary is NOT defined,
649 * then fall through to raw octets, so that
650 * the user can at least make them by hand...
653 /* raw octets: 0x01020304... */
655 if (strncasecmp(value, "0x", 2) == 0) {
661 while (*cp && vp->length < MAX_STRING_LEN) {
664 if (sscanf(cp, "%02x", &tmp) != 1) {
665 librad_log("Non-hex characters at %c%c", cp[0], cp[1]);
678 if (ifid_aton(value, vp->strvalue) == NULL) {
679 librad_log("failed to parse interface-id "
680 "string \"%s\"", value);
684 vp->strvalue[vp->length] = '\0';
691 librad_log("unknown attribute type %d", vp->type);
699 * Create a VALUE_PAIR from an ASCII attribute and value.
701 VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
714 * Check for tags in 'Attribute:Tag' format.
719 ts = strrchr( attribute, ':' );
721 /* Colon found with something behind it */
722 if (ts[1] == '*' && ts[2] == 0) {
723 /* Wildcard tag for check items */
726 } else if ((ts[1] >= '0') && (ts[1] <= '9')) {
727 /* It's not a wild card tag */
728 tag = strtol(ts + 1, &tc, 0);
729 if (tc && !*tc && TAG_VALID_ZERO(tag))
733 librad_log("Invalid tag for attribute %s", attribute);
739 if ((da = dict_attrbyname(attribute)) == NULL) {
740 librad_log("Unknown attribute %s", attribute);
744 if ((vp = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
745 librad_log("out of memory");
749 memset(vp, 0, sizeof(VALUE_PAIR));
750 vp->attribute = da->attr;
752 vp->operator = (operator == 0) ? T_OP_EQ : operator;
753 strcpy(vp->name, da->name);
754 vp->flags = da->flags;
757 /* Check for a tag in the 'Merit' format of:
758 * :Tag:Value. Print an error if we already found
759 * a tag in the Attribute.
762 if (value && (*value == ':' && da->flags.has_tag)) {
763 /* If we already found a tag, this is invalid */
766 librad_log("Duplicate tag %s for attribute %s",
768 DEBUG("Duplicate tag %s for attribute %s\n",
773 /* Colon found and attribute allows a tag */
774 if (value[1] == '*' && value[2] == ':') {
775 /* Wildcard tag for check items */
780 tag = strtol(value + 1, &tc, 0);
781 if (tc && *tc==':' && TAG_VALID_ZERO(tag))
792 switch (vp->operator) {
797 * For =* and !* operators, the value is irrelevant
802 vp->strvalue[0] = '\0';
808 * Regular expression comparison of integer attributes
809 * does a STRING comparison of the names of their
810 * integer attributes.
812 case T_OP_REG_EQ: /* =~ */
813 case T_OP_REG_NE: /* !~ */
814 if (vp->type == PW_TYPE_INTEGER) {
819 * Regular expression match with no regular
820 * expression is wrong.
827 res = regcomp(&cre, value, REG_EXTENDED|REG_NOSUB);
831 regerror(res, &cre, msg, sizeof(msg));
832 librad_log("Illegal regular expression in attribute: %s: %s",
839 librad_log("Regelar expressions not enabled in this build, error in attribute %s",
846 if (value && (pairparsevalue(vp, value) == NULL)) {
855 * Read a valuepair from a buffer, and advance pointer.
856 * Sets *eol to T_EOL if end of line was encountered.
858 VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol)
864 LRAD_TOKEN token, t, xlat;
870 token = gettoken(ptr, attr, sizeof(attr));
872 /* If it's a comment, then exit, as we haven't read a pair */
873 if (token == T_HASH) {
875 librad_log("Read a comment instead of a token");
879 /* It's not a comment, so it MUST be an attribute */
880 if ((token == T_EOL) ||
882 librad_log("No token read where we expected an attribute name");
886 /* Now we should have an '=' here. */
887 token = gettoken(ptr, buf, sizeof(buf));
888 if (token < T_EQSTART || token > T_EQEND) {
889 librad_log("expecting '='");
893 /* Read value. Note that empty string values are allowed */
894 xlat = gettoken(ptr, value, sizeof(value));
896 librad_log("failed to get value");
901 * Peek at the next token. Must be T_EOL, T_COMMA, or T_HASH
904 t = gettoken(&p, buf, sizeof(buf));
905 if (t != T_EOL && t != T_COMMA && t != T_HASH) {
906 librad_log("Expected end of line or comma");
917 * Make the full pair now.
920 vp = pairmake(attr, value, token);
924 * Mark the pair to be allocated later.
926 case T_BACK_QUOTED_STRING:
927 vp = pairmake(attr, NULL, token);
930 vp->flags.do_xlat = 1;
931 strNcpy(vp->strvalue, value, sizeof(vp->strvalue));
940 * Read one line of attribute/value pairs. This might contain
941 * multiple pairs seperated by comma's.
943 LRAD_TOKEN userparse(char *buffer, VALUE_PAIR **first_pair)
947 LRAD_TOKEN last_token = T_INVALID;
948 LRAD_TOKEN previous_token;
951 * We allow an empty line.
958 previous_token = last_token;
959 if ((vp = pairread(&p, &last_token)) == NULL) {
962 pairadd(first_pair, vp);
963 } while (*p && (last_token == T_COMMA));
966 * Don't tell the caller that there was a comment.
968 if (last_token == T_HASH) {
969 return previous_token;
973 * And return the last token which we read.