2 * valuepair.c Functions to handle VALUE_PAIRs
8 static const char rcsid[] = "$Id$";
12 #include <sys/types.h>
19 #include "libradius.h"
31 static const char *months[] = {
32 "jan", "feb", "mar", "apr", "may", "jun",
33 "jul", "aug", "sep", "oct", "nov", "dec" };
37 * Create a new valuepair.
39 VALUE_PAIR *paircreate(int attr, int type)
44 if ((vp = malloc(sizeof(VALUE_PAIR))) == NULL) {
45 librad_log("out of memory");
48 memset(vp, 0, sizeof(VALUE_PAIR));
50 vp->operator = T_OP_EQ;
52 if ((da = dict_attrbyvalue(attr)) != NULL)
53 strcpy(vp->name, da->name);
55 sprintf(vp->name, "Attr-%d", attr);
71 * Release the memory used by a list of attribute-value
72 * pairs, and sets the pair pointer to NULL.
74 void pairfree(VALUE_PAIR **pair_ptr)
76 VALUE_PAIR *next, *pair;
78 if (!pair_ptr) return;
81 while (pair != NULL) {
92 * Find the pair with the matching attribute
94 VALUE_PAIR * pairfind(VALUE_PAIR *first, int attr)
96 while(first && first->attribute != attr)
103 * Delete the pair(s) with the matching attribute
105 void pairdelete(VALUE_PAIR **first, int attr)
107 VALUE_PAIR *i, *next;
108 VALUE_PAIR **last = first;
110 for(i = *first; i; i = next) {
112 if (i->attribute == attr) {
122 * Add a pair at the end of a VALUE_PAIR list.
124 void pairadd(VALUE_PAIR **first, VALUE_PAIR *add)
128 if (*first == NULL) {
132 for(i = *first; i->next; i = i->next)
138 * Copy just a certain type of pairs.
140 VALUE_PAIR *paircopy2(VALUE_PAIR *vp, int attr)
142 VALUE_PAIR *first, *n, **last;
148 if (attr >= 0 && vp->attribute != attr) {
152 if ((n = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
153 librad_log("out of memory");
156 memcpy(n, vp, sizeof(VALUE_PAIR));
169 VALUE_PAIR *paircopy(VALUE_PAIR *vp)
171 return paircopy2(vp, -1);
176 * Move attributes from one list to the other
177 * if not already present.
179 void pairmove(VALUE_PAIR **to, VALUE_PAIR **from)
181 VALUE_PAIR **tailto, *i, *j, *next;
182 VALUE_PAIR *tailfrom = NULL;
184 int has_password = 0;
193 * First, see if there are any passwords here, and
194 * point "tailto" to the end of the "to" list.
197 for(i = *to; i; i = i->next) {
198 if (i->attribute == PW_PASSWORD ||
199 i->attribute == PW_CRYPT_PASSWORD)
205 * Loop over the "from" list.
207 for(i = *from; i; i = next) {
210 * If there was a password in the "to" list,
211 * do not move any other password from the
212 * "from" to the "to" list.
215 (i->attribute == PW_PASSWORD ||
216 i->attribute == PW_CRYPT_PASSWORD)) {
221 * If the attribute is already present in "to",
222 * do not move it from "from" to "to". We make
223 * an exception for "Hint" which can appear multiple
224 * times, and we never move "Fall-Through".
226 if (i->attribute == PW_FALL_THROUGH ||
227 (i->attribute != PW_HINT && i->attribute != PW_FRAMED_ROUTE)) {
229 found = pairfind(*to, i->attribute);
230 switch (i->operator) {
233 * If a similar attribute is found,
236 case T_OP_SUB: /* -= */
238 if (!i->strvalue[0] ||
239 (strcmp((char *)found->strvalue,
240 (char *)i->strvalue) == 0)){
241 pairdelete(to, found->attribute);
244 * 'tailto' may have been
248 for(j = *to; j; j = j->next) {
257 /* really HAVE_REGEX_H */
260 * Attr-Name =~ "s/find/replace/"
262 * Very bad code. Barely working,
268 (i->strvalue[0] == 's')) {
276 q = strchr(p + 1, *p);
277 if (!q || (q[strlen(q) - 1] != *p)) {
281 str = strdup(i->strvalue + 2);
284 q[strlen(q) - 1] = '\0';
286 regcomp(®, str, 0);
287 if (regexec(®, found->strvalue,
289 fprintf(stderr, "\"%s\" will have %d to %d replaced with %s\n",
290 found->strvalue, match[0].rm_so,
297 tailfrom = i; /* don't copy it over */
301 case T_OP_EQ: /* = */
304 continue; /* with the loop */
309 * If a similar attribute is found,
310 * replace it with the new one. Otherwise,
311 * add the new one to the list.
313 case T_OP_SET: /* := */
315 pairdelete(to, found->attribute);
317 * 'tailto' may have been
321 for(j = *to; j; j = j->next) {
329 * Add the new element to the list, even
330 * if similar ones already exist.
333 case T_OP_ADD: /* += */
339 tailfrom->next = next;
344 * If ALL of the 'to' attributes have been deleted,
345 * then ensure that the 'tail' is updated to point
360 * Move one kind of attributes from one list to the other
362 void pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, int attr)
364 VALUE_PAIR *to_tail, *i, *next;
365 VALUE_PAIR *iprev = NULL;
368 * Find the last pair in the "to" list and put it in "to_tail".
372 for(i = *to; i; i = i->next)
377 for(i = *from; i; i = next) {
382 * If the attribute to move is NOT a VSA, then it
383 * ignores any attributes which do not match exactly.
385 if ((attr != PW_VENDOR_SPECIFIC) &&
386 (i->attribute != attr)) {
392 * If the attribute to move IS a VSA, then it ignores
393 * any non-VSA attribute.
395 if ((attr == PW_VENDOR_SPECIFIC) &&
396 (VENDOR(i->attribute) == 0)) {
402 * Remove the attribute from the "from" list.
410 * Add the attribute to the "to" list.
423 * Sort of strtok/strsep function.
425 static char *mystrtok(char **ptr, const char *sep)
431 while (**ptr && strchr(sep, **ptr))
436 while (**ptr && strchr(sep, **ptr) == NULL)
444 * Turn printable string into time_t
445 * Returns -1 on error, 0 on OK.
447 static int gettime(const char *valstr, time_t *lvalue)
454 char *year, *month, *day;
457 tm = localtime_r(&t, &s_tm);
459 strNcpy(buf, valstr, sizeof(buf));
460 for (p = buf; *p; p++)
461 if (isupper(*p)) *p = tolower(*p);
464 day = mystrtok(&p, " \t");
465 month = mystrtok(&p, " \t");
466 year = mystrtok(&p, " \t");
467 if (!year || !month || !day) return -1;
470 for (i = 0; i < 12; i++) {
471 if (strncasecmp(months[i], month, 3) == 0) {
477 /* month not found? */
478 if (tm->tm_mon == 12) return -1;
480 tm->tm_mday = atoi(day);
481 if ((tm->tm_mday < 1) || (tm->tm_mday > 31)) {
485 tm->tm_year = atoi(year);
486 if (tm->tm_year >= 1900) tm->tm_year -= 1900;
488 *lvalue = mktime(tm);
493 * Create a VALUE_PAIR from an ASCII attribute and value.
495 VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
507 * Check for tags in 'Attribute:Tag' format.
512 ts = strrchr( attribute, ':' );
514 /* Colon found with something behind it */
515 if (ts[1] == '*' && ts[2] == 0) {
516 /* Wildcard tag for check items */
519 } else if ((ts[1] >= 0) && (ts[1] <= '9')) {
520 /* It's not a wild card tag */
521 tag = strtol(ts + 1, &tc, 0);
522 if (tc && !*tc && TAG_VALID_ZERO(tag))
526 librad_log("Invalid tag for attribute %s", attribute);
532 if ((da = dict_attrbyname(attribute)) == NULL) {
533 librad_log("Unknown attribute %s", attribute);
537 if ((vp = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) {
538 librad_log("out of memory");
542 memset(vp, 0, sizeof(VALUE_PAIR));
543 vp->attribute = da->attr;
545 vp->operator = (operator == 0) ? T_OP_EQ : operator;
546 strcpy(vp->name, da->name);
547 vp->flags = da->flags;
550 /* Check for a tag in the 'Merit' format of:
551 * :Tag:Value. Print an error if we already found
552 * a tag in the Attribute.
555 if (*value == ':' && da->flags.has_tag) {
556 /* If we already found a tag, this is invalid */
559 librad_log("Duplicate tag %s for attribute %s",
561 DEBUG("Duplicate tag %s for attribute %s\n",
566 /* Colon found and attribute allows a tag */
567 if (value[1] == '*' && value[2] == ':') {
568 /* Wildcard tag for check items */
573 tag = strtol(value + 1, &tc, 0);
574 if (tc && *tc==':' && TAG_VALID_ZERO(tag))
586 * Even for integers, dates and ip addresses we
587 * keep the original string in vp->strvalue.
589 strNcpy((char *)vp->strvalue, value, MAX_STRING_LEN);
592 * For =* and !* operators, the value is irrelevant
596 if( vp->operator == T_OP_CMP_TRUE
597 || vp->operator == T_OP_CMP_FALSE ) {
603 vp->length = strlen(value);
604 if (vp->length >= MAX_STRING_LEN) {
605 vp->length = MAX_STRING_LEN - 1;
610 * FIXME: complain if hostname
611 * cannot be resolved, or resolve later!
613 if ((p = strrchr(value, '+')) != NULL && !p[1]) {
614 cs = s = strdup(value);
617 vp->flags.addport = 1;
622 vp->lvalue = librad_dodns ? ip_getaddr(cs) :
627 case PW_TYPE_INTEGER:
629 * If it starts with a digit, it must
630 * be a number (or a range).
632 * Note that ALL integers are unsigned!
634 if (isdigit(*value)) {
635 vp->lvalue = atoi(value);
639 * Look for the named value for the given
642 else if ((dval = dict_valbyname(da->attr, value)) == NULL) {
644 librad_log("Unknown value %s for attribute %s",
649 vp->lvalue = dval->value;
655 if (gettime(value, (time_t *)&vp->lvalue) < 0) {
657 librad_log("failed to parse time string "
663 case PW_TYPE_ABINARY:
666 * Special case to convert filter to binary
668 if ( filterBinary( vp, value ) < 0 ) {
669 librad_log("failed to parse Ascend binary attribute: %s",
676 * If Ascend binary is NOT defined,
677 * then fall through to raw octets, so that
678 * the user can at least make them by hand...
681 /* raw octets: 0x01020304... */
684 if (strncasecmp(value, "0x", 2) == 0) {
688 while (*cp && vp->length < MAX_STRING_LEN) {
691 if (sscanf(cp, "%02x", &tmp) != 1) break;
697 } else { /* assume it's a raw string */
698 vp->length = strlen(value);
699 if (vp->length >= MAX_STRING_LEN) {
700 vp->length = MAX_STRING_LEN - 1;
707 librad_log("unknown attribute type %d", da->type);
714 * Read a valuepair from a buffer, and advance pointer.
715 * Sets *eol to T_EOL if end of line was encountered.
717 VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol)
728 token = gettoken(ptr, attr, sizeof(attr));
730 /* If it's a comment, then exit, as we haven't read a pair */
731 if (token == T_HASH) {
736 /* It's not a comment, so it MUST be an attribute */
737 if ((token == T_EOL) ||
739 librad_log("No token read where we expected an attribute name");
743 /* Now we should have an '=' here. */
744 token = gettoken(ptr, buf, sizeof(buf));
745 if (token < T_EQSTART || token > T_EQEND) {
746 librad_log("expecting '='");
750 /* Read value. Note that empty string values are allowed */
751 t = gettoken(ptr, value, sizeof(value));
753 librad_log("failed to get value");
758 * Peek at the next token. Must be T_EOL, T_COMMA, or T_HASH
761 t = gettoken(&p, buf, sizeof(buf));
762 if (t != T_EOL && t != T_COMMA && t != T_HASH) {
763 librad_log("Expected end of line or comma");
772 return pairmake(attr, value, token);
776 * Read one line of attribute/value pairs. This might contain
777 * multiple pairs seperated by comma's.
779 LRAD_TOKEN userparse(char *buffer, VALUE_PAIR **first_pair)
783 LRAD_TOKEN last_token = T_INVALID;
784 LRAD_TOKEN previous_token;
787 * We allow an empty line.
794 previous_token = last_token;
795 if ((vp = pairread(&p, &last_token)) == NULL) {
798 pairadd(first_pair, vp);
799 } while (*p && (last_token == T_COMMA));
802 * Don't tell the caller that there was a comment.
804 if (last_token == T_HASH) {
805 return previous_token;
809 * And return the last token which we read.