2 * print.c Routines to print stuff.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2000,2006 The FreeRADIUS server project
25 #include <freeradius-devel/libradius.h>
30 * Checks for utf-8, taken from:
32 * http://www.w3.org/International/questions/qa-forms-utf-8
34 * Note that we don't care about the length of the input string,
35 * because '\0' is an invalid UTF-8 character.
37 int fr_utf8_char(uint8_t const *str)
39 if (*str < 0x20) return 0;
41 if (*str <= 0x7e) return 1; /* 1 */
43 if (*str <= 0xc1) return 0;
45 if ((str[0] >= 0xc2) && /* 2 */
52 if ((str[0] == 0xe0) && /* 3 */
60 if ((str[0] >= 0xe1) && /* 4a */
69 if ((str[0] >= 0xee) && /* 4b */
78 if ((str[0] == 0xed) && /* 5 */
86 if ((str[0] == 0xf0) && /* 6 */
96 if ((str[0] >= 0xf1) && /* 6 */
108 if ((str[0] == 0xf4) && /* 7 */
119 * Invalid UTF-8 Character
124 /** Escape any non printable or non-UTF8 characters in the input string
126 * @param[in] in string to escape.
127 * @param[in] inlen length of string to escape (lets us deal with embedded NULLs)
128 * @param[out] out where to write the escaped string.
129 * @param[out] outlen the length of the buffer pointed to by out.
130 * @return the number of bytes written to the out buffer, or a number > outlen if truncation has occurred.
132 size_t fr_print_string(char const *in, size_t inlen, char *out, size_t outlen)
134 uint8_t const *p = (uint8_t const *) in;
137 size_t freespace = outlen;
139 /* Can't '\0' terminate */
140 if (freespace == 0) {
144 /* No input, so no output... */
151 /* Figure out the length of the input string */
152 if (inlen == 0) inlen = strlen(in);
154 /* Not enough space to hold one char */
156 /* And there's input data... */
167 * Hack: never print trailing zero.
168 * Some clients send pings with an off-by-one
169 * length (confused with strings in C).
171 if ((inlen == 1) && (*p == '\0')) {
202 if (freespace < 3) break; /* \ + <c> + \0 */
211 utf8 = fr_utf8_char(p);
213 if (freespace < 5) break; /* \ + <o><o><o> + \0 */
214 snprintf(out, freespace, "\\%03o", *p);
223 if (freespace < 2) goto finish; /* <c> + \0 */
227 } while (--utf8 > 0);
233 /* Indicate truncation occurred */
234 if (inlen > 0) return outlen + inlen;
236 return outlen - freespace;
239 /** Find the length of the buffer required to fully escape a string with fr_print_string
241 * Were assuming here that's it's cheaper to figure out the length and do one
242 * alloc than repeatedly expand the buffer when we find extra chars which need
245 * @param in string to calculate the escaped length for.
246 * @param inlen length of the input string, if 0 strlen will be used to check the length.
247 * @return the size of buffer required to hold the escaped string excluding the NULL byte.
249 size_t fr_print_string_len(char const *in, size_t inlen)
251 uint8_t const *p = (uint8_t const *) in;
265 * Hack: never print trailing zero. Some clients send pings
266 * with an off-by-one length (confused with strings in C).
268 if ((inlen == 1) && (*p == '\0')) {
288 utf8 = fr_utf8_char(p);
300 } while (--utf8 > 0);
306 /** Print the value of an attribute to a string
309 size_t vp_data_prints_value(char *out, size_t outlen,
310 DICT_ATTR const *da, value_data_t const *data, size_t data_len, int8_t quote)
313 char buf[1024]; /* Interim buffer to use with poorly behaved printing functions */
314 char const *a = NULL;
318 size_t len = 0, freespace = outlen;
321 if (outlen == 0) return data_len;
327 /* need to copy the escaped value, but quoted */
333 *out++ = (char) quote;
336 len = fr_print_string(data->strvalue, data_len, out, freespace);
337 /* always terminate the quoted string with another quote */
338 if (len >= (freespace - 1)) {
339 out[outlen - 2] = (char) quote;
340 out[outlen - 1] = '\0';
346 *out++ = (char) quote;
353 /* xlat.c - need to copy raw value verbatim */
354 else if (quote < 0) {
355 if (outlen > data_len) {
356 memcpy(out, data->strvalue, data_len + 1);
360 memcpy(out, data->strvalue, outlen);
361 out[outlen - 1] = '\0';
362 return data_len; /* not a typo */
365 return fr_print_string(data->strvalue, data_len, out, outlen);
367 case PW_TYPE_INTEGER:
370 /* Normal, non-tagged attribute */
371 if ((v = dict_valbyattr(da->attr, da->vendor, data->integer)) != NULL) {
375 /* should never be truncated */
376 len = snprintf(buf, sizeof(buf), "%u", data->integer);
381 case PW_TYPE_INTEGER64:
382 return snprintf(out, outlen, "%" PRIu64, data->integer64);
387 len = strftime(buf, sizeof(buf) - 1, "%%%b %e %Y %H:%M:%S %Z%%", localtime_r(&t, &s_tm));
388 buf[0] = (char) quote;
389 buf[len - 1] = (char) quote;
392 len = strftime(buf, sizeof(buf), "%b %e %Y %H:%M:%S %Z", localtime_r(&t, &s_tm));
397 case PW_TYPE_SIGNED: /* Damned code for 1 WiMAX attribute */
398 len = snprintf(buf, sizeof(buf), "%d", data->sinteger);
402 case PW_TYPE_IPV4_ADDR:
403 a = inet_ntop(AF_INET, &(data->ipaddr), buf, sizeof(buf));
407 case PW_TYPE_ABINARY:
408 #ifdef WITH_ASCEND_BINARY
409 print_abinary(buf, sizeof(buf), (uint8_t const *) data->filter, len, quote);
421 /* Return the number of bytes we would have written */
422 len = (data_len * 2) + 2;
423 if (freespace <= 1) {
430 if (freespace <= 1) {
437 if (freespace <= 2) {
442 /* Get maximum number of bytes we can encode given freespace */
443 max = ((freespace % 2) ? freespace - 1 : freespace - 2) / 2;
444 fr_bin2hex(out, data->octets, (data_len > max) ? max : data_len);
449 a = ifid_ntoa(buf, sizeof(buf), data->ifid);
453 case PW_TYPE_IPV6_ADDR:
454 a = inet_ntop(AF_INET6, &data->ipv6addr, buf, sizeof(buf));
458 case PW_TYPE_IPV6_PREFIX:
460 struct in6_addr addr;
465 memcpy(&addr, &(data->ipv6prefix[2]), sizeof(addr));
467 a = inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
473 len += snprintf(p, sizeof(buf) - len, "/%u", (unsigned int) data->ipv6prefix[1]);
478 case PW_TYPE_IPV4_PREFIX:
485 memcpy(&addr, &(data->ipv4prefix[2]), sizeof(addr));
487 a = inet_ntop(AF_INET, &addr, buf, sizeof(buf));
493 len += snprintf(p, sizeof(buf) - len, "/%u", (unsigned int) (data->ipv4prefix[1] & 0x3f));
498 case PW_TYPE_ETHERNET:
499 return snprintf(out, outlen, "%02x:%02x:%02x:%02x:%02x:%02x",
500 data->ether[0], data->ether[1],
501 data->ether[2], data->ether[3],
502 data->ether[4], data->ether[5]);
510 if (a) strlcpy(out, a, outlen);
512 return len; /* Return the number of bytes we would of written (for truncation detection) */
515 /** Print the value of an attribute to a string
517 * @param[out] out Where to write the string.
518 * @param[in] outlen Size of outlen (must be at least 3 bytes).
519 * @param[in] vp to print.
520 * @param[in] quote Char to add before and after printed value, if 0 no char will be added, if < 0 raw string will be
522 * @return the length of data written to out, or a value >= outlen on truncation.
524 size_t vp_prints_value(char *out, size_t outlen, VALUE_PAIR const *vp, int8_t quote)
528 return vp_data_prints_value(out, outlen, vp->da, &vp->data, vp->length, quote);
531 char *vp_aprint_type(TALLOC_CTX *ctx, PW_TYPE type)
534 case PW_TYPE_STRING :
535 return talloc_typed_strdup(ctx, "_");
537 case PW_TYPE_INTEGER64:
541 case PW_TYPE_INTEGER:
543 return talloc_typed_strdup(ctx, "0");
545 case PW_TYPE_IPV4_ADDR :
546 return talloc_typed_strdup(ctx, "?.?.?.?");
548 case PW_TYPE_IPV4_PREFIX:
549 return talloc_typed_strdup(ctx, "?.?.?.?/?");
551 case PW_TYPE_IPV6_ADDR:
552 return talloc_typed_strdup(ctx, "[:?:]");
554 case PW_TYPE_IPV6_PREFIX:
555 return talloc_typed_strdup(ctx, "[:?:]/?");
558 return talloc_typed_strdup(ctx, "??");
560 case PW_TYPE_ETHERNET:
561 return talloc_typed_strdup(ctx, "??:??:??:??:??:??:??:??");
563 #ifdef WITH_ASCEND_BINARY
564 case PW_TYPE_ABINARY:
565 return talloc_typed_strdup(ctx, "??");
572 return talloc_typed_strdup(ctx, "<UNKNOWN-TYPE>");
575 /** Prints attribute values escaped suitably for use as JSON values
577 * Returns < 0 if the buffer may be (or have been) too small to write the encoded
580 * @param out Where to write the string.
581 * @param outlen Lenth of output buffer.
582 * @param vp to print.
583 * @return the length of data written to out, or a value >= outlen on truncation.
585 size_t vp_prints_value_json(char *out, size_t outlen, VALUE_PAIR const *vp)
588 size_t len, freespace = outlen;
590 if (!vp->da->flags.has_tag) {
591 switch (vp->da->type) {
592 case PW_TYPE_INTEGER:
595 if (vp->da->flags.has_value) break;
597 return snprintf(out, freespace, "%u", vp->vp_integer);
600 return snprintf(out, freespace, "%d", vp->vp_signed);
607 /* Indicate truncation */
608 if (freespace < 2) return outlen + 1;
612 switch (vp->da->type) {
614 for (q = vp->vp_strvalue; q < vp->vp_strvalue + vp->length; q++) {
615 /* Indicate truncation */
616 if (freespace < 3) return outlen + 1;
622 } else if (*q == '\\') {
626 } else if (*q == '/') {
630 } else if (*q >= ' ') {
663 len = snprintf(out, freespace, "u%04X", *q);
664 if (is_truncated(len, freespace)) return (outlen - freespace) + len;
673 len = vp_prints_value(out, freespace, vp, 0);
674 if (is_truncated(len, freespace)) return (outlen - freespace) + len;
680 /* Indicate truncation */
681 if (freespace < 2) return outlen + 1;
684 *out = '\0'; // We don't increment out, because the nul byte should not be included in the length
686 return outlen - freespace;
690 * This is a hack, and has to be kept in sync with tokens.h
692 static char const *vp_tokens[] = {
693 "?", /* T_OP_INVALID */
723 extern int const fr_attr_max_tlv;
724 extern int const fr_attr_shift[];
725 extern int const fr_attr_mask[];
728 /** Print one attribute and value to a string
730 * Print a VALUE_PAIR in the format:
732 <attribute_name>[:tag] <op> <value>
736 * @param out Where to write the string.
737 * @param outlen Lenth of output buffer.
738 * @param vp to print.
739 * @return the length of data written to out, or a value >= outlen on truncation.
741 size_t vp_prints(char *out, size_t outlen, VALUE_PAIR const *vp)
743 char const *token = NULL;
744 size_t len, freespace = outlen;
749 if (!vp || !vp->da) return 0;
753 if ((vp->op > T_OP_INVALID) && (vp->op < T_TOKEN_LAST)) {
754 token = vp_tokens[vp->op];
756 token = "<INVALID-TOKEN>";
759 if (vp->da->flags.has_tag && (vp->tag != TAG_ANY)) {
760 len = snprintf(out, freespace, "%s:%d %s ", vp->da->name, vp->tag, token);
762 len = snprintf(out, freespace, "%s %s ", vp->da->name, token);
765 if (is_truncated(len, freespace)) return len;
769 len = vp_prints_value(out, freespace, vp, '\'');
770 if (is_truncated(len, freespace)) return (outlen - freespace) + len;
773 return (outlen - freespace);
776 /** Print one attribute and value to FP
778 * Complete string with '\\t' and '\\n' is written to buffer before printing to
779 * avoid issues when running with multiple threads.
781 * @param fp to output to.
782 * @param vp to print.
784 void vp_print(FILE *fp, VALUE_PAIR const *vp)
793 len = vp_prints(p, sizeof(buf) - 1, vp);
800 * Deal with truncation gracefully
802 if (((size_t) (p - buf)) >= (sizeof(buf) - 2)) {
803 p = buf + (sizeof(buf) - 2);
813 /** Print a list of attributes and values
815 * @param fp to output to.
816 * @param vp to print.
818 void vp_printlist(FILE *fp, VALUE_PAIR const *vp)
821 for (vp = fr_cursor_init(&cursor, &vp); vp; vp = fr_cursor_next(&cursor)) {
828 * vp_prints_value for talloc
830 char *vp_aprint_value(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
834 switch (vp->da->type) {
839 /* Gets us the size of the buffer we need to alloc */
840 len = fr_print_string_len(vp->vp_strvalue, vp->length);
841 p = talloc_array(ctx, char, len + 1); /* +1 for '\0' */
844 ret = fr_print_string(vp->vp_strvalue, vp->length, p, len + 1);
845 if (!fr_assert(ret == len)) {
854 case PW_TYPE_INTEGER:
858 dv = dict_valbyattr(vp->da->attr, vp->da->vendor,
861 p = talloc_typed_strdup(ctx, dv->name);
863 p = talloc_typed_asprintf(ctx, "%u", vp->vp_integer);
869 p = talloc_typed_asprintf(ctx, "%d", vp->vp_signed);
872 case PW_TYPE_INTEGER64:
873 p = talloc_typed_asprintf(ctx, "%" PRIu64 , vp->vp_integer64);
876 case PW_TYPE_ETHERNET:
877 p = talloc_typed_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x",
878 vp->vp_ether[0], vp->vp_ether[1],
879 vp->vp_ether[2], vp->vp_ether[3],
880 vp->vp_ether[4], vp->vp_ether[5]);
883 case PW_TYPE_ABINARY:
884 #ifdef WITH_ASCEND_BINARY
885 p = talloc_array(ctx, char, 128);
887 print_abinary(p, 128, (uint8_t *) &vp->vp_filter, vp->length, 0);
894 p = talloc_array(ctx, char, 1 + vp->length * 2);
896 fr_bin2hex(p, vp->vp_octets, vp->length);
906 p = talloc_array(ctx, char, 64);
907 strftime(p, 64, "%b %e %Y %H:%M:%S %Z",
908 localtime_r(&t, &s_tm));
913 * We need to use the proper inet_ntop functions for IP
914 * addresses, else the output might not match output of
915 * other functions, which makes testing difficult.
917 * An example is tunnelled ipv4 in ipv6 addresses.
919 case PW_TYPE_IPV4_ADDR:
920 case PW_TYPE_IPV4_PREFIX:
922 char buff[INET_ADDRSTRLEN + 4]; // + /prefix
925 vp_prints_value(buff, sizeof(buff), vp, 0);
927 p = talloc_typed_strdup(ctx, buff);
931 case PW_TYPE_IPV6_ADDR:
932 case PW_TYPE_IPV6_PREFIX:
934 char buff[INET6_ADDRSTRLEN + 4]; // + /prefix
937 vp_prints_value(buff, sizeof(buff), vp, 0);
939 p = talloc_typed_strdup(ctx, buff);
944 p = talloc_typed_asprintf(ctx, "%x:%x:%x:%x",
945 (vp->vp_ifid[0] << 8) | vp->vp_ifid[1],
946 (vp->vp_ifid[2] << 8) | vp->vp_ifid[3],
947 (vp->vp_ifid[4] << 8) | vp->vp_ifid[5],
948 (vp->vp_ifid[6] << 8) | vp->vp_ifid[7]);
959 /** Print one attribute and value to a string
961 * Print a VALUE_PAIR in the format:
963 <attribute_name>[:tag] <op> <value>
967 * @param ctx to allocate string in.
968 * @param vp to print.
969 * @return a talloced buffer with the attribute operator and value.
971 char *vp_aprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
973 char const *token = NULL;
976 if (!vp || !vp->da) return 0;
980 if ((vp->op > T_OP_INVALID) && (vp->op < T_TOKEN_LAST)) {
981 token = vp_tokens[vp->op];
983 token = "<INVALID-TOKEN>";
986 value = vp_aprint_value(ctx, vp);
987 pair = vp->da->flags.has_tag ?
988 talloc_asprintf(ctx, "%s:%d %s %s", vp->da->name, vp->tag, token, value) :
989 talloc_asprintf(ctx, "%s %s %s", vp->da->name, token, value);