#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/libradius.h>
#include <ctype.h>
#ifdef HAVE_MALLOC_H
# include <malloc.h>
#endif
#ifdef HAVE_REGEX_H
# include <regex.h>
#endif
static const char *months[] = {
"jan", "feb", "mar", "apr", "may", "jun",
"jul", "aug", "sep", "oct", "nov", "dec" };
#define FR_VP_NAME_PAD (32)
#define FR_VP_NAME_LEN (24)
VALUE_PAIR *pairalloc(DICT_ATTR *da)
{
size_t name_len = 0;
VALUE_PAIR *vp;
if (!da) name_len = FR_VP_NAME_PAD;
vp = malloc(sizeof(*vp) + name_len);
if (!vp) return NULL;
memset(vp, 0, sizeof(*vp));
if (da) {
vp->attribute = da->attr;
vp->vendor = da->vendor;
vp->type = da->type;
vp->name = da->name;
vp->flags = da->flags;
} else {
vp->attribute = 0;
vp->vendor = 0;
vp->type = PW_TYPE_OCTETS;
vp->name = NULL;
memset(&vp->flags, 0, sizeof(vp->flags));
vp->flags.unknown_attr = 1;
}
switch (vp->type) {
case PW_TYPE_BYTE:
vp->length = 1;
break;
case PW_TYPE_SHORT:
vp->length = 2;
break;
case PW_TYPE_INTEGER:
case PW_TYPE_IPADDR:
case PW_TYPE_DATE:
case PW_TYPE_SIGNED:
vp->length = 4;
break;
case PW_TYPE_IFID:
vp->length = sizeof(vp->vp_ifid);
break;
case PW_TYPE_IPV6ADDR:
vp->length = sizeof(vp->vp_ipv6addr);
break;
case PW_TYPE_IPV6PREFIX:
vp->length = sizeof(vp->vp_ipv6prefix);
break;
case PW_TYPE_ETHERNET:
vp->length = sizeof(vp->vp_ether);
break;
case PW_TYPE_TLV:
vp->vp_tlv = NULL;
vp->length = 0;
break;
case PW_TYPE_COMBO_IP:
default:
vp->length = 0;
break;
}
return vp;
}
VALUE_PAIR *paircreate_raw(int attr, int type, VALUE_PAIR *vp)
{
char *p = (char *) (vp + 1);
if (!vp->flags.unknown_attr) {
pairfree(&vp);
return NULL;
}
vp->vendor = VENDOR(attr);
vp->attribute = attr;
vp->operator = T_OP_EQ;
vp->name = p;
vp->type = type;
vp->length = 0;
memset(&vp->flags, 0, sizeof(vp->flags));
vp->flags.unknown_attr = 1;
if (!vp_print_name(p, FR_VP_NAME_LEN, vp->attribute)) {
free(vp);
return NULL;
}
return vp;
}
VALUE_PAIR *paircreate(int attr, int type)
{
VALUE_PAIR *vp;
DICT_ATTR *da;
da = dict_attrbyvalue(attr);
if ((vp = pairalloc(da)) == NULL) {
fr_strerror_printf("out of memory");
return NULL;
}
vp->operator = T_OP_EQ;
if (!da) return paircreate_raw(attr, type, vp);
return vp;
}
void pairbasicfree(VALUE_PAIR *pair)
{
if (pair->type == PW_TYPE_TLV) free(pair->vp_tlv);
memset(pair, 0, sizeof(*pair));
free(pair);
}
void pairfree(VALUE_PAIR **pair_ptr)
{
VALUE_PAIR *next, *pair;
if (!pair_ptr) return;
pair = *pair_ptr;
while (pair != NULL) {
next = pair->next;
pairbasicfree(pair);
pair = next;
}
*pair_ptr = NULL;
}
VALUE_PAIR * pairfind(VALUE_PAIR *first, int attr)
{
while(first && first->attribute != attr)
first = first->next;
return first;
}
void pairdelete(VALUE_PAIR **first, int attr)
{
VALUE_PAIR *i, *next;
VALUE_PAIR **last = first;
for(i = *first; i; i = next) {
next = i->next;
if (i->attribute == attr) {
*last = next;
pairbasicfree(i);
} else {
last = &i->next;
}
}
}
void pairadd(VALUE_PAIR **first, VALUE_PAIR *add)
{
VALUE_PAIR *i;
if (!add) return;
if (*first == NULL) {
*first = add;
return;
}
for(i = *first; i->next; i = i->next)
;
i->next = add;
}
void pairreplace(VALUE_PAIR **first, VALUE_PAIR *replace)
{
VALUE_PAIR *i, *next;
VALUE_PAIR **prev = first;
if (*first == NULL) {
*first = replace;
return;
}
for(i = *first; i; i = next) {
next = i->next;
if (i->attribute == replace->attribute) {
*prev = replace;
replace->next = next;
pairbasicfree(i);
return;
}
prev = &i->next;
}
*prev = replace;
}
VALUE_PAIR *paircopyvp(const VALUE_PAIR *vp)
{
size_t name_len;
VALUE_PAIR *n;
if (!vp->flags.unknown_attr) {
name_len = 0;
} else {
name_len = FR_VP_NAME_PAD;
}
if ((n = malloc(sizeof(*n) + name_len)) == NULL) {
fr_strerror_printf("out of memory");
return NULL;
}
memcpy(n, vp, sizeof(*n) + name_len);
if (vp->flags.unknown_attr) n->name = (char *) (n + 1);
n->next = NULL;
if ((n->type == PW_TYPE_TLV) &&
(n->vp_tlv != NULL)) {
n->vp_tlv = malloc(n->length);
memcpy(n->vp_tlv, vp->vp_tlv, n->length);
}
return n;
}
VALUE_PAIR *paircopy2(VALUE_PAIR *vp, int attr)
{
VALUE_PAIR *first, *n, **last;
first = NULL;
last = &first;
while (vp) {
if (attr >= 0 && vp->attribute != attr) {
vp = vp->next;
continue;
}
n = paircopyvp(vp);
if (!n) return first;
*last = n;
last = &n->next;
vp = vp->next;
}
return first;
}
VALUE_PAIR *paircopy(VALUE_PAIR *vp)
{
return paircopy2(vp, -1);
}
void pairmove(VALUE_PAIR **to, VALUE_PAIR **from)
{
VALUE_PAIR **tailto, *i, *j, *next;
VALUE_PAIR *tailfrom = NULL;
VALUE_PAIR *found;
int has_password = 0;
tailto = to;
for(i = *to; i; i = i->next) {
if (i->attribute == PW_USER_PASSWORD ||
i->attribute == PW_CRYPT_PASSWORD)
has_password = 1;
tailto = &i->next;
}
for(i = *from; i; i = next) {
next = i->next;
if (has_password &&
(i->attribute == PW_USER_PASSWORD ||
i->attribute == PW_CRYPT_PASSWORD)) {
tailfrom = i;
continue;
}
switch (i->operator) {
case T_OP_NE:
case T_OP_GE:
case T_OP_GT:
case T_OP_LE:
case T_OP_LT:
case T_OP_CMP_TRUE:
case T_OP_CMP_FALSE:
case T_OP_CMP_EQ:
tailfrom = i;
continue;
default:
break;
}
if (i->attribute == PW_FALL_THROUGH ||
(i->attribute != PW_HINT && i->attribute != PW_FRAMED_ROUTE)) {
found = pairfind(*to, i->attribute);
switch (i->operator) {
case T_OP_SUB:
if (found) {
if (!i->vp_strvalue[0] ||
(strcmp((char *)found->vp_strvalue,
(char *)i->vp_strvalue) == 0)){
pairdelete(to, found->attribute);
tailto = to;
for(j = *to; j; j = j->next) {
tailto = &j->next;
}
}
}
tailfrom = i;
continue;
break;
#if 0
case T_OP_REG_EQ:
if (found &&
(i->vp_strvalue[0] == 's')) {
regex_t reg;
regmatch_t match[1];
char *str;
char *p, *q;
p = i->vp_strvalue + 1;
q = strchr(p + 1, *p);
if (!q || (q[strlen(q) - 1] != *p)) {
tailfrom = i;
continue;
}
str = strdup(i->vp_strvalue + 2);
q = strchr(str, *p);
*(q++) = '\0';
q[strlen(q) - 1] = '\0';
regcomp(®, str, 0);
if (regexec(®, found->vp_strvalue,
1, match, 0) == 0) {
fprintf(stderr, "\"%s\" will have %d to %d replaced with %s\n",
found->vp_strvalue, match[0].rm_so,
match[0].rm_eo, q);
}
regfree(®);
free(str);
}
tailfrom = i;
continue;
break;
#endif
case T_OP_EQ:
if (found) {
tailfrom = i;
continue;
}
break;
case T_OP_SET:
if (found) {
VALUE_PAIR *mynext = found->next;
memcpy(found, i, sizeof(*found));
found->next = mynext;
pairdelete(&found->next, found->attribute);
for(j = found; j; j = j->next) {
tailto = &j->next;
}
continue;
}
break;
default:
case T_OP_ADD:
break;
}
}
if (tailfrom)
tailfrom->next = next;
else
*from = next;
if (!*to) {
tailto = to;
}
*tailto = i;
if (i) {
i->next = NULL;
tailto = &i->next;
}
}
}
void pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, int attr)
{
VALUE_PAIR *to_tail, *i, *next;
VALUE_PAIR *iprev = NULL;
if (*to != NULL) {
to_tail = *to;
for(i = *to; i; i = i->next)
to_tail = i;
} else
to_tail = NULL;
for(i = *from; i; i = next) {
next = i->next;
if ((attr != PW_VENDOR_SPECIFIC) &&
(i->attribute != attr)) {
iprev = i;
continue;
}
if ((attr == PW_VENDOR_SPECIFIC) &&
(VENDOR(i->attribute) == 0)) {
iprev = i;
continue;
}
if (iprev)
iprev->next = next;
else
*from = next;
if (to_tail)
to_tail->next = i;
else
*to = i;
to_tail = i;
i->next = NULL;
}
}
static char *mystrtok(char **ptr, const char *sep)
{
char *res;
if (**ptr == 0)
return NULL;
while (**ptr && strchr(sep, **ptr))
(*ptr)++;
if (**ptr == 0)
return NULL;
res = *ptr;
while (**ptr && strchr(sep, **ptr) == NULL)
(*ptr)++;
if (**ptr != 0)
*(*ptr)++ = 0;
return res;
}
static int gettime(const char *valstr, time_t *date)
{
int i;
time_t t;
struct tm *tm, s_tm;
char buf[64];
char *p;
char *f[4];
char *tail = '\0';
*date = strtoul(valstr, &tail, 10);
if (*tail == '\0') {
return 0;
}
tm = &s_tm;
memset(tm, 0, sizeof(*tm));
tm->tm_isdst = -1;
strlcpy(buf, valstr, sizeof(buf));
p = buf;
f[0] = mystrtok(&p, " \t");
f[1] = mystrtok(&p, " \t");
f[2] = mystrtok(&p, " \t");
f[3] = mystrtok(&p, " \t");
if (!f[0] || !f[1] || !f[2]) return -1;
if (f[3]) {
for (i = 0; i < 3; i++) {
if (strchr(f[i], ':')) {
p = f[3];
f[3] = f[i];
f[i] = p;
break;
}
}
}
tm->tm_mon = 12;
for (i = 0; i < 3; i++) {
if (isalpha( (int) *f[i])) {
p = f[0];
f[0] = f[i];
f[i] = p;
for (i = 0; i < 12; i++) {
if (strncasecmp(months[i], f[0], 3) == 0) {
tm->tm_mon = i;
break;
}
}
}
}
if (tm->tm_mon == 12) return -1;
tm->tm_year = atoi(f[1]);
tm->tm_mday = atoi(f[2]);
if (tm->tm_year >= 1900) {
tm->tm_year -= 1900;
} else {
if (tm->tm_mday < 1900) return -1;
i = tm->tm_year;
tm->tm_year = tm->tm_mday - 1900;
tm->tm_mday = i;
}
if ((tm->tm_mday < 1) || (tm->tm_mday > 31)) {
return -1;
}
if (f[3]) {
f[0] = f[3];
f[1] = strchr(f[0], ':');
if (!f[1]) return -1;
*(f[1]++) = '\0';
f[2] = strchr(f[1], ':');
if (f[2]) {
*(f[2]++) = '\0';
tm->tm_sec = atoi(f[2]);
}
tm->tm_hour = atoi(f[0]);
tm->tm_min = atoi(f[1]);
}
t = mktime(tm);
if (t == (time_t) -1) return -1;
*date = t;
return 0;
}
static const char *hextab = "0123456789abcdef";
static uint32_t getint(const char *value, char **end)
{
if ((value[0] == '0') && (value[1] == 'x')) {
return strtoul(value, end, 16);
}
return strtoul(value, end, 10);
}
static int check_for_whitespace(const char *value)
{
while (*value) {
if (!isspace((int) *value)) return 0;
value++;
}
return 1;
}
VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value)
{
char *p, *s=0;
const char *cp, *cs;
int x;
size_t length;
DICT_VALUE *dval;
if (!value) return NULL;
if (vp->type != PW_TYPE_TLV) {
strlcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue));
vp->length = strlen(vp->vp_strvalue);
}
switch(vp->type) {
case PW_TYPE_STRING:
p = vp->vp_strvalue;
cp = value;
length = 0;
while (*cp && (length < (sizeof(vp->vp_strvalue) - 1))) {
char c = *cp++;
if (c == '\\') {
switch (*cp) {
case 'r':
c = '\r';
cp++;
break;
case 'n':
c = '\n';
cp++;
break;
case 't':
c = '\t';
cp++;
break;
case '"':
c = '"';
cp++;
break;
case '\'':
c = '\'';
cp++;
break;
case '\\':
c = '\\';
cp++;
break;
case '`':
c = '`';
cp++;
break;
case '\0':
c = '\\';
break;
default:
if ((cp[0] >= '0') &&
(cp[0] <= '9') &&
(cp[1] >= '0') &&
(cp[1] <= '9') &&
(cp[2] >= '0') &&
(cp[2] <= '9') &&
(sscanf(cp, "%3o", &x) == 1)) {
c = x;
cp += 3;
}
}
}
*p++ = c;
length++;
}
vp->vp_strvalue[length] = '\0';
vp->length = length;
break;
case PW_TYPE_IPADDR:
if ((vp->operator == T_OP_REG_EQ) ||
(vp->operator == T_OP_REG_NE)) {
break;
}
s = NULL;
if ((p = strrchr(value, '+')) != NULL && !p[1]) {
cs = s = strdup(value);
if (!s) return NULL;
p = strrchr(s, '+');
*p = 0;
vp->flags.addport = 1;
} else {
p = NULL;
cs = value;
}
{
fr_ipaddr_t ipaddr;
if (ip_hton(cs, AF_INET, &ipaddr) < 0) {
free(s);
fr_strerror_printf("Failed to find IP address for %s", cs);
return NULL;
}
vp->vp_ipaddr = ipaddr.ipaddr.ip4addr.s_addr;
}
free(s);
vp->length = 4;
break;
case PW_TYPE_BYTE:
vp->length = 1;
vp->vp_integer = getint(value, &p);
if (!*p) {
if (vp->vp_integer > 255) {
fr_strerror_printf("Byte value \"%s\" is larger than 255", value);
return NULL;
}
break;
}
if (check_for_whitespace(p)) break;
goto check_for_value;
case PW_TYPE_SHORT:
vp->vp_integer = getint(value, &p);
vp->length = 2;
if (!*p) {
if (vp->vp_integer > 65535) {
fr_strerror_printf("Byte value \"%s\" is larger than 65535", value);
return NULL;
}
break;
}
if (check_for_whitespace(p)) break;
goto check_for_value;
case PW_TYPE_INTEGER:
vp->vp_integer = getint(value, &p);
vp->length = 4;
if (!*p) break;
if (check_for_whitespace(p)) break;
check_for_value:
if ((dval = dict_valbyname(vp->attribute, value)) == NULL) {
fr_strerror_printf("Unknown value %s for attribute %s",
value, vp->name);
return NULL;
}
vp->vp_integer = dval->value;
break;
case PW_TYPE_DATE:
{
time_t date;
if (gettime(value, &date) < 0) {
fr_strerror_printf("failed to parse time string "
"\"%s\"", value);
return NULL;
}
vp->vp_date = date;
}
vp->length = 4;
break;
case PW_TYPE_ABINARY:
#ifdef ASCEND_BINARY
if (strncasecmp(value, "0x", 2) == 0) {
vp->type = PW_TYPE_OCTETS;
goto do_octets;
}
if (ascend_parse_filter(vp) < 0 ) {
fr_strerror_printf("failed to parse Ascend binary attribute: %s",
fr_strerror());
return NULL;
}
break;
do_octets:
#endif
case PW_TYPE_OCTETS:
if (strncasecmp(value, "0x", 2) == 0) {
uint8_t *us;
cp = value + 2;
us = vp->vp_octets;
vp->length = 0;
if ((strlen(cp) & 0x01) != 0) {
fr_strerror_printf("Hex string is not an even length string.");
return NULL;
}
while (*cp &&
(vp->length < MAX_STRING_LEN)) {
unsigned int tmp;
if (sscanf(cp, "%02x", &tmp) != 1) {
fr_strerror_printf("Non-hex characters at %c%c", cp[0], cp[1]);
return NULL;
}
cp += 2;
*(us++) = tmp;
vp->length++;
}
}
break;
case PW_TYPE_IFID:
if (ifid_aton(value, (void *) &vp->vp_ifid) == NULL) {
fr_strerror_printf("failed to parse interface-id "
"string \"%s\"", value);
return NULL;
}
vp->length = 8;
break;
case PW_TYPE_IPV6ADDR:
{
fr_ipaddr_t ipaddr;
if (ip_hton(value, AF_INET6, &ipaddr) < 0) {
char buffer[1024];
strlcpy(buffer, fr_strerror(), sizeof(buffer));
fr_strerror_printf("failed to parse IPv6 address "
"string \"%s\": %s", value, buffer);
return NULL;
}
vp->vp_ipv6addr = ipaddr.ipaddr.ip6addr;
vp->length = 16;
}
break;
case PW_TYPE_IPV6PREFIX:
p = strchr(value, '/');
if (!p || ((p - value) >= 256)) {
fr_strerror_printf("invalid IPv6 prefix "
"string \"%s\"", value);
return NULL;
} else {
unsigned int prefix;
char buffer[256], *eptr;
memcpy(buffer, value, p - value);
buffer[p - value] = '\0';
if (inet_pton(AF_INET6, buffer, vp->vp_octets + 2) <= 0) {
fr_strerror_printf("failed to parse IPv6 address "
"string \"%s\"", value);
return NULL;
}
prefix = strtoul(p + 1, &eptr, 10);
if ((prefix > 128) || *eptr) {
fr_strerror_printf("failed to parse IPv6 address "
"string \"%s\"", value);
return NULL;
}
vp->vp_octets[1] = prefix;
}
vp->vp_octets[0] = '\0';
vp->length = 16 + 2;
break;
case PW_TYPE_ETHERNET:
{
const char *c1, *c2;
length = 0;
cp = value;
while (*cp) {
if (cp[1] == ':') {
c1 = hextab;
c2 = memchr(hextab, tolower((int) cp[0]), 16);
cp += 2;
} else if ((cp[1] != '\0') &&
((cp[2] == ':') ||
(cp[2] == '\0'))) {
c1 = memchr(hextab, tolower((int) cp[0]), 16);
c2 = memchr(hextab, tolower((int) cp[1]), 16);
cp += 2;
if (*cp == ':') cp++;
} else {
c1 = c2 = NULL;
}
if (!c1 || !c2 || (length >= sizeof(vp->vp_ether))) {
fr_strerror_printf("failed to parse Ethernet address \"%s\"", value);
return NULL;
}
vp->vp_ether[length] = ((c1-hextab)<<4) + (c2-hextab);
length++;
}
}
vp->length = 6;
break;
case PW_TYPE_COMBO_IP:
if (inet_pton(AF_INET6, value, vp->vp_strvalue) > 0) {
vp->type = PW_TYPE_IPV6ADDR;
vp->length = 16;
vp->vp_strvalue[vp->length] = '\0';
} else {
fr_ipaddr_t ipaddr;
if (ip_hton(value, AF_INET, &ipaddr) < 0) {
fr_strerror_printf("Failed to find IPv4 address for %s", value);
return NULL;
}
vp->type = PW_TYPE_IPADDR;
vp->vp_ipaddr = ipaddr.ipaddr.ip4addr.s_addr;
vp->length = 4;
}
break;
case PW_TYPE_SIGNED:
vp->vp_signed = (int32_t) strtol(value, &p, 10);
vp->length = 4;
break;
case PW_TYPE_TLV:
if (strncasecmp(value, "0x", 2) != 0) {
fr_strerror_printf("Invalid TLV specification");
return NULL;
}
length = strlen(value + 2) / 2;
if (vp->length < length) {
free(vp->vp_tlv);
vp->vp_tlv = NULL;
}
vp->vp_tlv = malloc(length);
if (!vp->vp_tlv) {
fr_strerror_printf("No memory");
return NULL;
}
if (fr_hex2bin(value + 2, vp->vp_tlv,
length) != length) {
fr_strerror_printf("Invalid hex data in TLV");
return NULL;
}
vp->length = length;
break;
default:
fr_strerror_printf("unknown attribute type %d", vp->type);
return NULL;
}
return vp;
}
static VALUE_PAIR *pairmake_any(const char *attribute, const char *value,
int operator)
{
int attr, vendor;
size_t size;
const char *p = attribute;
char *q;
VALUE_PAIR *vp;
if (value && (strncasecmp(value, "0x", 2) != 0)) {
fr_strerror_printf("Invalid octet string \"%s\" for attribute name \"%s\"", value, attribute);
return NULL;
}
vendor = 0;
if (strncasecmp(p, "Attr-", 5) != 0) {
if (strncasecmp(p, "Vendor-", 7) == 0) {
vendor = (int) strtol(p + 7, &q, 10);
if ((vendor == 0) || (vendor > 65535)) {
fr_strerror_printf("Invalid vendor value in attribute name \"%s\"", attribute);
return NULL;
}
p = q;
} else {
char buffer[256];
q = strchr(p, '-');
if (!q) {
fr_strerror_printf("Invalid vendor name in attribute name \"%s\"", attribute);
return NULL;
}
if ((size_t) (q - p) >= sizeof(buffer)) {
fr_strerror_printf("Vendor name too long in attribute name \"%s\"", attribute);
return NULL;
}
memcpy(buffer, p, (q - p));
buffer[q - p] = '\0';
vendor = dict_vendorbyname(buffer);
if (!vendor) {
fr_strerror_printf("Unknown vendor name in attribute name \"%s\"", attribute);
return NULL;
}
p = q;
}
if (*p != '-') {
fr_strerror_printf("Invalid text following vendor definition in attribute name \"%s\"", attribute);
return NULL;
}
p++;
}
if (strncasecmp(p, "Attr-", 5) != 0) {
fr_strerror_printf("Invalid format in attribute name \"%s\"", attribute);
return NULL;
}
attr = strtol(p + 5, &q, 10);
if ((attr == 0) || *q) {
fr_strerror_printf("Invalid value in attribute name \"%s\"", attribute);
return NULL;
}
if (vendor) {
DICT_VENDOR *dv = dict_vendorbyvalue(vendor);
if (!dv) {
if (attr > 255) {
attr_error:
fr_strerror_printf("Invalid attribute number in attribute name \"%s\"", attribute);
return NULL;
}
} else switch (dv->type) {
case 1:
if (attr > 255) goto attr_error;
break;
case 2:
if (attr > 65535) goto attr_error;
break;
case 4:
if (attr > 65535) goto attr_error;
break;
default:
fr_strerror_printf("Internal sanity check failed");
return NULL;
}
}
attr |= vendor << 16;
if ((vp = paircreate(attr, PW_TYPE_OCTETS)) == NULL) {
fr_strerror_printf("out of memory");
return NULL;
}
vp->operator = (operator == 0) ? T_OP_EQ : operator;
if (!value) return vp;
size = strlen(value + 2);
switch (vp->type) {
default:
if (size == (vp->length * 2)) break;
vp->type = PW_TYPE_OCTETS;
case PW_TYPE_STRING:
case PW_TYPE_OCTETS:
case PW_TYPE_ABINARY:
vp->length = size >> 1;
break;
}
if (fr_hex2bin(value + 2, vp->vp_octets, size) != vp->length) {
fr_strerror_printf("Invalid hex string");
free(vp);
return NULL;
}
switch (vp->type) {
case PW_TYPE_DATE:
case PW_TYPE_IPADDR:
case PW_TYPE_INTEGER:
memcpy(&vp->lvalue, vp->vp_octets, sizeof(vp->lvalue));
vp->vp_strvalue[0] = '\0';
break;
default:
break;
}
return vp;
}
VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator)
{
DICT_ATTR *da;
VALUE_PAIR *vp;
char *tc, *ts;
signed char tag;
int found_tag;
char buffer[64];
const char *attrname = attribute;
found_tag = 0;
tag = 0;
ts = strrchr(attribute, ':');
if (ts && !ts[1]) {
fr_strerror_printf("Invalid tag for attribute %s", attribute);
return NULL;
}
if (ts && ts[1]) {
strlcpy(buffer, attribute, sizeof(buffer));
attrname = buffer;
ts = strrchr(attrname, ':');
if (ts[1] == '*' && ts[2] == 0) {
tag = TAG_ANY;
*ts = 0;
} else if ((ts[1] >= '0') && (ts[1] <= '9')) {
tag = strtol(ts + 1, &tc, 0);
if (tc && !*tc && TAG_VALID_ZERO(tag))
*ts = 0;
else tag = 0;
} else {
fr_strerror_printf("Invalid tag for attribute %s", attribute);
return NULL;
}
found_tag = 1;
}
if ((da = dict_attrbyname(attrname)) == NULL) {
return pairmake_any(attrname, value, operator);
}
if ((vp = pairalloc(da)) == NULL) {
fr_strerror_printf("out of memory");
return NULL;
}
vp->operator = (operator == 0) ? T_OP_EQ : operator;
if (value && (*value == ':' && da->flags.has_tag)) {
if(found_tag) {
fr_strerror_printf("Duplicate tag %s for attribute %s",
value, vp->name);
DEBUG("Duplicate tag %s for attribute %s\n",
value, vp->name);
pairbasicfree(vp);
return NULL;
}
if (value[1] == '*' && value[2] == ':') {
tag = TAG_ANY;
value += 3;
} else {
tag = strtol(value + 1, &tc, 0);
if (tc && *tc==':' && TAG_VALID_ZERO(tag))
value = tc + 1;
else tag = 0;
}
found_tag = 1;
}
if (found_tag) {
vp->flags.tag = tag;
}
switch (vp->operator) {
default:
break;
case T_OP_CMP_TRUE:
case T_OP_CMP_FALSE:
vp->vp_strvalue[0] = '\0';
vp->length = 0;
return vp;
break;
case T_OP_REG_EQ:
case T_OP_REG_NE:
if (!value) {
fr_strerror_printf("No regular expression found in %s",
vp->name);
pairbasicfree(vp);
return NULL;
}
strlcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue));
vp->length = strlen(vp->vp_strvalue);
return vp;
}
if (value && (pairparsevalue(vp, value) == NULL)) {
pairbasicfree(vp);
return NULL;
}
return vp;
}
static const int valid_attr_name[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
VALUE_PAIR *pairread(const char **ptr, FR_TOKEN *eol)
{
char buf[64];
char attr[64];
char value[1024], *q;
const char *p;
FR_TOKEN token, t, xlat;
VALUE_PAIR *vp;
size_t len;
*eol = T_OP_INVALID;
p = *ptr;
while ((*p == ' ') || (*p == '\t')) p++;
if (!*p) {
*eol = T_OP_INVALID;
fr_strerror_printf("No token read where we expected an attribute name");
return NULL;
}
if (*p == '#') {
*eol = T_HASH;
fr_strerror_printf("Read a comment instead of a token");
return NULL;
}
q = attr;
for (len = 0; len < sizeof(attr); len++) {
if (valid_attr_name[(int)*p]) {
*q++ = *p++;
continue;
}
break;
}
if (len == sizeof(attr)) {
*eol = T_OP_INVALID;
fr_strerror_printf("Attribute name is too long");
return NULL;
}
if ((len > 0) && (attr[len - 1] == ':')) {
p--;
len--;
}
attr[len] = '\0';
*ptr = p;
token = gettoken(ptr, buf, sizeof(buf));
if (token < T_EQSTART || token > T_EQEND) {
fr_strerror_printf("expecting operator");
return NULL;
}
xlat = gettoken(ptr, value, sizeof(value));
if (xlat == T_EOL) {
fr_strerror_printf("failed to get value");
return NULL;
}
p = *ptr;
t = gettoken(&p, buf, sizeof(buf));
if (t != T_EOL && t != T_COMMA && t != T_HASH) {
fr_strerror_printf("Expected end of line or comma");
return NULL;
}
*eol = t;
if (t == T_COMMA) {
*ptr = p;
}
vp = NULL;
switch (xlat) {
default:
vp = pairmake(attr, value, token);
break;
case T_DOUBLE_QUOTED_STRING:
p = strchr(value, '%');
if (p && (p[1] == '{')) {
if (strlen(value) >= sizeof(vp->vp_strvalue)) {
fr_strerror_printf("Value too long");
return NULL;
}
vp = pairmake(attr, NULL, token);
if (!vp) {
*eol = T_OP_INVALID;
return NULL;
}
strlcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue));
vp->flags.do_xlat = 1;
vp->length = 0;
} else {
vp = pairmake(attr, value, token);
if (!vp) {
*eol = T_OP_INVALID;
return NULL;
}
}
break;
case T_SINGLE_QUOTED_STRING:
vp = pairmake(attr, NULL, token);
if (!vp) {
*eol = T_OP_INVALID;
return NULL;
}
if ((vp->type == PW_TYPE_STRING) ||
(vp->type == PW_TYPE_OCTETS)) {
strlcpy(vp->vp_strvalue, value,
sizeof(vp->vp_strvalue));
vp->length = strlen(vp->vp_strvalue);
} else if (!pairparsevalue(vp, value)) {
pairfree(&vp);
*eol = T_OP_INVALID;
return NULL;
}
break;
case T_BACK_QUOTED_STRING:
if (strlen(value) >= sizeof(vp->vp_strvalue)) {
fr_strerror_printf("Value too long");
return NULL;
}
vp = pairmake(attr, NULL, token);
if (!vp) {
*eol = T_OP_INVALID;
return NULL;
}
vp->flags.do_xlat = 1;
strlcpy(vp->vp_strvalue, value, sizeof(vp->vp_strvalue));
vp->length = 0;
break;
}
if (!vp) {
*eol = T_OP_INVALID;
return NULL;
}
return vp;
}
FR_TOKEN userparse(const char *buffer, VALUE_PAIR **first_pair)
{
VALUE_PAIR *vp;
const char *p;
FR_TOKEN last_token = T_OP_INVALID;
FR_TOKEN previous_token;
if (buffer[0] == 0)
return T_EOL;
p = buffer;
do {
previous_token = last_token;
if ((vp = pairread(&p, &last_token)) == NULL) {
return last_token;
}
pairadd(first_pair, vp);
} while (*p && (last_token == T_COMMA));
if (last_token == T_HASH) {
return previous_token;
}
return last_token;
}
VALUE_PAIR *readvp2(FILE *fp, int *pfiledone, const char *errprefix)
{
char buf[8192];
FR_TOKEN last_token = T_EOL;
VALUE_PAIR *vp;
VALUE_PAIR *list;
int error = 0;
list = NULL;
while (!error && fgets(buf, sizeof(buf), fp) != NULL) {
if ((buf[0] == '\n') && (list)) {
return list;
}
if ((buf[0] == '\n') && (!list)) {
continue;
}
if (buf[0] == '#') continue;
vp = NULL;
last_token = userparse(buf, &vp);
if (!vp) {
if (last_token != T_EOL) {
fr_perror("%s", errprefix);
error = 1;
break;
}
break;
}
pairadd(&list, vp);
buf[0] = '\0';
}
if (error) pairfree(&list);
*pfiledone = 1;
return error ? NULL: list;
}
int paircmp(VALUE_PAIR *one, VALUE_PAIR *two)
{
int compare;
switch (one->operator) {
case T_OP_CMP_TRUE:
return (two != NULL);
case T_OP_CMP_FALSE:
return (two == NULL);
case T_OP_REG_EQ:
case T_OP_REG_NE:
#ifndef HAVE_REGEX_H
return -1;
#else
{
regex_t reg;
char buffer[MAX_STRING_LEN * 4 + 1];
compare = regcomp(®, one->vp_strvalue,
REG_EXTENDED);
if (compare != 0) {
regerror(compare, ®, buffer, sizeof(buffer));
fr_strerror_printf("Illegal regular expression in attribute: %s: %s",
one->name, buffer);
return -1;
}
vp_prints_value(buffer, sizeof(buffer), two, 0);
compare = regexec(®, buffer, 0, NULL, 0);
regfree(®);
if (one->operator == T_OP_REG_EQ) return (compare == 0);
return (compare != 0);
}
#endif
default:
break;
}
switch (one->type) {
case PW_TYPE_ABINARY:
case PW_TYPE_OCTETS:
{
size_t length;
if (one->length < two->length) {
length = one->length;
} else {
length = two->length;
}
if (length) {
compare = memcmp(two->vp_octets, one->vp_octets,
length);
if (compare != 0) break;
}
compare = two->length - one->length;
}
break;
case PW_TYPE_STRING:
compare = strcmp(two->vp_strvalue, one->vp_strvalue);
break;
case PW_TYPE_BYTE:
case PW_TYPE_SHORT:
case PW_TYPE_INTEGER:
case PW_TYPE_DATE:
compare = two->vp_integer - one->vp_integer;
break;
case PW_TYPE_IPADDR:
compare = ntohl(two->vp_ipaddr) - ntohl(one->vp_ipaddr);
break;
case PW_TYPE_IPV6ADDR:
compare = memcmp(&two->vp_ipv6addr, &one->vp_ipv6addr,
sizeof(two->vp_ipv6addr));
break;
case PW_TYPE_IPV6PREFIX:
compare = memcmp(&two->vp_ipv6prefix, &one->vp_ipv6prefix,
sizeof(two->vp_ipv6prefix));
break;
case PW_TYPE_IFID:
compare = memcmp(&two->vp_ifid, &one->vp_ifid,
sizeof(two->vp_ifid));
break;
default:
return 0;
}
switch (one->operator) {
case T_OP_CMP_EQ:
return (compare == 0);
case T_OP_NE:
return (compare != 0);
case T_OP_LT:
return (compare < 0);
case T_OP_GT:
return (compare > 0);
case T_OP_LE:
return (compare <= 0);
case T_OP_GE:
return (compare >= 0);
default:
return 0;
}
return 0;
}