aboutsummaryrefslogtreecommitdiff
path: root/common/print.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/print.c')
-rw-r--r--common/print.c1518
1 files changed, 1518 insertions, 0 deletions
diff --git a/common/print.c b/common/print.c
new file mode 100644
index 0000000..e8eac79
--- /dev/null
+++ b/common/print.c
@@ -0,0 +1,1518 @@
+/* print.c
+
+ Turn data structures into printable text. */
+
+/*
+ * Copyright (c) 2009-2011 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 2004-2007 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1995-2003 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+int db_time_format = DEFAULT_TIME_FORMAT;
+
+char *quotify_string (const char *s, const char *file, int line)
+{
+ unsigned len = 0;
+ const char *sp;
+ char *buf, *nsp;
+
+ for (sp = s; sp && *sp; sp++) {
+ if (*sp == ' ')
+ len++;
+ else if (!isascii ((int)*sp) || !isprint ((int)*sp))
+ len += 4;
+ else if (*sp == '"' || *sp == '\\')
+ len += 2;
+ else
+ len++;
+ }
+
+ buf = dmalloc (len + 1, file, line);
+ if (buf) {
+ nsp = buf;
+ for (sp = s; sp && *sp; sp++) {
+ if (*sp == ' ')
+ *nsp++ = ' ';
+ else if (!isascii ((int)*sp) || !isprint ((int)*sp)) {
+ sprintf (nsp, "\\%03o",
+ *(const unsigned char *)sp);
+ nsp += 4;
+ } else if (*sp == '"' || *sp == '\\') {
+ *nsp++ = '\\';
+ *nsp++ = *sp;
+ } else
+ *nsp++ = *sp;
+ }
+ *nsp++ = 0;
+ }
+ return buf;
+}
+
+char *quotify_buf (const unsigned char *s, unsigned len,
+ const char *file, int line)
+{
+ unsigned nulen = 0;
+ char *buf, *nsp;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (s [i] == ' ')
+ nulen++;
+ else if (!isascii (s [i]) || !isprint (s [i]))
+ nulen += 4;
+ else if (s [i] == '"' || s [i] == '\\')
+ nulen += 2;
+ else
+ nulen++;
+ }
+
+ buf = dmalloc (nulen + 1, MDL);
+ if (buf) {
+ nsp = buf;
+ for (i = 0; i < len; i++) {
+ if (s [i] == ' ')
+ *nsp++ = ' ';
+ else if (!isascii (s [i]) || !isprint (s [i])) {
+ sprintf (nsp, "\\%03o", s [i]);
+ nsp += 4;
+ } else if (s [i] == '"' || s [i] == '\\') {
+ *nsp++ = '\\';
+ *nsp++ = s [i];
+ } else
+ *nsp++ = s [i];
+ }
+ *nsp++ = 0;
+ }
+ return buf;
+}
+
+char *print_base64 (const unsigned char *buf, unsigned len,
+ const char *file, int line)
+{
+ char *s, *b;
+ unsigned bl;
+ int i;
+ unsigned val, extra;
+ static char to64 [] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ bl = ((len * 4 + 2) / 3) + 1;
+ b = dmalloc (bl + 1, file, line);
+ if (!b)
+ return (char *)0;
+
+ i = 0;
+ s = b;
+ while (i != len) {
+ val = buf [i++];
+ extra = val & 3;
+ val = val >> 2;
+ *s++ = to64 [val];
+ if (i == len) {
+ *s++ = to64 [extra << 4];
+ *s++ = '=';
+ break;
+ }
+ val = (extra << 8) + buf [i++];
+ extra = val & 15;
+ val = val >> 4;
+ *s++ = to64 [val];
+ if (i == len) {
+ *s++ = to64 [extra << 2];
+ *s++ = '=';
+ break;
+ }
+ val = (extra << 8) + buf [i++];
+ extra = val & 0x3f;
+ val = val >> 6;
+ *s++ = to64 [val];
+ *s++ = to64 [extra];
+ }
+ if (!len)
+ *s++ = '=';
+ *s++ = 0;
+ if (s > b + bl + 1)
+ abort ();
+ return b;
+}
+
+char *print_hw_addr (htype, hlen, data)
+ const int htype;
+ const int hlen;
+ const unsigned char *data;
+{
+ static char habuf [49];
+ char *s;
+ int i;
+
+ if (hlen <= 0)
+ habuf [0] = 0;
+ else {
+ s = habuf;
+ for (i = 0; i < hlen; i++) {
+ sprintf (s, "%02x", data [i]);
+ s += strlen (s);
+ *s++ = ':';
+ }
+ *--s = 0;
+ }
+ return habuf;
+}
+
+void print_lease (lease)
+ struct lease *lease;
+{
+ struct tm *t;
+ char tbuf [32];
+
+ log_debug (" Lease %s",
+ piaddr (lease -> ip_addr));
+
+ t = gmtime (&lease -> starts);
+ strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t);
+ log_debug (" start %s", tbuf);
+
+ t = gmtime (&lease -> ends);
+ strftime (tbuf, sizeof tbuf, "%Y/%m/%d %H:%M:%S", t);
+ log_debug (" end %s", tbuf);
+
+ if (lease -> hardware_addr.hlen)
+ log_debug (" hardware addr = %s",
+ print_hw_addr (lease -> hardware_addr.hbuf [0],
+ lease -> hardware_addr.hlen - 1,
+ &lease -> hardware_addr.hbuf [1]));
+ log_debug (" host %s ",
+ lease -> host ? lease -> host -> name : "<none>");
+}
+
+#if defined (DEBUG_PACKET)
+void dump_packet_option (struct option_cache *oc,
+ struct packet *packet,
+ struct lease *lease,
+ struct client_state *client,
+ struct option_state *in_options,
+ struct option_state *cfg_options,
+ struct binding_scope **scope,
+ struct universe *u, void *foo)
+{
+ const char *name, *dot;
+ struct data_string ds;
+ memset (&ds, 0, sizeof ds);
+
+ if (u != &dhcp_universe) {
+ name = u -> name;
+ dot = ".";
+ } else {
+ name = "";
+ dot = "";
+ }
+ if (evaluate_option_cache (&ds, packet, lease, client,
+ in_options, cfg_options, scope, oc, MDL)) {
+ log_debug (" option %s%s%s %s;\n",
+ name, dot, oc -> option -> name,
+ pretty_print_option (oc -> option,
+ ds.data, ds.len, 1, 1));
+ data_string_forget (&ds, MDL);
+ }
+}
+
+void dump_packet (tp)
+ struct packet *tp;
+{
+ struct dhcp_packet *tdp = tp -> raw;
+
+ log_debug ("packet length %d", tp -> packet_length);
+ log_debug ("op = %d htype = %d hlen = %d hops = %d",
+ tdp -> op, tdp -> htype, tdp -> hlen, tdp -> hops);
+ log_debug ("xid = %x secs = %ld flags = %x",
+ tdp -> xid, (unsigned long)tdp -> secs, tdp -> flags);
+ log_debug ("ciaddr = %s", inet_ntoa (tdp -> ciaddr));
+ log_debug ("yiaddr = %s", inet_ntoa (tdp -> yiaddr));
+ log_debug ("siaddr = %s", inet_ntoa (tdp -> siaddr));
+ log_debug ("giaddr = %s", inet_ntoa (tdp -> giaddr));
+ log_debug ("chaddr = %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
+ ((unsigned char *)(tdp -> chaddr)) [0],
+ ((unsigned char *)(tdp -> chaddr)) [1],
+ ((unsigned char *)(tdp -> chaddr)) [2],
+ ((unsigned char *)(tdp -> chaddr)) [3],
+ ((unsigned char *)(tdp -> chaddr)) [4],
+ ((unsigned char *)(tdp -> chaddr)) [5]);
+ log_debug ("filename = %s", tdp -> file);
+ log_debug ("server_name = %s", tdp -> sname);
+ if (tp -> options_valid) {
+ int i;
+
+ for (i = 0; i < tp -> options -> universe_count; i++) {
+ if (tp -> options -> universes [i]) {
+ option_space_foreach (tp, (struct lease *)0,
+ (struct client_state *)0,
+ (struct option_state *)0,
+ tp -> options,
+ &global_scope,
+ universes [i], 0,
+ dump_packet_option);
+ }
+ }
+ }
+ log_debug ("%s", "");
+}
+#endif
+
+void dump_raw (buf, len)
+ const unsigned char *buf;
+ unsigned len;
+{
+ int i;
+ char lbuf [80];
+ int lbix = 0;
+
+/*
+ 1 2 3 4 5 6 7
+01234567890123456789012345678901234567890123456789012345678901234567890123
+280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .................
+*/
+
+ memset(lbuf, ' ', 79);
+ lbuf [79] = 0;
+
+ for (i = 0; i < len; i++) {
+ if ((i & 15) == 0) {
+ if (lbix) {
+ lbuf[53]=' ';
+ lbuf[54]=' ';
+ lbuf[55]=' ';
+ lbuf[73]='\0';
+ log_info ("%s", lbuf);
+ }
+ memset(lbuf, ' ', 79);
+ lbuf [79] = 0;
+ sprintf (lbuf, "%03x:", i);
+ lbix = 4;
+ } else if ((i & 7) == 0)
+ lbuf [lbix++] = ' ';
+
+ if(isprint(buf[i])) {
+ lbuf[56+(i%16)]=buf[i];
+ } else {
+ lbuf[56+(i%16)]='.';
+ }
+
+ sprintf (&lbuf [lbix], " %02x", buf [i]);
+ lbix += 3;
+ lbuf[lbix]=' ';
+
+ }
+ lbuf[53]=' ';
+ lbuf[54]=' ';
+ lbuf[55]=' ';
+ lbuf[73]='\0';
+ log_info ("%s", lbuf);
+}
+
+void hash_dump (table)
+ struct hash_table *table;
+{
+ int i;
+ struct hash_bucket *bp;
+
+ if (!table)
+ return;
+
+ for (i = 0; i < table -> hash_count; i++) {
+ if (!table -> buckets [i])
+ continue;
+ log_info ("hash bucket %d:", i);
+ for (bp = table -> buckets [i]; bp; bp = bp -> next) {
+ if (bp -> len)
+ dump_raw (bp -> name, bp -> len);
+ else
+ log_info ("%s", (const char *)bp -> name);
+ }
+ }
+}
+
+/*
+ * print a string as hex. This only outputs
+ * colon separated hex list no matter what
+ * the input looks like. See print_hex
+ * for a function that prints either cshl
+ * or a string if all bytes are printible
+ * It only uses limit characters from buf
+ * and doesn't do anything if buf == NULL
+ *
+ * len - length of data
+ * data - input data
+ * limit - length of buf to use
+ * buf - output buffer
+ */
+void print_hex_only (len, data, limit, buf)
+ unsigned len;
+ const u_int8_t *data;
+ unsigned limit;
+ char *buf;
+{
+ unsigned i;
+
+ if ((buf == NULL) || (limit < 3))
+ return;
+
+ for (i = 0; (i < limit / 3) && (i < len); i++) {
+ sprintf(&buf[i*3], "%02x:", data[i]);
+ }
+ buf[(i * 3) - 1] = 0;
+ return;
+}
+
+/*
+ * print a string as either text if all the characters
+ * are printable or colon separated hex if they aren't
+ *
+ * len - length of data
+ * data - input data
+ * limit - length of buf to use
+ * buf - output buffer
+ */
+void print_hex_or_string (len, data, limit, buf)
+ unsigned len;
+ const u_int8_t *data;
+ unsigned limit;
+ char *buf;
+{
+ unsigned i;
+ if ((buf == NULL) || (limit < 3))
+ return;
+
+ for (i = 0; (i < (limit - 3)) && (i < len); i++) {
+ if (!isascii(data[i]) || !isprint(data[i])) {
+ print_hex_only(len, data, limit, buf);
+ return;
+ }
+ }
+
+ buf[0] = '"';
+ i = len;
+ if (i > (limit - 3))
+ i = limit - 3;
+ memcpy(&buf[1], data, i);
+ buf[i + 1] = '"';
+ buf[i + 2] = 0;
+ return;
+}
+
+/*
+ * print a string as either hex or text
+ * using static buffers to hold the output
+ *
+ * len - length of data
+ * data - input data
+ * limit - length of buf
+ * buf_num - the output buffer to use
+ */
+#define HBLEN 1024
+char *print_hex(len, data, limit, buf_num)
+ unsigned len;
+ const u_int8_t *data;
+ unsigned limit;
+ unsigned buf_num;
+{
+ static char hex_buf_1[HBLEN + 1];
+ static char hex_buf_2[HBLEN + 1];
+ static char hex_buf_3[HBLEN + 1];
+ char *hex_buf;
+
+ switch(buf_num) {
+ case 0:
+ hex_buf = hex_buf_1;
+ if (limit >= sizeof(hex_buf_1))
+ limit = sizeof(hex_buf_1);
+ break;
+ case 1:
+ hex_buf = hex_buf_2;
+ if (limit >= sizeof(hex_buf_2))
+ limit = sizeof(hex_buf_2);
+ break;
+ case 2:
+ hex_buf = hex_buf_3;
+ if (limit >= sizeof(hex_buf_3))
+ limit = sizeof(hex_buf_3);
+ break;
+ default:
+ return(NULL);
+ }
+
+ print_hex_or_string(len, data, limit, hex_buf);
+ return(hex_buf);
+}
+
+#define DQLEN 80
+
+char *print_dotted_quads (len, data)
+ unsigned len;
+ const u_int8_t *data;
+{
+ static char dq_buf [DQLEN + 1];
+ int i;
+ char *s, *last;
+
+ s = &dq_buf [0];
+ last = s;
+
+ i = 0;
+
+ /* %Audit% Loop bounds checks to 21 bytes. %2004.06.17,Safe%
+ * The sprintf can't exceed 18 bytes, and since the loop enforces
+ * 21 bytes of space per iteration at no time can we exit the
+ * loop without at least 3 bytes spare.
+ */
+ do {
+ sprintf (s, "%u.%u.%u.%u, ",
+ data [i], data [i + 1], data [i + 2], data [i + 3]);
+ s += strlen (s);
+ i += 4;
+ } while ((s - &dq_buf [0] > DQLEN - 21) &&
+ i + 3 < len);
+ if (i == len)
+ s [-2] = 0;
+ else
+ strcpy (s, "...");
+ return dq_buf;
+}
+
+char *print_dec_1 (val)
+ unsigned long val;
+{
+ static char vbuf [32];
+ sprintf (vbuf, "%lu", val);
+ return vbuf;
+}
+
+char *print_dec_2 (val)
+ unsigned long val;
+{
+ static char vbuf [32];
+ sprintf (vbuf, "%lu", val);
+ return vbuf;
+}
+
+static unsigned print_subexpression (struct expression *, char *, unsigned);
+
+static unsigned print_subexpression (expr, buf, len)
+ struct expression *expr;
+ char *buf;
+ unsigned len;
+{
+ unsigned rv, left;
+ const char *s;
+
+ switch (expr -> op) {
+ case expr_none:
+ if (len > 3) {
+ strcpy (buf, "nil");
+ return 3;
+ }
+ break;
+
+ case expr_match:
+ if (len > 7) {
+ strcpy (buf, "(match)");
+ return 7;
+ }
+ break;
+
+ case expr_check:
+ rv = 10 + strlen (expr -> data.check -> name);
+ if (len > rv) {
+ sprintf (buf, "(check %s)",
+ expr -> data.check -> name);
+ return rv;
+ }
+ break;
+
+ case expr_equal:
+ if (len > 6) {
+ rv = 4;
+ strcpy (buf, "(eq ");
+ rv += print_subexpression (expr -> data.equal [0],
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.equal [1],
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_not_equal:
+ if (len > 7) {
+ rv = 5;
+ strcpy (buf, "(neq ");
+ rv += print_subexpression (expr -> data.equal [0],
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.equal [1],
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_regex_match:
+ if (len > 10) {
+ rv = 4;
+ strcpy(buf, "(regex ");
+ rv += print_subexpression(expr->data.equal[0],
+ buf + rv, len - rv - 2);
+ buf[rv++] = ' ';
+ rv += print_subexpression(expr->data.equal[1],
+ buf + rv, len - rv - 1);
+ buf[rv++] = ')';
+ buf[rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_substring:
+ if (len > 11) {
+ rv = 8;
+ strcpy (buf, "(substr ");
+ rv += print_subexpression (expr -> data.substring.expr,
+ buf + rv, len - rv - 3);
+ buf [rv++] = ' ';
+ rv += print_subexpression
+ (expr -> data.substring.offset,
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.substring.len,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_suffix:
+ if (len > 10) {
+ rv = 8;
+ strcpy (buf, "(suffix ");
+ rv += print_subexpression (expr -> data.suffix.expr,
+ buf + rv, len - rv - 2);
+ if (len > rv)
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.suffix.len,
+ buf + rv, len - rv - 1);
+ if (len > rv)
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_lcase:
+ if (len > 9) {
+ rv = 7;
+ strcpy(buf, "(lcase ");
+ rv += print_subexpression(expr->data.lcase,
+ buf + rv, len - rv - 1);
+ buf[rv++] = ')';
+ buf[rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_ucase:
+ if (len > 9) {
+ rv = 7;
+ strcpy(buf, "(ucase ");
+ rv += print_subexpression(expr->data.ucase,
+ buf + rv, len - rv - 1);
+ buf[rv++] = ')';
+ buf[rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_concat:
+ if (len > 10) {
+ rv = 8;
+ strcpy (buf, "(concat ");
+ rv += print_subexpression (expr -> data.concat [0],
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.concat [1],
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_pick_first_value:
+ if (len > 8) {
+ rv = 6;
+ strcpy (buf, "(pick1st ");
+ rv += print_subexpression
+ (expr -> data.pick_first_value.car,
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression
+ (expr -> data.pick_first_value.cdr,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_host_lookup:
+ rv = 15 + strlen (expr -> data.host_lookup -> hostname);
+ if (len > rv) {
+ sprintf (buf, "(dns-lookup %s)",
+ expr -> data.host_lookup -> hostname);
+ return rv;
+ }
+ break;
+
+ case expr_and:
+ s = "and";
+ binop:
+ rv = strlen (s);
+ if (len > rv + 4) {
+ buf [0] = '(';
+ strcpy (&buf [1], s);
+ rv += 1;
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.and [0],
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.and [1],
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_or:
+ s = "or";
+ goto binop;
+
+ case expr_add:
+ s = "+";
+ goto binop;
+
+ case expr_subtract:
+ s = "-";
+ goto binop;
+
+ case expr_multiply:
+ s = "*";
+ goto binop;
+
+ case expr_divide:
+ s = "/";
+ goto binop;
+
+ case expr_remainder:
+ s = "%";
+ goto binop;
+
+ case expr_binary_and:
+ s = "&";
+ goto binop;
+
+ case expr_binary_or:
+ s = "|";
+ goto binop;
+
+ case expr_binary_xor:
+ s = "^";
+ goto binop;
+
+ case expr_not:
+ if (len > 6) {
+ rv = 5;
+ strcpy (buf, "(not ");
+ rv += print_subexpression (expr -> data.not,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_config_option:
+ s = "cfg-option";
+ goto dooption;
+
+ case expr_option:
+ s = "option";
+ dooption:
+ rv = strlen (s) + 2 + (strlen (expr -> data.option -> name) +
+ strlen (expr -> data.option -> universe -> name));
+ if (len > rv) {
+ sprintf (buf, "(option %s.%s)",
+ expr -> data.option -> universe -> name,
+ expr -> data.option -> name);
+ return rv;
+ }
+ break;
+
+ case expr_hardware:
+ if (len > 10) {
+ strcpy (buf, "(hardware)");
+ return 10;
+ }
+ break;
+
+ case expr_packet:
+ if (len > 10) {
+ rv = 8;
+ strcpy (buf, "(substr ");
+ rv += print_subexpression (expr -> data.packet.offset,
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.packet.len,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_const_data:
+ s = print_hex_1 (expr -> data.const_data.len,
+ expr -> data.const_data.data, len);
+ rv = strlen (s);
+ if (rv >= len)
+ rv = len - 1;
+ strncpy (buf, s, rv);
+ buf [rv] = 0;
+ return rv;
+
+ case expr_encapsulate:
+ rv = 13;
+ strcpy (buf, "(encapsulate ");
+ rv += expr -> data.encapsulate.len;
+ if (rv + 2 > len)
+ rv = len - 2;
+ strncpy (buf,
+ (const char *)expr -> data.encapsulate.data, rv - 13);
+ buf [rv++] = ')';
+ buf [rv++] = 0;
+ break;
+
+ case expr_extract_int8:
+ if (len > 7) {
+ rv = 6;
+ strcpy (buf, "(int8 ");
+ rv += print_subexpression (expr -> data.extract_int,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_extract_int16:
+ if (len > 8) {
+ rv = 7;
+ strcpy (buf, "(int16 ");
+ rv += print_subexpression (expr -> data.extract_int,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_extract_int32:
+ if (len > 8) {
+ rv = 7;
+ strcpy (buf, "(int32 ");
+ rv += print_subexpression (expr -> data.extract_int,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_encode_int8:
+ if (len > 7) {
+ rv = 6;
+ strcpy (buf, "(to-int8 ");
+ rv += print_subexpression (expr -> data.encode_int,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_encode_int16:
+ if (len > 8) {
+ rv = 7;
+ strcpy (buf, "(to-int16 ");
+ rv += print_subexpression (expr -> data.encode_int,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_encode_int32:
+ if (len > 8) {
+ rv = 7;
+ strcpy (buf, "(to-int32 ");
+ rv += print_subexpression (expr -> data.encode_int,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_const_int:
+ s = print_dec_1 (expr -> data.const_int);
+ rv = strlen (s);
+ if (len > rv) {
+ strcpy (buf, s);
+ return rv;
+ }
+ break;
+
+ case expr_exists:
+ rv = 10 + (strlen (expr -> data.option -> name) +
+ strlen (expr -> data.option -> universe -> name));
+ if (len > rv) {
+ sprintf (buf, "(exists %s.%s)",
+ expr -> data.option -> universe -> name,
+ expr -> data.option -> name);
+ return rv;
+ }
+ break;
+
+ case expr_variable_exists:
+ rv = 10 + strlen (expr -> data.variable);
+ if (len > rv) {
+ sprintf (buf, "(defined %s)", expr -> data.variable);
+ return rv;
+ }
+ break;
+
+ case expr_variable_reference:
+ rv = strlen (expr -> data.variable);
+ if (len > rv) {
+ sprintf (buf, "%s", expr -> data.variable);
+ return rv;
+ }
+ break;
+
+ case expr_known:
+ s = "known";
+ astring:
+ rv = strlen (s);
+ if (len > rv) {
+ strcpy (buf, s);
+ return rv;
+ }
+ break;
+
+ case expr_leased_address:
+ s = "leased-address";
+ goto astring;
+
+ case expr_client_state:
+ s = "client-state";
+ goto astring;
+
+ case expr_host_decl_name:
+ s = "host-decl-name";
+ goto astring;
+
+ case expr_lease_time:
+ s = "lease-time";
+ goto astring;
+
+ case expr_static:
+ s = "static";
+ goto astring;
+
+ case expr_filename:
+ s = "filename";
+ goto astring;
+
+ case expr_sname:
+ s = "server-name";
+ goto astring;
+
+ case expr_reverse:
+ if (len > 11) {
+ rv = 13;
+ strcpy (buf, "(reverse ");
+ rv += print_subexpression (expr -> data.reverse.width,
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.reverse.buffer,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_binary_to_ascii:
+ if (len > 5) {
+ rv = 9;
+ strcpy (buf, "(b2a ");
+ rv += print_subexpression (expr -> data.b2a.base,
+ buf + rv, len - rv - 4);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.b2a.width,
+ buf + rv, len - rv - 3);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.b2a.separator,
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.b2a.buffer,
+ buf + rv, len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_dns_transaction:
+ rv = 10;
+ if (len < rv + 2) {
+ buf [0] = '(';
+ strcpy (&buf [1], "ns-update ");
+ while (len < rv + 2) {
+ rv += print_subexpression
+ (expr -> data.dns_transaction.car,
+ buf + rv, len - rv - 2);
+ buf [rv++] = ' ';
+ expr = expr -> data.dns_transaction.cdr;
+ }
+ buf [rv - 1] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ return 0;
+
+ case expr_ns_delete:
+ s = "delete";
+ left = 4;
+ goto dodnsupd;
+ case expr_ns_exists:
+ s = "exists";
+ left = 4;
+ goto dodnsupd;
+ case expr_ns_not_exists:
+ s = "not_exists";
+ left = 4;
+ goto dodnsupd;
+ case expr_ns_add:
+ s = "update";
+ left = 5;
+ dodnsupd:
+ rv = strlen (s);
+ if (len > strlen (s) + 1) {
+ buf [0] = '(';
+ strcpy (buf + 1, s);
+ rv++;
+ buf [rv++] = ' ';
+ s = print_dec_1 (expr -> data.ns_add.rrclass);
+ if (len > rv + strlen (s) + left) {
+ strcpy (&buf [rv], s);
+ rv += strlen (&buf [rv]);
+ }
+ buf [rv++] = ' ';
+ left--;
+ s = print_dec_1 (expr -> data.ns_add.rrtype);
+ if (len > rv + strlen (s) + left) {
+ strcpy (&buf [rv], s);
+ rv += strlen (&buf [rv]);
+ }
+ buf [rv++] = ' ';
+ left--;
+ rv += print_subexpression
+ (expr -> data.ns_add.rrname,
+ buf + rv, len - rv - left);
+ buf [rv++] = ' ';
+ left--;
+ rv += print_subexpression
+ (expr -> data.ns_add.rrdata,
+ buf + rv, len - rv - left);
+ buf [rv++] = ' ';
+ left--;
+ rv += print_subexpression
+ (expr -> data.ns_add.ttl,
+ buf + rv, len - rv - left);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_null:
+ if (len > 6) {
+ strcpy (buf, "(null)");
+ return 6;
+ }
+ break;
+ case expr_funcall:
+ rv = 12 + strlen (expr -> data.funcall.name);
+ if (len > rv + 1) {
+ strcpy (buf, "(funcall ");
+ strcpy (buf + 9, expr -> data.funcall.name);
+ buf [rv++] = ' ';
+ rv += print_subexpression
+ (expr -> data.funcall.arglist, buf + rv,
+ len - rv - 1);
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_arg:
+ rv = print_subexpression (expr -> data.arg.val, buf, len);
+ if (expr -> data.arg.next && rv + 2 < len) {
+ buf [rv++] = ' ';
+ rv += print_subexpression (expr -> data.arg.next,
+ buf, len);
+ if (rv + 1 < len)
+ buf [rv++] = 0;
+ return rv;
+ }
+ break;
+
+ case expr_function:
+ rv = 9;
+ if (len > rv + 1) {
+ struct string_list *foo;
+ strcpy (buf, "(function");
+ for (foo = expr -> data.func -> args;
+ foo; foo = foo -> next) {
+ if (len > rv + 2 + strlen (foo -> string)) {
+ buf [rv - 1] = ' ';
+ strcpy (&buf [rv], foo -> string);
+ rv += strlen (foo -> string);
+ }
+ }
+ buf [rv++] = ')';
+ buf [rv] = 0;
+ return rv;
+ }
+
+ case expr_gethostname:
+ if (len > 13) {
+ strcpy(buf, "(gethostname)");
+ return 13;
+ }
+ break;
+
+ default:
+ log_fatal("Impossible case at %s:%d (undefined expression "
+ "%d).", MDL, expr->op);
+ break;
+ }
+ return 0;
+}
+
+void print_expression (name, expr)
+ const char *name;
+ struct expression *expr;
+{
+ char buf [1024];
+
+ print_subexpression (expr, buf, sizeof buf);
+ log_info ("%s: %s", name, buf);
+}
+
+int token_print_indent_concat (FILE *file, int col, int indent,
+ const char *prefix,
+ const char *suffix, ...)
+{
+ va_list list;
+ unsigned len;
+ char *s, *t, *u;
+
+ va_start (list, suffix);
+ s = va_arg (list, char *);
+ len = 0;
+ while (s) {
+ len += strlen (s);
+ s = va_arg (list, char *);
+ }
+ va_end (list);
+
+ t = dmalloc (len + 1, MDL);
+ if (!t)
+ log_fatal ("token_print_indent: no memory for copy buffer");
+
+ va_start (list, suffix);
+ s = va_arg (list, char *);
+ u = t;
+ while (s) {
+ len = strlen (s);
+ strcpy (u, s);
+ u += len;
+ s = va_arg (list, char *);
+ }
+ va_end (list);
+
+ len = token_print_indent (file, col, indent,
+ prefix, suffix, t);
+ dfree (t, MDL);
+ return col;
+}
+
+int token_indent_data_string (FILE *file, int col, int indent,
+ const char *prefix, const char *suffix,
+ struct data_string *data)
+{
+ int i;
+ char *buf;
+ char obuf [3];
+
+ /* See if this is just ASCII. */
+ for (i = 0; i < data -> len; i++)
+ if (!isascii (data -> data [i]) ||
+ !isprint (data -> data [i]))
+ break;
+
+ /* If we have a purely ASCII string, output it as text. */
+ if (i == data -> len) {
+ buf = dmalloc (data -> len + 3, MDL);
+ if (buf) {
+ buf [0] = '"';
+ memcpy (buf + 1, data -> data, data -> len);
+ buf [data -> len + 1] = '"';
+ buf [data -> len + 2] = 0;
+ i = token_print_indent (file, col, indent,
+ prefix, suffix, buf);
+ dfree (buf, MDL);
+ return i;
+ }
+ }
+
+ for (i = 0; i < data -> len; i++) {
+ sprintf (obuf, "%2.2x", data -> data [i]);
+ col = token_print_indent (file, col, indent,
+ i == 0 ? prefix : "",
+ (i + 1 == data -> len
+ ? suffix
+ : ""), obuf);
+ if (i + 1 != data -> len)
+ col = token_print_indent (file, col, indent,
+ prefix, suffix, ":");
+ }
+ return col;
+}
+
+int token_print_indent (FILE *file, int col, int indent,
+ const char *prefix,
+ const char *suffix, const char *buf)
+{
+ int len = strlen (buf) + strlen (prefix);
+ if (col + len > 79) {
+ if (indent + len < 79) {
+ indent_spaces (file, indent);
+ col = indent;
+ } else {
+ indent_spaces (file, col);
+ col = len > 79 ? 0 : 79 - len - 1;
+ }
+ } else if (prefix && *prefix) {
+ fputs (prefix, file);
+ col += strlen (prefix);
+ }
+ fputs (buf, file);
+ col += len;
+ if (suffix && *suffix) {
+ if (col + strlen (suffix) > 79) {
+ indent_spaces (file, indent);
+ col = indent;
+ } else {
+ fputs (suffix, file);
+ col += strlen (suffix);
+ }
+ }
+ return col;
+}
+
+void indent_spaces (FILE *file, int indent)
+{
+ int i;
+ fputc ('\n', file);
+ for (i = 0; i < indent; i++)
+ fputc (' ', file);
+}
+
+#if defined (NSUPDATE)
+#if defined (DEBUG_DNS_UPDATES)
+/*
+ * direction outbound (messages to the dns server)
+ * inbound (messages from the dns server)
+ * ddns_cb is the control block associated with the message
+ * result is the result from the dns code. For outbound calls
+ * it is from the call to pass the message to the dns library.
+ * For inbound calls it is from the event returned by the library.
+ *
+ * For outbound messages we print whatever we think is interesting
+ * from the control block.
+ * For inbound messages we only print the transaction id pointer
+ * and the result and expect that the user will match them up as
+ * necessary. Note well: the transaction information is opaque to
+ * us so we simply print the pointer to it. This should be sufficient
+ * to match requests and replys in a short sequence but is awkward
+ * when trying to use it for longer sequences.
+ */
+void
+print_dns_status (int direction,
+ struct dhcp_ddns_cb *ddns_cb,
+ isc_result_t result)
+{
+ char obuf[1024];
+ char *s = obuf, *end = &obuf[sizeof(obuf)-2];
+ char *en;
+ const char *result_str;
+ char ddns_address[
+ sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
+
+ if (direction == DDNS_PRINT_INBOUND) {
+ log_info("DDNS reply: id ptr %p, result: %s",
+ ddns_cb->transaction, isc_result_totext(result));
+ return;
+ }
+
+ /*
+ * To avoid having to figure out if any of the strings
+ * aren't NULL terminated, just 0 the whole string
+ */
+ memset(obuf, 0, 1024);
+
+ en = "DDNS request: id ptr ";
+ if (s + strlen(en) + 16 < end) {
+ sprintf(s, "%s%p", en, ddns_cb->transaction);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+
+ switch (ddns_cb->state) {
+ case DDNS_STATE_ADD_FW_NXDOMAIN:
+ en = " add forward ";
+ break;
+ case DDNS_STATE_ADD_FW_YXDHCID:
+ en = " modify forward ";
+ break;
+
+ case DDNS_STATE_ADD_PTR:
+ en = " add reverse ";
+ break;
+
+ case DDNS_STATE_REM_FW_YXDHCID:
+ en = " remove forward ";
+ break;
+
+ case DDNS_STATE_REM_FW_NXRR:
+ en = " remove rrset ";
+ break;
+
+ case DDNS_STATE_REM_PTR:
+ en = " remove reverse ";
+ break;
+
+ case DDNS_STATE_CLEANUP:
+ en = " cleanup ";
+ break;
+
+ default:
+ en = " unknown state ";
+ break;
+ }
+
+ switch (ddns_cb->state) {
+ case DDNS_STATE_ADD_FW_NXDOMAIN:
+ case DDNS_STATE_ADD_FW_YXDHCID:
+ case DDNS_STATE_REM_FW_YXDHCID:
+ case DDNS_STATE_REM_FW_NXRR:
+ strcpy(ddns_address, piaddr(ddns_cb->address));
+ if (s + strlen(en) + strlen(ddns_address) +
+ ddns_cb->fwd_name.len + 5 < end) {
+ sprintf(s, "%s%s for %.*s", en, ddns_address,
+ ddns_cb->fwd_name.len,
+ ddns_cb->fwd_name.data);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+ break;
+
+ case DDNS_STATE_ADD_PTR:
+ case DDNS_STATE_REM_PTR:
+ if (s + strlen(en) + ddns_cb->fwd_name.len +
+ ddns_cb->rev_name.len + 5 < end) {
+ sprintf(s, "%s%.*s for %.*s", en,
+ ddns_cb->fwd_name.len,
+ ddns_cb->fwd_name.data,
+ ddns_cb->rev_name.len,
+ ddns_cb->rev_name.data);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+ break;
+
+ case DDNS_STATE_CLEANUP:
+ default:
+ if (s + strlen(en) < end) {
+ sprintf(s, "%s", en);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+ break;
+ }
+
+ en = " zone: ";
+ if (s + strlen(en) + strlen((char *)ddns_cb->zone_name) < end) {
+ sprintf(s, "%s%s", en, ddns_cb->zone_name);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+
+ en = " dhcid: ";
+ if (ddns_cb->dhcid.len > 0) {
+ if (s + strlen(en) + ddns_cb->dhcid.len-1 < end) {
+ strcpy(s, en);
+ s += strlen(s);
+ strncpy(s, (char *)ddns_cb->dhcid.data+1,
+ ddns_cb->dhcid.len-1);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+ } else {
+ en = " dhcid: <empty>";
+ if (s + strlen(en) < end) {
+ strcpy(s, en);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+ }
+
+ en = " ttl: ";
+ if (s + strlen(en) + 10 < end) {
+ sprintf(s, "%s%ld", en, ddns_cb->ttl);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+
+ en = " result: ";
+ result_str = isc_result_totext(result);
+ if (s + strlen(en) + strlen(result_str) < end) {
+ sprintf(s, "%s%s", en, result_str);
+ s += strlen(s);
+ } else {
+ goto bailout;
+ }
+
+ bailout:
+ /*
+ * We either finished building the string or ran out
+ * of space, print whatever we have in case it is useful
+ */
+ log_info("%s", obuf);
+
+ return;
+}
+#endif
+#endif /* NSUPDATE */
+
+/* Format the given time as "A; # B", where A is the format
+ * used by the parser, and B is the local time, for humans.
+ */
+const char *
+print_time(TIME t)
+{
+ static char buf[sizeof("epoch 9223372036854775807; "
+ "# Wed Jun 30 21:49:08 2147483647")];
+ static char buf1[sizeof("# Wed Jun 30 21:49:08 2147483647")];
+ time_t since_epoch;
+ /* The string: "6 2147483647/12/31 23:59:60;"
+ * is smaller than the other, used to declare the buffer size, so
+ * we can use one buffer for both.
+ */
+
+ if (t == MAX_TIME)
+ return "never;";
+
+ if (t < 0)
+ return NULL;
+
+ /* For those lucky enough to have a 128-bit time_t, ensure that
+ * whatever (corrupt) value we're given doesn't exceed the static
+ * buffer.
+ */
+#if (MAX_TIME > 0x7fffffffffffffff)
+ if (t > 0x7fffffffffffffff)
+ return NULL;
+#endif
+
+ if (db_time_format == LOCAL_TIME_FORMAT) {
+ since_epoch = mktime(localtime(&t));
+ if ((strftime(buf1, sizeof(buf1),
+ "# %a %b %d %H:%M:%S %Y",
+ localtime(&t)) == 0) ||
+ (snprintf(buf, sizeof(buf), "epoch %lu; %s",
+ (unsigned long)since_epoch, buf1) >= sizeof(buf)))
+ return NULL;
+
+ } else {
+ /* No bounds check for the year is necessary - in this case,
+ * strftime() will run out of space and assert an error.
+ */
+ if (strftime(buf, sizeof(buf), "%w %Y/%m/%d %H:%M:%S;",
+ gmtime(&t)) == 0)
+ return NULL;
+ }
+
+ return buf;
+}