From: Arran Cudbard-Bell Date: Fri, 18 Nov 2016 22:57:05 +0000 (-0500) Subject: Add custom fprintf style function for talloc binary buffers and value boxes X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f3cb31517249ade636e7321a9bf2cdf1d9d99d76;p=thirdparty%2Ffreeradius-server.git Add custom fprintf style function for talloc binary buffers and value boxes --- diff --git a/src/include/libradius.h b/src/include/libradius.h index 6853c8f22f1..7a387059672 100644 --- a/src/include/libradius.h +++ b/src/include/libradius.h @@ -177,6 +177,7 @@ char const *fr_utf8_strchr(int *chr_len, char const *str, char const *chr); size_t fr_snprint(char *out, size_t outlen, char const *in, ssize_t inlen, char quote); size_t fr_snprint_len(char const *in, ssize_t inlen, char quote); char *fr_asprint(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote); +char *fr_vasprintf(TALLOC_CTX *ctx, char const *fmt, va_list ap); #define is_truncated(_ret, _max) ((_ret) >= (size_t)(_max)) #define truncate_len(_ret, _max) (((_ret) >= (size_t)(_max)) ? (((size_t)(_max)) - 1) : _ret) diff --git a/src/include/log.h b/src/include/log.h index ffa0c6c5cd1..46ed2bed13c 100644 --- a/src/include/log.h +++ b/src/include/log.h @@ -112,7 +112,7 @@ bool radlog_debug_enabled(log_type_t type, log_lvl_t lvl, REQUEST *request) CC_HINT(nonnull); void vradlog_request(log_type_t type, log_lvl_t lvl, REQUEST *request, char const *msg, va_list ap) - CC_HINT(format (printf, 4, 0)) CC_HINT(nonnull (3, 4)); + CC_HINT(nonnull (3, 4)); void radlog_request(log_type_t type, log_lvl_t lvl, REQUEST *request, char const *msg, ...) CC_HINT(format (printf, 4, 5)) CC_HINT(nonnull (3, 4)); diff --git a/src/include/pair.h b/src/include/pair.h index 70f68507d27..b7363f9eca1 100644 --- a/src/include/pair.h +++ b/src/include/pair.h @@ -72,13 +72,19 @@ struct value_box { uint8_t ether[6]; //!< Ethernet (MAC) address. bool boolean; //!< A truth value. - uint8_t byte; //!< 8bit unsigned integer. - uint16_t ushort; //!< 16bit unsigned integer. - uint32_t integer; //!< 32bit unsigned integer. - uint64_t integer64; //!< 64bit unsigned integer. - size_t size; //!< System specific file/memory size. - int32_t sinteger; //!< 32bit signed integer. + struct { + union { + uint8_t byte; //!< 8bit unsigned integer. + uint16_t ushort; //!< 16bit unsigned integer. + uint32_t integer; //!< 32bit unsigned integer. + uint64_t integer64; //!< 64bit unsigned integer. + size_t size; //!< System specific file/memory size. + + int32_t sinteger; //!< 32bit signed integer. + }; + fr_dict_attr_t const *enumv; //!< Enumeration values for integer type. + }; struct timeval timeval; //!< A time value with usec precision. double decimal; //!< Double precision float. diff --git a/src/lib/print.c b/src/lib/print.c index 20e3a4e1afe..5fb30a07615 100644 --- a/src/lib/print.c +++ b/src/lib/print.c @@ -397,3 +397,292 @@ char *fr_asprint(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote) return out; } + +DIAG_OFF(format-nonliteral) +/** Special version of printf which supports %b and %v + * + * @todo Do something sensible with 'n$', though it's probably not actually used + * anywhere in the server. + * + * - %b takes a talloced binary buffer and prints it as hex. + * The length of the buffer is determined with a cal + */ +char *fr_vasprintf(TALLOC_CTX *ctx, char const *fmt, va_list ap) +{ + char const *p = fmt, *end = p + strlen(fmt), *fmt_p = p, *fmt_q = p; + char *out = NULL, *out_tmp; + va_list ap_p, ap_q; + + out = talloc_strdup(ctx, ""); + va_copy(ap_p, ap); + va_copy(ap_q, ap_p); + + do { + + char *q; + char *custom; + char len[2] = { '\0', '\0' }; + long width = 0, group = 0, precision = 0, tmp; + + if ((*p != '%') || (*++p == '%')) { + fmt_q = p + 1; + continue; /* literal char */ + } + + /* + * Check for parameter field + */ + tmp = strtoul(p, &q, 10); + if ((q != p) && (*q == '$')) { + group = tmp; + p = q + 1; + } + + /* + * Check for flags + */ + do { + switch (*p) { + case '-': + continue; + + case '+': + continue; + + case ' ': + continue; + + case '0': + continue; + + case '#': + continue; + + default: + goto done_flags; + } + } while (++p < end); + done_flags: + + /* + * Check for width field + */ + if (*p == '*') { + width = va_arg(ap_q, int); + p++; + } else { + width = strtoul(p, &q, 10); + p = q; + } + + /* + * Check for precision field + */ + if (*p == '.') { + p++; + precision = strtoul(p, &q, 10); + p = q; + } + + /* + * Length modifiers + */ + switch (*p) { + case 'h': + case 'l': + len[0] = *p++; + if ((*p == 'h') || (*p == 'l')) len[1] = *p++; + break; + + case 'L': + case 'z': + case 'j': + case 't': + len[0] = *p++; + break; + } + + /* + * Types + */ + switch (*p) { + case 'i': /* int */ + case 'd': /* int */ + case 'u': /* unsigned int */ + case 'x': /* unsigned int */ + case 'X': /* unsigned int */ + case 'o': /* unsigned int */ + switch (len[0]) { + case 'h': + if (len[1] == 'h') { /* char (promoted to int) */ + (void) va_arg(ap_q, int); + } else { + (void) va_arg(ap_q, int); /* short (promoted to int) */ + } + break; + + case 'L': + if ((*p == 'i') || (*p == 'd')) { + if (len [1] == 'L') { + (void) va_arg(ap_q, long); /* long */ + } else { + (void) va_arg(ap_q, long long); /* long long */ + } + } else { + if (len [1] == 'L') { + (void) va_arg(ap_q, unsigned long); /* unsigned long */ + } else { + (void) va_arg(ap_q, unsigned long long);/* unsigned long long */ + } + } + break; + + case 'z': + (void) va_arg(ap_q, size_t); /* size_t */ + break; + + case 'j': + (void) va_arg(ap_q, intmax_t); /* intmax_t */ + break; + + case 't': + (void) va_arg(ap_q, ptrdiff_t); /* ptrdiff_t */ + break; + + case '\0': /* no length modifier */ + if ((*p == 'i') || (*p == 'd')) { + (void) va_arg(ap_q, int); /* int */ + } else { + (void) va_arg(ap_q, unsigned int); /* unsigned int */ + } + } + break; + + case 'f': /* double */ + case 'F': /* double */ + case 'e': /* double */ + case 'E': /* double */ + case 'g': /* double */ + case 'G': /* double */ + case 'a': /* double */ + case 'A': /* double */ + switch (len[0]) { + case 'L': + (void) va_arg(ap_q, long double); /* long double */ + break; + + case 'l': /* does nothing */ + default: /* no length modifier */ + (void) va_arg(ap_q, double); /* double */ + } + break; + + case 's': + (void) va_arg(ap_q, char *); /* char * */ + break; + + case 'c': + (void) va_arg(ap_q, int); /* char (promoted to int) */ + break; + + case 'p': + (void) va_arg(ap_q, void *); /* void * */ + break; + + case 'n': + (void) va_arg(ap_q, int *); /* int * */ + break; + + /* + * Custom types + */ + case 'v': + { + value_box_t const *value = va_arg(ap_q, value_box_t const *); + + /* + * Allocations that are not part of the output + * string need to occur in the NULL ctx so we don't fragment + * any pool associated with it. + */ + custom = value_box_asprint(NULL, value->type, value->datum.enumv, value, '"'); + if (!custom) { + talloc_free(out); + return NULL; + } + + do_splice: + /* + * Pass part of a format string to printf + */ + if (fmt_q != fmt_p) { + char *sub_fmt; + + sub_fmt = talloc_strndup(NULL, fmt_p, fmt_q - fmt_p); + out_tmp = talloc_vasprintf_append_buffer(out, sub_fmt, ap_p); + talloc_free(sub_fmt); + if (!out_tmp) { + oom: + fr_strerror_printf("Out of memory"); + talloc_free(out); + talloc_free(custom); + va_end(ap_p); + va_end(ap_q); + return NULL; + } + out = out_tmp; + + out_tmp = talloc_strdup_append_buffer(out, custom); + TALLOC_FREE(custom); + if (!out_tmp) goto oom; + out = out_tmp; + + va_end(ap_p); /* one time use only */ + va_copy(ap_p, ap_q); /* already advanced to the next argument */ + } + + fmt_p = p + 1; + } + break; + + case 'b': + { + uint8_t const *bin = va_arg(ap_q, uint8_t *); + + /* + * Only automagically figure out the length + * if it's not specified. + * + * This allows %b to be used with stack buffers, + * so long as the length is specified in the format string. + */ + if (precision == 0) precision = talloc_array_length(bin); + + custom = talloc_array(NULL, char, (precision * 2) + 1); + if (!custom) goto oom; + fr_bin2hex(custom, bin, precision); + + goto do_splice; + } + + default: + break; + } + fmt_q = p + 1; + } while (++p < end); + + /* + * Print out the rest of the format string. + */ + if (*fmt_p) { + out_tmp = talloc_vasprintf_append_buffer(out, fmt_p, ap_p); + if (!out_tmp) goto oom; + out = out_tmp; + } + + va_end(ap_p); + va_end(ap_q); + + return out; +} +DIAG_ON(format-nonliteral) diff --git a/src/main/log.c b/src/main/log.c index 8fc2ae36490..51a17af4860 100644 --- a/src/main/log.c +++ b/src/main/log.c @@ -822,7 +822,7 @@ print_msg: * running unit tests which generate errors under CI. */ va_copy(aq, ap); - msg_exp = talloc_vasprintf(request, msg, aq); + msg_exp = fr_vasprintf(request, msg, aq); va_end(aq); /*