/* print.c Turn data structures into printable text. */ /* * Copyright (c) 2004-2017 by Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1995-2003 by Internet Software Consortium * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * 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 * * https://www.isc.org/ * */ #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, char enclose_char, 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++; } if (enclose_char) { nulen +=2 ; } buf = dmalloc (nulen + 1, MDL); if (buf) { nsp = buf; if (enclose_char) { *nsp++ = enclose_char; } 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]; } if (enclose_char) { *nsp++ = enclose_char; } *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 : ""); } #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; { char *bufptr = buf; int byte = 0; if (data == NULL || bufptr == NULL || limit == 0) { return; } if (((len == 0) || ((len * 3) > limit))) { *bufptr = 0x0; return; } for ( ; byte < len; ++byte) { if (byte > 0) { *bufptr++ = ':'; } sprintf(bufptr, "%02x", data[byte]); bufptr += 2; } 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; s = &dq_buf [0]; 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; } break; 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); col = 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 = 0; if (prefix != NULL) len += strlen (prefix); if (buf != NULL) len += strlen (buf); 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); } if ((buf != NULL) && (*buf != 0)) { fputs (buf, file); col += strlen(buf); } 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: "; 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; } /* !brief Return the given data as a string of hex digits "xx:xx:xx ..." * * Converts the given data into a null-terminated, string of hex digits, * stored in an allocated buffer. It is the caller's responsiblity to free * the buffer. * * \param s - pointer to the data to convert * \param len - length of the data to convert * \param file - source file of invocation * \param line - line number of invocation * * \return Returns an allocated buffer containing the hex string */ char *buf_to_hex (const unsigned char *s, unsigned len, const char *file, int line) { unsigned nulen = 0; char *buf; /* If somebody hands us length of zero, we'll give them * back an empty string */ if (!len) { buf = dmalloc (1, MDL); if (buf) { *buf = 0x0; } return (buf); } /* Figure out how big it needs to be. print_to_hex uses * "%02x:" per character. Note since there's no trailing colon * we'll have room for the null */ nulen = (len * 3); /* Allocate our buffer */ buf = dmalloc (nulen, MDL); /* Hex-ify it */ if (buf) { print_hex_only (len, s, nulen, buf); } return buf; } /* !brief Formats data into a string based on a lease id format * * Takes the given data and returns an allocated string whose contents are * the string version of that data, formatted according to the output lease * id format. Note it is the caller's responsiblity to delete the string. * * Currently two formats are supported: * * OCTAL - Default or "legacy" CSL format enclosed in quotes '"'. * * HEX - Bytes represented as string colon seperated of hex digit pairs * (xx:xx:xx...) * * \param s - data to convert * \param len - length of the data to convert * \param format - desired format of the result * \param file - source file of invocation * \param line - line number of invocation * * \return A pointer to the allocated, null-terminated string */ char *format_lease_id(const unsigned char *s, unsigned len, int format, const char *file, int line) { char *idstr = NULL; switch (format) { case TOKEN_HEX: idstr = buf_to_hex(s, len, MDL); break; case TOKEN_OCTAL: default: idstr = quotify_buf(s, len, '"', MDL); break; } return (idstr); }