X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Flib%2Fvaluepair.c;h=57fe9a2c0f00927e6bd7032735b1b67c38a3383f;hb=bca85674a83f94b9cfae0ebdc129bbaf645c8f7d;hp=7b3a3e8bc58af64271f2bb29ed279db953a89983;hpb=52b9428533c5bfced6dbea240a8c442b1f25965d;p=freeradius.git diff --git a/src/lib/valuepair.c b/src/lib/valuepair.c index 7b3a3e8..57fe9a2 100644 --- a/src/lib/valuepair.c +++ b/src/lib/valuepair.c @@ -23,13 +23,8 @@ #include RCSID("$Id$") -#include - -#include +#include -#include -#include -#include #include #ifdef HAVE_MALLOC_H @@ -40,52 +35,59 @@ RCSID("$Id$") # include #endif -#include -#include - static const char *months[] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; - /* - * Create a new valuepair. + * This padding is necessary only for attributes that are NOT + * in the dictionary, and then only because the rest of the + * code accesses vp->name directly, rather than through an + * accessor function. + * + * The name padding only has to large enough for: + * + * Vendor-65535-Attr-65535 + * + * i.e. 23 characters, plus a zero. We add another 8 bytes for + * padding, because the VALUE_PAIR structure may be un-aligned. + * + * The result is that for the normal case, the server uses a less + * memory (36 bytes * number of VALUE_PAIRs). */ -VALUE_PAIR *paircreate(int attr, int type) -{ - VALUE_PAIR *vp; - DICT_ATTR *da; +#define FR_VP_NAME_PAD (32) +#define FR_VP_NAME_LEN (24) - if ((vp = malloc(sizeof(VALUE_PAIR))) == NULL) { - librad_log("out of memory"); - return NULL; - } - memset(vp, 0, sizeof(VALUE_PAIR)); - vp->attribute = attr; - vp->operator = T_OP_EQ; - vp->type = type; +VALUE_PAIR *pairalloc(DICT_ATTR *da) +{ + size_t name_len = 0; + VALUE_PAIR *vp; /* - * Dictionary type over-rides what the caller says. + * Not in the dictionary: the name is allocated AFTER + * the VALUE_PAIR struct. */ - if ((da = dict_attrbyvalue(attr)) != NULL) { - strcpy(vp->name, da->name); + 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 if (VENDOR(attr) == 0) { - sprintf(vp->name, "Attr-%u", attr); } else { - DICT_VENDOR *v; - - v = dict_vendorbyvalue(VENDOR(attr)); - if (v) { - sprintf(vp->name, "%s-Attr-%u", - v->name, attr & 0xffff); - } else { - sprintf(vp->name, "Vendor-%u-Attr-%u", - VENDOR(attr), attr & 0xffff); - } + 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; @@ -98,6 +100,7 @@ VALUE_PAIR *paircreate(int attr, int type) case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: case PW_TYPE_DATE: + case PW_TYPE_SIGNED: vp->length = 4; break; @@ -113,6 +116,16 @@ VALUE_PAIR *paircreate(int attr, int type) 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; @@ -122,11 +135,64 @@ VALUE_PAIR *paircreate(int attr, int type) } /* + * Create a new valuepair. + */ +VALUE_PAIR *paircreate_raw(int attr, int vendor, int type, VALUE_PAIR *vp) +{ + char *p = (char *) (vp + 1); + + if (!vp->flags.unknown_attr) { + pairfree(&vp); + return NULL; + } + + vp->vendor = vendor; + 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, vp->vendor)) { + free(vp); + return NULL; + } + + return vp; +} + +/* + * Create a new valuepair. + */ +VALUE_PAIR *paircreate(int attr, int vendor, int type) +{ + VALUE_PAIR *vp; + DICT_ATTR *da; + + da = dict_attrbyvalue(attr, vendor); + if ((vp = pairalloc(da)) == NULL) { + fr_strerror_printf("out of memory"); + return NULL; + } + vp->operator = T_OP_EQ; + + /* + * It isn't in the dictionary: update the name. + */ + if (!da) return paircreate_raw(attr, vendor, type, vp); + + return vp; +} + +/* * release the memory used by a single attribute-value pair * just a wrapper around free() for now. */ void pairbasicfree(VALUE_PAIR *pair) { + if (pair->type == PW_TYPE_TLV) free(pair->vp_tlv); /* clear the memory here */ memset(pair, 0, sizeof(*pair)); free(pair); @@ -156,25 +222,30 @@ void pairfree(VALUE_PAIR **pair_ptr) /* * Find the pair with the matching attribute */ -VALUE_PAIR * pairfind(VALUE_PAIR *first, int attr) +VALUE_PAIR * pairfind(VALUE_PAIR *first, unsigned int attr, unsigned int vendor) { - while(first && first->attribute != attr) + while (first) { + if ((first->attribute == attr) && (first->vendor == vendor)) { + return first; + } first = first->next; - return first; + } + + return NULL; } /* * Delete the pair(s) with the matching attribute */ -void pairdelete(VALUE_PAIR **first, int attr) +void pairdelete(VALUE_PAIR **first, unsigned int attr, unsigned int vendor) { VALUE_PAIR *i, *next; VALUE_PAIR **last = first; for(i = *first; i; i = next) { next = i->next; - if (i->attribute == attr) { + if ((i->attribute == attr) && (i->vendor == vendor)) { *last = next; pairbasicfree(i); } else { @@ -226,7 +297,8 @@ void pairreplace(VALUE_PAIR **first, VALUE_PAIR *replace) * Found the first attribute, replace it, * and return. */ - if (i->attribute == replace->attribute) { + if ((i->attribute == replace->attribute) && + (i->vendor == replace->vendor)) { *prev = replace; /* @@ -250,10 +322,49 @@ void pairreplace(VALUE_PAIR **first, VALUE_PAIR *replace) *prev = replace; } + +/* + * Copy just one VP. + */ +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); + + /* + * Reset the name field to point to the NEW attribute, + * rather than to the OLD one. + */ + 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; +} + + /* * Copy just a certain type of pairs. */ -VALUE_PAIR *paircopy2(VALUE_PAIR *vp, int attr) +VALUE_PAIR *paircopy2(VALUE_PAIR *vp, unsigned int attr, unsigned int vendor) { VALUE_PAIR *first, *n, **last; @@ -261,16 +372,14 @@ VALUE_PAIR *paircopy2(VALUE_PAIR *vp, int attr) last = &first; while (vp) { - if (attr >= 0 && vp->attribute != attr) { + if ((attr > 0) && + !((vp->attribute == attr) && (vp->vendor == vendor))) { vp = vp->next; continue; } - if ((n = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) { - librad_log("out of memory"); - return first; - } - memcpy(n, vp, sizeof(VALUE_PAIR)); - n->next = NULL; + + n = paircopyvp(vp); + if (!n) return first; *last = n; last = &n->next; vp = vp->next; @@ -284,7 +393,7 @@ VALUE_PAIR *paircopy2(VALUE_PAIR *vp, int attr) */ VALUE_PAIR *paircopy(VALUE_PAIR *vp) { - return paircopy2(vp, -1); + return paircopy2(vp, 0, 0); } @@ -359,7 +468,7 @@ void pairmove(VALUE_PAIR **to, VALUE_PAIR **from) if (i->attribute == PW_FALL_THROUGH || (i->attribute != PW_HINT && i->attribute != PW_FRAMED_ROUTE)) { - found = pairfind(*to, i->attribute); + found = pairfind(*to, i->attribute, i->vendor); switch (i->operator) { /* @@ -371,7 +480,7 @@ void pairmove(VALUE_PAIR **to, VALUE_PAIR **from) if (!i->vp_strvalue[0] || (strcmp((char *)found->vp_strvalue, (char *)i->vp_strvalue) == 0)){ - pairdelete(to, found->attribute); + pairdelete(to, found->attribute, found->vendor); /* * 'tailto' may have been @@ -466,7 +575,7 @@ void pairmove(VALUE_PAIR **to, VALUE_PAIR **from) memcpy(found, i, sizeof(*found)); found->next = mynext; - pairdelete(&found->next, found->attribute); + pairdelete(&found->next, found->attribute, found->vendor); /* * 'tailto' may have been @@ -512,7 +621,7 @@ void pairmove(VALUE_PAIR **to, VALUE_PAIR **from) /* * Move one kind of attributes from one list to the other */ -void pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, int attr) +void pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, unsigned int attr, unsigned int vendor) { VALUE_PAIR *to_tail, *i, *next; VALUE_PAIR *iprev = NULL; @@ -530,27 +639,37 @@ void pairmove2(VALUE_PAIR **to, VALUE_PAIR **from, int attr) for(i = *from; i; i = next) { next = i->next; - /* - * If the attribute to move is NOT a VSA, then it - * ignores any attributes which do not match exactly. + * vendor=0, attr = PW_VENDOR_SPECIFIC means + * "match any vendor attribute". */ - if ((attr != PW_VENDOR_SPECIFIC) && - (i->attribute != attr)) { + if ((vendor == 0) && (attr == PW_VENDOR_SPECIFIC)) { + /* + * It's a VSA: move it over. + */ + if (i->vendor != 0) goto move; + + /* + * It's Vendor-Specific: move it over. + */ + if (i->attribute == attr) goto move; + + /* + * It's not a VSA: ignore it. + */ iprev = i; continue; } /* - * If the attribute to move IS a VSA, then it ignores - * any non-VSA attribute. + * If it isn't an exact match, ignore it. */ - if ((attr == PW_VENDOR_SPECIFIC) && - (VENDOR(i->attribute) == 0)) { + if (!((i->vendor == vendor) && (i->attribute == attr))) { iprev = i; continue; } + move: /* * Remove the attribute from the "from" list. */ @@ -597,7 +716,7 @@ static char *mystrtok(char **ptr, const char *sep) * Turn printable string into time_t * Returns -1 on error, 0 on OK. */ -static int gettime(const char *valstr, uint32_t *lvalue) +static int gettime(const char *valstr, time_t *date) { int i; time_t t; @@ -610,7 +729,7 @@ static int gettime(const char *valstr, uint32_t *lvalue) /* * Test for unix timestamp date */ - *lvalue = strtoul(valstr, &tail, 10); + *date = strtoul(valstr, &tail, 10); if (*tail == '\0') { return 0; } @@ -712,13 +831,11 @@ static int gettime(const char *valstr, uint32_t *lvalue) f[2] = strchr(f[1], ':'); /* find : separator */ if (f[2]) { *(f[2]++) = '\0'; /* nuke it, and point to SS */ - } else { - strcpy(f[2], "0"); /* assignment would discard const */ - } + tm->tm_sec = atoi(f[2]); + } /* else leave it as zero */ tm->tm_hour = atoi(f[0]); tm->tm_min = atoi(f[1]); - tm->tm_sec = atoi(f[2]); } /* @@ -727,11 +844,12 @@ static int gettime(const char *valstr, uint32_t *lvalue) t = mktime(tm); if (t == (time_t) -1) return -1; - *lvalue = t; + *date = t; return 0; } +static const char *hextab = "0123456789abcdef"; /* * Parse a string value into a given VALUE_PAIR @@ -741,24 +859,109 @@ static int gettime(const char *valstr, uint32_t *lvalue) * double-check the parsed value, to be sure it's legal for that * type (length, etc.) */ +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; + /* * Even for integers, dates and ip addresses we * keep the original string in vp->vp_strvalue. */ - strlcpy((char *)vp->vp_strvalue, value, sizeof(vp->vp_strvalue)); - vp->length = strlen(vp->vp_strvalue); + 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: /* - * Already handled above. + * Do escaping here */ + 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 = '\\'; /* no cp++ */ + 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; + } /* else just do '\\' */ + } + } + *p++ = c; + length++; + } + vp->vp_strvalue[length] = '\0'; + vp->length = length; break; case PW_TYPE_IPADDR: @@ -774,8 +977,10 @@ VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value) * FIXME: complain if hostname * cannot be resolved, or resolve later! */ + 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; @@ -783,106 +988,98 @@ VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value) p = NULL; cs = value; } - + { - lrad_ipaddr_t ipaddr; + fr_ipaddr_t ipaddr; if (ip_hton(cs, AF_INET, &ipaddr) < 0) { - librad_log("Failed to find IP address for %s", cs); + free(s); + fr_strerror_printf("Failed to find IP address for %s", cs); return NULL; } - vp->lvalue = ipaddr.ipaddr.ip4addr.s_addr; + vp->vp_ipaddr = ipaddr.ipaddr.ip4addr.s_addr; } - if (s) free(s); + free(s); vp->length = 4; break; case PW_TYPE_BYTE: + vp->length = 1; + /* * Note that ALL integers are unsigned! */ - vp->lvalue = (uint32_t) strtoul(value, &p, 10); + vp->vp_integer = getint(value, &p); if (!*p) { - if (vp->lvalue > 255) { - librad_log("Byte value \"%s\" is larger than 255", value); + if (vp->vp_integer > 255) { + fr_strerror_printf("Byte value \"%s\" is larger than 255", value); return NULL; } - vp->length = 1; break; } - - /* - * Look for the named value for the given - * attribute. - */ - if ((dval = dict_valbyname(vp->attribute, value)) == NULL) { - librad_log("Unknown value %s for attribute %s", - value, vp->name); - return NULL; - } - vp->lvalue = dval->value; - vp->length = 1; - break; + if (check_for_whitespace(p)) break; + goto check_for_value; case PW_TYPE_SHORT: /* * Note that ALL integers are unsigned! */ - vp->lvalue = (uint32_t) strtoul(value, &p, 10); + vp->vp_integer = getint(value, &p); + vp->length = 2; if (!*p) { - if (vp->lvalue > 65535) { - librad_log("Byte value \"%s\" is larger than 65535", value); + if (vp->vp_integer > 65535) { + fr_strerror_printf("Byte value \"%s\" is larger than 65535", value); return NULL; } - vp->length = 2; break; } - - /* - * Look for the named value for the given - * attribute. - */ - if ((dval = dict_valbyname(vp->attribute, value)) == NULL) { - librad_log("Unknown value %s for attribute %s", - value, vp->name); - return NULL; - } - vp->lvalue = dval->value; - vp->length = 2; - break; + if (check_for_whitespace(p)) break; + goto check_for_value; case PW_TYPE_INTEGER: /* * Note that ALL integers are unsigned! */ - vp->lvalue = (uint32_t) strtoul(value, &p, 10); - if (!*p) { - vp->length = 4; - break; - } + vp->vp_integer = getint(value, &p); + vp->length = 4; + if (!*p) break; + if (check_for_whitespace(p)) break; + check_for_value: /* * Look for the named value for the given * attribute. */ - if ((dval = dict_valbyname(vp->attribute, value)) == NULL) { - librad_log("Unknown value %s for attribute %s", + if ((dval = dict_valbyname(vp->attribute, vp->vendor, value)) == NULL) { + fr_strerror_printf("Unknown value %s for attribute %s", value, vp->name); return NULL; } - vp->lvalue = dval->value; - vp->length = 4; + vp->vp_integer = dval->value; break; case PW_TYPE_DATE: - if (gettime(value, &vp->lvalue) < 0) { - librad_log("failed to parse time string " - "\"%s\"", value); - return NULL; + { + /* + * time_t may be 64 bits, whule vp_date + * MUST be 32-bits. We need an + * intermediary variable to handle + * the conversions. + */ + 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) { @@ -891,8 +1088,8 @@ VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value) } if (ascend_parse_filter(vp) < 0 ) { - librad_log("failed to parse Ascend binary attribute: %s", - librad_errstr); + fr_strerror_printf("failed to parse Ascend binary attribute: %s", + fr_strerror()); return NULL; } break; @@ -918,17 +1115,17 @@ VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value) * die. */ if ((strlen(cp) & 0x01) != 0) { - librad_log("Hex string is not an even length string."); + 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) { - librad_log("Non-hex characters at %c%c", cp[0], cp[1]); + fr_strerror_printf("Non-hex characters at %c%c", cp[0], cp[1]); return NULL; } @@ -940,60 +1137,148 @@ VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value) break; case PW_TYPE_IFID: - if (ifid_aton(value, (unsigned char *) vp->vp_strvalue) == NULL) { - librad_log("failed to parse interface-id " + 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; - vp->vp_strvalue[vp->length] = '\0'; break; case PW_TYPE_IPV6ADDR: - if (inet_pton(AF_INET6, value, vp->vp_strvalue) <= 0) { - librad_log("failed to parse IPv6 address " - "string \"%s\"", value); - return NULL; + { + 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; /* length of IPv6 address */ } - vp->length = 16; /* length of IPv6 address */ - vp->vp_strvalue[vp->length] = '\0'; break; - /* - * Anything else. - */ + case PW_TYPE_IPV6PREFIX: p = strchr(value, '/'); if (!p || ((p - value) >= 256)) { - librad_log("invalid IPv6 prefix " + 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_strvalue + 2) <= 0) { - librad_log("failed to parse IPv6 address " + + 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) { - librad_log("failed to parse IPv6 address " + fr_strerror_printf("failed to parse IPv6 address " "string \"%s\"", value); return NULL; } - vp->vp_strvalue[1] = prefix; + vp->vp_octets[1] = prefix; } - vp->vp_strvalue[0] = '\0'; + 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; /* length of IPv6 address */ + 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: /* Damned code for 1 WiMAX attribute */ + vp->vp_signed = (int32_t) strtol(value, &p, 10); + vp->length = 4; + break; + + case PW_TYPE_TLV: /* don't use this! */ + 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; + + /* + * Anything else. + */ default: - librad_log("unknown attribute type %d", vp->type); + fr_strerror_printf("unknown attribute type %d", vp->type); return NULL; } @@ -1006,119 +1291,178 @@ VALUE_PAIR *pairparsevalue(VALUE_PAIR *vp, const char *value) * * Attr-%d * Vendor-%d-Attr-%d + * VendorName-Attr-%d */ static VALUE_PAIR *pairmake_any(const char *attribute, const char *value, int operator) { - int attr; - const char *p; + int attr, vendor; + size_t size; + const char *p = attribute; + char *q; VALUE_PAIR *vp; /* * Unknown attributes MUST be of type 'octets' */ if (value && (strncasecmp(value, "0x", 2) != 0)) { - goto error; + fr_strerror_printf("Invalid octet string \"%s\" for attribute name \"%s\"", value, attribute); + return NULL; } + vendor = 0; + /* - * Attr-%d + * Pull off vendor prefix first. */ - if (strncasecmp(attribute, "Attr-", 5) == 0) { - attr = atoi(attribute + 5); - p = attribute + 5; - p += strspn(p, "0123456789"); - if (*p != 0) goto error; + if (strncasecmp(p, "Attr-", 5) != 0) { + if (strncasecmp(p, "Vendor-", 7) == 0) { + vendor = (int) strtol(p + 7, &q, 10); + if ((vendor == 0) || (vendor > FR_MAX_VENDOR)) { + fr_strerror_printf("Invalid vendor value in attribute name \"%s\"", attribute); + return NULL; + } - /* - * Vendor-%d-Attr-%d - */ - } else if (strncasecmp(attribute, "Vendor-", 7) == 0) { - int vendor; + p = q; - vendor = atoi(attribute + 7); - if ((vendor == 0) || (vendor > 65535)) goto error; + } else { /* must be vendor name */ + char buffer[256]; - p = attribute + 7; - p += strspn(p, "0123456789"); + q = strchr(p, '-'); - /* - * Not Vendor-%d-Attr-%d - */ - if (strncasecmp(p, "-Attr-", 6) != 0) goto error; + if (!q) { + fr_strerror_printf("Invalid vendor name in attribute name \"%s\"", attribute); + return NULL; + } - p += 6; - attr = atoi(p); + if ((size_t) (q - p) >= sizeof(buffer)) { + fr_strerror_printf("Vendor name too long in attribute name \"%s\"", attribute); + return NULL; + } - p += strspn(p, "0123456789"); - if (*p != 0) goto error; + memcpy(buffer, p, (q - p)); + buffer[q - p] = '\0'; - if ((attr == 0) || (attr > 65535)) goto error; + vendor = dict_vendorbyname(buffer); + if (!vendor) { + fr_strerror_printf("Unknown vendor name in attribute name \"%s\"", attribute); + return NULL; + } - attr |= (vendor << 16); + p = q; + } - /* - * VendorName-Attr-%d - */ - } else if (((p = strchr(attribute, '-')) != NULL) && - (strncasecmp(p, "-Attr-", 6) == 0)) { - int vendor; - char buffer[256]; + if (*p != '-') { + fr_strerror_printf("Invalid text following vendor definition in attribute name \"%s\"", attribute); + return NULL; + } + p++; + } - if (((size_t) (p - attribute)) >= sizeof(buffer)) goto error; + /* + * Attr-%d + */ + if (strncasecmp(p, "Attr-", 5) != 0) { + fr_strerror_printf("Invalid format in attribute name \"%s\"", attribute); + return NULL; + } - memcpy(buffer, attribute, p - attribute); - buffer[p - attribute] = '\0'; + attr = strtol(p + 5, &q, 10); - vendor = dict_vendorbyname(buffer); - if (vendor == 0) goto error; + /* + * Invalid, or trailing text after number. + */ + if ((attr == 0) || *q) { + fr_strerror_printf("Invalid value in attribute name \"%s\"", attribute); + return NULL; + } - p += 6; - attr = atoi(p); + /* + * Double-check the size of attr. + */ + if (vendor) { + DICT_VENDOR *dv = dict_vendorbyvalue(vendor); - p += strspn(p, "0123456789"); - if (*p != 0) goto error; + if (!dv) { + if (attr > 255) { + attr_error: + fr_strerror_printf("Invalid attribute number in attribute name \"%s\"", attribute); + return NULL; + } - if ((attr == 0) || (attr > 65535)) goto error; + } else switch (dv->type) { + case 1: + if (attr > 255) goto attr_error; + break; - attr |= (vendor << 16); + case 2: + if (attr > 65535) goto attr_error; + break; - } else { /* very much unknown: die */ - error: - librad_log("Unknown attribute \"%s\"", attribute); - return NULL; + case 4: + break; + + default: + fr_strerror_printf("Internal sanity check failed"); + return NULL; + } } /* - * We've now parsed the attribute properly, and verified - * it to have value 'octets'. Let's create it. + * We've now parsed the attribute properly, Let's create + * it. This next stop also looks the attribute up in the + * dictionary, and creates the appropriate type for it. */ - if ((vp = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) { - librad_log("out of memory"); + if ((vp = paircreate(attr, vendor, PW_TYPE_OCTETS)) == NULL) { + fr_strerror_printf("out of memory"); return NULL; } - memset(vp, 0, sizeof(VALUE_PAIR)); - vp->type = PW_TYPE_OCTETS; + + vp->operator = (operator == 0) ? T_OP_EQ : operator; + if (!value) return vp; + + size = strlen(value + 2); /* - * It may not be valid hex characters. If not, die. + * We may be reading something like Attr-5. i.e. + * who-ever wrote the text didn't understand it, but we + * do. */ - if (pairparsevalue(vp, value) == NULL) { - pairfree(&vp); - return NULL; + switch (vp->type) { + default: + if (size == (vp->length * 2)) break; + vp->type = PW_TYPE_OCTETS; + /* FALL-THROUGH */ + + case PW_TYPE_STRING: + case PW_TYPE_OCTETS: + case PW_TYPE_ABINARY: + vp->length = size >> 1; + break; } - if (VENDOR(attr) == 0) { - sprintf(vp->name, "Attr-%u", attr); - } else { - sprintf(vp->name, "Vendor-%u-Attr-%u", - VENDOR(attr), attr & 0xffff); + if (fr_hex2bin(value + 2, vp->vp_octets, size) != vp->length) { + fr_strerror_printf("Invalid hex string"); + free(vp); + return NULL; } - vp->attribute = attr; - vp->operator = (operator == 0) ? T_OP_EQ : operator; - vp->next = NULL; - + /* + * Move contents around based on type. This is + * to work around the historical use of "lvalue". + */ + 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; } @@ -1133,10 +1477,8 @@ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator) char *tc, *ts; signed char tag; int found_tag; -#ifdef HAVE_REGEX_H - int res; - regex_t cre; -#endif + char buffer[64]; + const char *attrname = attribute; /* * Check for tags in 'Attribute:Tag' format. @@ -1146,11 +1488,15 @@ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator) ts = strrchr(attribute, ':'); if (ts && !ts[1]) { - librad_log("Invalid tag for attribute %s", attribute); + 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, ':'); + /* Colon found with something behind it */ if (ts[1] == '*' && ts[2] == 0) { /* Wildcard tag for check items */ @@ -1163,7 +1509,7 @@ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator) *ts = 0; else tag = 0; } else { - librad_log("Invalid tag for attribute %s", attribute); + fr_strerror_printf("Invalid tag for attribute %s", attribute); return NULL; } found_tag = 1; @@ -1173,22 +1519,15 @@ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator) * It's not found in the dictionary, so we use * another method to create the attribute. */ - if ((da = dict_attrbyname(attribute)) == NULL) { - return pairmake_any(attribute, value, operator); + if ((da = dict_attrbyname(attrname)) == NULL) { + return pairmake_any(attrname, value, operator); } - if ((vp = (VALUE_PAIR *)malloc(sizeof(VALUE_PAIR))) == NULL) { - librad_log("out of memory"); + if ((vp = pairalloc(da)) == NULL) { + fr_strerror_printf("out of memory"); return NULL; } - - memset(vp, 0, sizeof(VALUE_PAIR)); - vp->attribute = da->attr; - vp->type = da->type; vp->operator = (operator == 0) ? T_OP_EQ : operator; - strcpy(vp->name, da->name); - vp->flags = da->flags; - vp->next = NULL; /* Check for a tag in the 'Merit' format of: * :Tag:Value. Print an error if we already found @@ -1198,13 +1537,12 @@ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator) if (value && (*value == ':' && da->flags.has_tag)) { /* If we already found a tag, this is invalid */ if(found_tag) { - pairbasicfree(vp); - librad_log("Duplicate tag %s for attribute %s", + 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; - } /* Colon found and attribute allows a tag */ if (value[1] == '*' && value[2] == ':') { @@ -1247,36 +1585,21 @@ VALUE_PAIR *pairmake(const char *attribute, const char *value, int operator) */ case T_OP_REG_EQ: /* =~ */ case T_OP_REG_NE: /* !~ */ - if (vp->type == PW_TYPE_INTEGER) { - return vp; - } -#ifdef HAVE_REGEX_H - /* - * Regular expression match with no regular - * expression is wrong. - */ if (!value) { - pairfree(&vp); + 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); + /* + * If anything goes wrong, this is a run-time error, + * not a compile-time error. + */ + return vp; - res = regcomp(&cre, value, REG_EXTENDED|REG_NOSUB); - if (res != 0) { - char msg[128]; - - regerror(res, &cre, msg, sizeof(msg)); - librad_log("Illegal regular expression in attribute: %s: %s", - vp->name, msg); - pairbasicfree(vp); - return NULL; - } - regfree(&cre); -#else - librad_log("Regelar expressions not enabled in this build, error in attribute %s", - vp->name); - pairbasicfree(vp); - return NULL; -#endif } /* @@ -1322,13 +1645,13 @@ static const int valid_attr_name[256] = { * Read a valuepair from a buffer, and advance pointer. * Sets *eol to T_EOL if end of line was encountered. */ -VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol) +VALUE_PAIR *pairread(const char **ptr, FR_TOKEN *eol) { char buf[64]; char attr[64]; - char value[512]; - char *p, *q; - LRAD_TOKEN token, t, xlat; + char value[1024], *q; + const char *p; + FR_TOKEN token, t, xlat; VALUE_PAIR *vp; size_t len; @@ -1339,13 +1662,13 @@ VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol) if (!*p) { *eol = T_OP_INVALID; - librad_log("No token read where we expected an attribute name"); + fr_strerror_printf("No token read where we expected an attribute name"); return NULL; } if (*p == '#') { *eol = T_HASH; - librad_log("Read a comment instead of a token"); + fr_strerror_printf("Read a comment instead of a token"); return NULL; } @@ -1360,14 +1683,14 @@ VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol) if (len == sizeof(attr)) { *eol = T_OP_INVALID; - librad_log("Attribute name is too long"); + fr_strerror_printf("Attribute name is too long"); return NULL; } /* * We may have Foo-Bar:= stuff, so back up. */ - if (attr[len - 1] == ':') { + if ((len > 0) && (attr[len - 1] == ':')) { p--; len--; } @@ -1378,14 +1701,14 @@ VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol) /* Now we should have an operator here. */ token = gettoken(ptr, buf, sizeof(buf)); if (token < T_EQSTART || token > T_EQEND) { - librad_log("expecting operator"); + fr_strerror_printf("expecting operator"); return NULL; } /* Read value. Note that empty string values are allowed */ xlat = gettoken(ptr, value, sizeof(value)); if (xlat == T_EOL) { - librad_log("failed to get value"); + fr_strerror_printf("failed to get value"); return NULL; } @@ -1395,7 +1718,7 @@ VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol) p = *ptr; t = gettoken(&p, buf, sizeof(buf)); if (t != T_EOL && t != T_COMMA && t != T_HASH) { - librad_log("Expected end of line or comma"); + fr_strerror_printf("Expected end of line or comma"); return NULL; } @@ -1420,7 +1743,7 @@ VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol) p = strchr(value, '%'); if (p && (p[1] == '{')) { if (strlen(value) >= sizeof(vp->vp_strvalue)) { - librad_log("Value too long"); + fr_strerror_printf("Value too long"); return NULL; } vp = pairmake(attr, NULL, token); @@ -1433,17 +1756,51 @@ VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol) vp->flags.do_xlat = 1; vp->length = 0; } else { + /* + * Parse && escape it, as defined by the + * data type. + */ 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; + } + + /* + * String and octet types get copied verbatim. + */ + 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); + + /* + * Everything else gets parsed: it's + * DATA, not a string! + */ + } else if (!pairparsevalue(vp, value)) { + pairfree(&vp); + *eol = T_OP_INVALID; + return NULL; + } + break; /* * Mark the pair to be allocated later. */ case T_BACK_QUOTED_STRING: if (strlen(value) >= sizeof(vp->vp_strvalue)) { - librad_log("Value too long"); + fr_strerror_printf("Value too long"); return NULL; } @@ -1474,12 +1831,12 @@ VALUE_PAIR *pairread(char **ptr, LRAD_TOKEN *eol) * Read one line of attribute/value pairs. This might contain * multiple pairs seperated by comma's. */ -LRAD_TOKEN userparse(char *buffer, VALUE_PAIR **first_pair) +FR_TOKEN userparse(const char *buffer, VALUE_PAIR **first_pair) { VALUE_PAIR *vp; - char *p; - LRAD_TOKEN last_token = T_OP_INVALID; - LRAD_TOKEN previous_token; + const char *p; + FR_TOKEN last_token = T_OP_INVALID; + FR_TOKEN previous_token; /* * We allow an empty line. @@ -1517,7 +1874,7 @@ LRAD_TOKEN userparse(char *buffer, VALUE_PAIR **first_pair) VALUE_PAIR *readvp2(FILE *fp, int *pfiledone, const char *errprefix) { char buf[8192]; - LRAD_TOKEN last_token = T_EOL; + FR_TOKEN last_token = T_EOL; VALUE_PAIR *vp; VALUE_PAIR *list; int error = 0; @@ -1548,7 +1905,7 @@ VALUE_PAIR *readvp2(FILE *fp, int *pfiledone, const char *errprefix) last_token = userparse(buf, &vp); if (!vp) { if (last_token != T_EOL) { - librad_perror("%s", errprefix); + fr_perror("%s", errprefix); error = 1; break; } @@ -1578,6 +1935,8 @@ VALUE_PAIR *readvp2(FILE *fp, int *pfiledone, const char *errprefix) * e.g. "foo" != "bar" * * Returns true (comparison is true), or false (comparison is not true); + * + * FIXME: Ignores tags! */ int paircmp(VALUE_PAIR *one, VALUE_PAIR *two) { @@ -1602,12 +1961,12 @@ int paircmp(VALUE_PAIR *one, VALUE_PAIR *two) { 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)); - librad_log("Illegal regular expression in attribute: %s: %s", + fr_strerror_printf("Illegal regular expression in attribute: %s: %s", one->name, buffer); return -1; } @@ -1629,7 +1988,7 @@ int paircmp(VALUE_PAIR *one, VALUE_PAIR *two) default: /* we're OK */ break; } - + /* * After doing the previous check for special comparisons, * do the per-type comparison here. @@ -1639,19 +1998,17 @@ int paircmp(VALUE_PAIR *one, VALUE_PAIR *two) case PW_TYPE_OCTETS: { size_t length; - const uint8_t *p, *q; if (one->length < two->length) { length = one->length; } else { length = two->length; } - - p = two->vp_octets; - q = one->vp_octets; - while (length) { - compare = ((int) *p) - ((int) *q); - if (compare != 0) goto type_switch; + + if (length) { + compare = memcmp(two->vp_octets, one->vp_octets, + length); + if (compare != 0) break; } /* @@ -1665,20 +2022,14 @@ int paircmp(VALUE_PAIR *one, VALUE_PAIR *two) break; case PW_TYPE_STRING: - if (one->flags.caseless) { - compare = strcasecmp(two->vp_strvalue, - one->vp_strvalue); - } else { - compare = strcmp(two->vp_strvalue, - one->vp_strvalue); - } + 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->lvalue - one->lvalue; + compare = two->vp_integer - one->vp_integer; break; case PW_TYPE_IPADDR: @@ -1689,7 +2040,7 @@ int paircmp(VALUE_PAIR *one, VALUE_PAIR *two) 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)); @@ -1707,17 +2058,16 @@ int paircmp(VALUE_PAIR *one, VALUE_PAIR *two) /* * Now do the operator comparison. */ - type_switch: 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);