fix printing of long hex values
[freeradius.git] / src / lib / value.c
index a318141..ca18327 100644 (file)
@@ -56,7 +56,7 @@ int value_data_cmp(PW_TYPE a_type, value_data_t const *a, size_t a_len,
        {
                size_t length;
 
-               if (a_len > b_len) {
+               if (a_len < b_len) {
                        length = a_len;
                } else {
                        length = b_len;
@@ -442,19 +442,18 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
        switch (*src_type) {
        case PW_TYPE_STRING:
        {
-               char            *p;
+               char            *p, *buff;
                char const      *q;
                int             x;
 
-               dst->strvalue = p = talloc_array(ctx, char, len + 1);
-               memcpy(p, src, len);
-               p[len] = '\0';
+               buff = p = talloc_bstrndup(ctx, src, len);
 
                /*
                 *      No de-quoting.  Just copy the string.
                 */
                if (!quote) {
                        ret = len;
+                       dst->strvalue = buff;
                        goto finish;
                }
 
@@ -466,7 +465,7 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
                if (quote == '\'') {
                        q = p;
 
-                       while (q < (dst->strvalue + len)) {
+                       while (q < (buff + len)) {
                                /*
                                 *      The quotation character is escaped.
                                 */
@@ -494,8 +493,10 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
                        }
 
                        *p = '\0';
-                       ret = p - dst->strvalue;
-                       dst->ptr = talloc_realloc(ctx, dst->ptr, char, ret + 1);
+                       ret = p - buff;
+
+                       /* Shrink the buffer to the correct size */
+                       dst->strvalue = talloc_realloc(ctx, buff, char, ret + 1);
                        goto finish;
                }
 
@@ -504,11 +505,12 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
                 *      escaping.
                 */
                q = p;
-               while (q < (dst->strvalue + len)) {
+               while (q < (buff + len)) {
                        char c = *q++;
 
-                       if ((c == '\\') && (q >= (dst->strvalue + len))) {
+                       if ((c == '\\') && (q >= (buff + len))) {
                                fr_strerror_printf("Invalid escape at end of string");
+                               talloc_free(buff);
                                return -1;
                        }
 
@@ -574,8 +576,8 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
                }
 
                *p = '\0';
-               ret = p - dst->strvalue;
-               dst->ptr = talloc_realloc(ctx, dst->ptr, char, ret + 1);
+               ret = p - buff;
+               dst->strvalue = talloc_realloc(ctx, buff, char, ret + 1);
        }
                goto finish;
 
@@ -584,6 +586,9 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
                return -1;
 
        /* raw octets: 0x01020304... */
+#ifndef WITH_ASCEND_BINARY
+       do_octets:
+#endif
        case PW_TYPE_OCTETS:
        {
                uint8_t *p;
@@ -598,14 +603,13 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
                        goto finish;
                }
 
-       do_octets:
                len -= 2;
 
                /*
                 *      Invalid.
                 */
                if ((len & 0x01) != 0) {
-                       fr_strerror_printf("Length of Hex String is not even, got %zu bytes", ret);
+                       fr_strerror_printf("Length of Hex String is not even, got %zu bytes", len);
                        return -1;
                }
 
@@ -623,12 +627,25 @@ ssize_t value_data_from_str(TALLOC_CTX *ctx, value_data_t *dst,
 
        case PW_TYPE_ABINARY:
 #ifdef WITH_ASCEND_BINARY
-               if ((len > 1) && (strncasecmp(src, "0x", 2) == 0)) goto do_octets;
+               if ((len > 1) && (strncasecmp(src, "0x", 2) == 0)) {
+                       ssize_t bin;
 
-               if (ascend_parse_filter(dst, src, len) < 0 ) {
-                       /* Allow ascend_parse_filter's strerror to bubble up */
-                       return -1;
+                       if (len > ((sizeof(dst->filter) + 1) * 2)) {
+                               fr_strerror_printf("Hex data is too large for ascend filter");
+                               return -1;
+                       }
+
+                       bin = fr_hex2bin((uint8_t *) &dst->filter, ret, src + 2, len - 2);
+                       if (bin < ret) {
+                               memset(((uint8_t *) &dst->filter) + bin, 0, ret - bin);
+                       }
+               } else {
+                       if (ascend_parse_filter(dst, src, len) < 0 ) {
+                               /* Allow ascend_parse_filter's strerror to bubble up */
+                               return -1;
+                       }
                }
+
                ret = sizeof(dst->filter);
                goto finish;
 #else
@@ -1074,6 +1091,10 @@ ssize_t value_data_cast(TALLOC_CTX *ctx, value_data_t *dst,
                        dst->integer64 = src->integer;
                        break;
 
+               case PW_TYPE_DATE:
+                       dst->integer64 = src->date;
+                       break;
+
                case PW_TYPE_OCTETS:
                        goto do_octets;
 
@@ -1127,6 +1148,45 @@ ssize_t value_data_cast(TALLOC_CTX *ctx, value_data_t *dst,
        }
 
        /*
+        *      We can cast integers less that < INT_MAX to signed
+        */
+       if (dst_type == PW_TYPE_SIGNED) {
+               switch (src_type) {
+               case PW_TYPE_BYTE:
+                       dst->sinteger = src->byte;
+                       break;
+
+               case PW_TYPE_SHORT:
+                       dst->sinteger = src->ushort;
+                       break;
+
+               case PW_TYPE_INTEGER:
+                       if (src->integer > INT_MAX) {
+                               fr_strerror_printf("Invalid cast: From integer to signed.  integer value %u is larger "
+                                                  "than max signed int and would overflow", src->integer);
+                               return -1;
+                       }
+                       dst->sinteger = (int)src->integer;
+                       break;
+
+               case PW_TYPE_INTEGER64:
+                       if (src->integer > INT_MAX) {
+                               fr_strerror_printf("Invalid cast: From integer64 to signed.  integer64 value %" PRIu64
+                                                  " is larger than max signed int and would overflow", src->integer64);
+                               return -1;
+                       }
+                       dst->sinteger = (int)src->integer64;
+                       break;
+
+               case PW_TYPE_OCTETS:
+                       goto do_octets;
+
+               default:
+                       goto invalid_cast;
+               }
+               goto fixed_length;
+       }
+       /*
         *      Conversions between IPv4 addresses, IPv6 addresses, IPv4 prefixes and IPv6 prefixes
         *
         *      For prefix to ipaddress conversions, we assume that the host portion has already
@@ -1457,7 +1517,7 @@ char *value_data_aprints(TALLOC_CTX *ctx,
 #ifdef WITH_ASCEND_BINARY
                p = talloc_array(ctx, char, 128);
                if (!p) return NULL;
-               print_abinary(p, 128, (uint8_t *) &data->filter, inlen, 0);
+               print_abinary(p, 128, (uint8_t const *) &data->filter, inlen, 0);
                break;
 #else
                  /* FALL THROUGH */
@@ -1470,6 +1530,7 @@ char *value_data_aprints(TALLOC_CTX *ctx,
                p[1] = 'x';
 
                fr_bin2hex(p + 2, data->octets, inlen);
+               p[2 + (inlen * 2)] = '\0';
                break;
 
        case PW_TYPE_DATE:
@@ -1570,6 +1631,7 @@ size_t value_data_prints(char *out, size_t outlen,
        DICT_VALUE      *v;
        char            buf[1024];      /* Interim buffer to use with poorly behaved printing functions */
        char const      *a = NULL;
+       char            *p = out;
        time_t          t;
        struct tm       s_tm;
        unsigned int    i;
@@ -1581,6 +1643,8 @@ size_t value_data_prints(char *out, size_t outlen,
 
        *out = '\0';
 
+       p = out;
+
        switch (type) {
        case PW_TYPE_STRING:
 
@@ -1590,22 +1654,23 @@ size_t value_data_prints(char *out, size_t outlen,
                if (quote) {
                        if (freespace < 3) return inlen + 2;
 
-                       *out++ = quote;
+                       *p++ = quote;
                        freespace--;
 
-                       len = fr_prints(out, freespace, data->strvalue, inlen, quote);
+                       len = fr_prints(p, freespace, data->strvalue, inlen, quote);
                        /* always terminate the quoted string with another quote */
                        if (len >= (freespace - 1)) {
+                               /* Use out not p as we're operating on the entire buffer */
                                out[outlen - 2] = (char) quote;
                                out[outlen - 1] = '\0';
                                return len + 2;
                        }
-                       out += len;
+                       p += len;
                        freespace -= len;
 
-                       *out++ = (char) quote;
+                       *p++ = (char) quote;
                        freespace--;
-                       *out = '\0';
+                       *p = '\0';
 
                        return len + 2;
                }
@@ -1663,7 +1728,7 @@ print_int:
 
        case PW_TYPE_ABINARY:
 #ifdef WITH_ASCEND_BINARY
-               print_abinary(buf, sizeof(buf), (uint8_t const *) data->filter, len, quote);
+               print_abinary(buf, sizeof(buf), (uint8_t const *) data->filter, inlen, quote);
                a = buf;
                len = strlen(buf);
                break;
@@ -1673,34 +1738,67 @@ print_int:
        case PW_TYPE_OCTETS:
        case PW_TYPE_TLV:
        {
-               size_t max;
+               size_t binlen;
+               size_t hexlen;
 
-               /* Return the number of bytes we would have written */
-               len = (inlen * 2) + 2;
-               if (freespace <= 1) {
-                       return len;
-               }
+               binlen = inlen;
+               hexlen = (binlen * 2) + 2; /* NOT accounting for trailing NUL */
 
-               *out++ = '0';
-               freespace--;
+               /*
+                *      If the buffer is too small, put something into
+                *      it, and return how much we should have written
+                *
+                *      0 + x + H + H + NUL = 5
+                */
+               if (freespace < 5) {
+                       switch (freespace) {
+                       case '4':
+                       case '3':
+                               out[0] = '0';
+                               out[1] = 'x';
+                               out[2] = '\0';
+                               return hexlen;
+
+                       case 2:
+                               *out = '0';
+                               out++;
+                               /* FALL-THROUGH */
+
+                       case 1:
+                               *out = '\0';
+                               break;
+
+                       case 0:
+                               break;
+                       }
 
-               if (freespace <= 1) {
-                       *out = '\0';
-                       return len;
+                       return hexlen;
                }
-               *out++ = 'x';
-               freespace--;
 
-               if (freespace <= 2) {
-                       *out = '\0';
-                       return len;
+               /*
+                *      The output buffer is at least 5 bytes, we haev
+                *      room for '0xHH' plus a trailing NUL byte.
+                */
+               out[0] = '0';
+               out[1] = 'x';
+
+               /*
+                *      Get maximum number of bytes we can encode
+                *      given freespace, ensuring we account for '0',
+                *      'x', and the trailing NUL in the buffer.
+                *
+                *      Note that we can't have "freespace = 0" after
+                *      this, as 'freespace' has to be at least 5.
+                */
+               freespace -= 3;
+               freespace /= 2;
+               if (binlen > freespace) {
+                       binlen = freespace;
                }
 
-               /* Get maximum number of bytes we can encode given freespace */
-               max = ((freespace % 2) ? freespace - 1 : freespace - 2) / 2;
-               fr_bin2hex(out, data->octets, ((size_t)inlen > max) ? max : (size_t)inlen);
+               fr_bin2hex(out + 2, data->octets, binlen);
+               return hexlen;
        }
-               return len;
 
        case PW_TYPE_IFID:
                a = ifid_ntoa(buf, sizeof(buf), data->ifid);
@@ -1723,7 +1821,7 @@ print_int:
 
                a = inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
                if (a) {
-                       char *p = buf;
+                       p = buf;
 
                        len = strlen(buf);
                        p += len;
@@ -1743,7 +1841,7 @@ print_int:
 
                a = inet_ntop(AF_INET, &addr, buf, sizeof(buf));
                if (a) {
-                       char *p = buf;
+                       p = buf;
 
                        len = strlen(buf);
                        p += len;