#ifndef lint
static char copyright[] =
-"$Id: options.c,v 1.91 2006/06/01 20:23:17 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
+"$Id: options.c,v 1.92 2006/07/22 02:24:16 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
#endif /* not lint */
#define DHCP_OPTION_DATA
static void do_option_set PROTO ((pair *,
struct option_cache *,
enum statement_op));
+static int pretty_escape(char **, char *, const unsigned char **,
+ const unsigned char *);
+static int pretty_text(char **, char *, const unsigned char **,
+ const unsigned char *, int);
+static int pretty_domain(char **, char *, const unsigned char **,
+ const unsigned char *);
/* Parse all available options out of the specified packet. */
case 'a':
case 'X':
case 'x':
+ case 'D':
return 0;
/* 'E' is variable length, but not arbitrary...you
break;
case 'd': /* "Domain name" */
+ case 'D': /* "rfc1035 compressed names" */
case 't': /* "ASCII Text" */
case 'X': /* "ASCII or Hex Conditional */
case 'x': /* "Hex" */
int emit_quotes;
{
static char optbuf [32768]; /* XXX */
+ static char *endbuf = &optbuf[sizeof(optbuf)];
int hunksize = 0;
int opthunk = 0;
int hunkinc = 0;
int numhunk = -1;
int numelem = 0;
+ int count;
+ int i, j, k, l;
char fmtbuf [32];
struct enumeration *enumbuf [32];
- int i, j, k, l;
char *op = optbuf;
const unsigned char *dp = data;
struct in_addr foo;
for (j = 0; j < numelem; j++) {
switch (fmtbuf [j]) {
case 't':
- if (emit_quotes)
- *op++ = '"';
- for (; dp < data + len; dp++) {
- if (!isascii (*dp) ||
- !isprint (*dp)) {
- /* Skip trailing NUL. */
- if (dp + 1 != data + len ||
- *dp != 0) {
- sprintf (op, "\\%03o",
- *dp);
- op += 4;
- }
- } else if (*dp == '"' ||
- *dp == '\'' ||
- *dp == '$' ||
- *dp == '`' ||
- *dp == '\\') {
- *op++ = '\\';
- *op++ = *dp;
- } else
- *op++ = *dp;
+ /* endbuf-1 leaves room for NULL. */
+ k = pretty_text(&op, endbuf - 1, &dp,
+ data + len, emit_quotes);
+ if (k == -1) {
+ log_error("Error printing text.");
+ break;
}
- if (emit_quotes)
- *op++ = '"';
*op = 0;
break;
+ case 'D': /* RFC1035 format name list */
+ for( ; dp < (data + len) ; dp += k) {
+ unsigned char nbuff[NS_MAXCDNAME];
+ const unsigned char *nbp, *nend;
+
+ nend = &nbuff[sizeof(nbuff)];
+
+ /* If this is for ISC DHCP consumption
+ * (emit_quotes), lay it out as a list
+ * of STRING tokens. Otherwise, it is
+ * a space-separated list of DNS-
+ * escaped names as /etc/resolv.conf
+ * might digest.
+ */
+ if (dp != data) {
+ if (op + 2 > endbuf)
+ break;
+
+ if (emit_quotes)
+ *op++ = ',';
+ *op++ = ' ';
+ }
+
+ k = MRns_name_unpack(data,
+ data + len,
+ dp, nbuff,
+ sizeof(nbuff));
+
+ if (k == -1) {
+ log_error("Invalid domain "
+ "list.");
+ break;
+ }
+
+ /* If emit_quotes, then use ISC DHCP
+ * escapes. Otherwise, rely only on
+ * ns_name_ntop().
+ */
+ if (emit_quotes) {
+ nbp = nbuff;
+ pretty_domain(&op, endbuf-1,
+ &nbp, nend);
+ } else {
+ count = MRns_name_ntop(
+ nbuff, op,
+ (endbuf-op)-1);
+
+ if (count == -1) {
+ log_error("Invalid "
+ "domain name.");
+ break;
+ }
+
+ op += count;
+ }
+ }
+ *op = '\0';
+ break;
/* pretty-printing an array of enums is
going to get ugly. */
case 'N':
break;
}
strcpy (op, enumbuf [j] -> values [i].name);
- op += strlen (op);
break;
case 'I':
foo.s_addr = htonl (getULong (dp));
dump_rc_history (0);
#endif
}
+
+static int
+pretty_escape(char **dst, char *dend, const unsigned char **src,
+ const unsigned char *send)
+{
+ int count = 0;
+
+ /* If there aren't as many bytes left as there are in the source
+ * buffer, don't even bother entering the loop.
+ */
+ if (dst == NULL || src == NULL || (*dst >= dend) || (*src > send) ||
+ *dst == NULL || *src == NULL ||
+ ((send - *src) > (dend - *dst)))
+ return -1;
+
+ for ( ; *src < send ; *src++) {
+ if (!isascii (**src) || !isprint (**src)) {
+ /* Skip trailing NUL. */
+ if ((*src + 1) != send || **src != '\0') {
+ if (*dst + 4 > dend)
+ return -1;
+
+ sprintf(*dst, "\\%03o",
+ **src);
+ *dst += 4;
+ count += 4;
+ }
+ } else if (**src == '"' || **src == '\'' || **src == '$' ||
+ **src == '`' || **src == '\\') {
+ if (*dst + 2 > dend)
+ return -1;
+
+ **dst = '\\';
+ *dst++;
+ **dst = **src;
+ *dst++;
+ count += 2;
+ } else {
+ if (*dst + 1 > dend)
+ return -1;
+
+ **dst = **src;
+ *dst++;
+ count++;
+ }
+ }
+
+ return count;
+}
+
+static int
+pretty_text(char **dst, char *dend, const unsigned char **src,
+ const unsigned char *send, int emit_quotes)
+{
+ int count;
+
+ if (dst == NULL || dend == NULL || src == NULL || send == NULL ||
+ *dst == NULL || *src == NULL ||
+ ((*dst + (emit_quotes ? 2 : 0)) > dend) || (*src > send))
+ return -1;
+
+ if (emit_quotes) {
+ **dst = '"';
+ *dst++;
+ }
+
+ /* dend-1 leaves 1 byte for the closing quote. */
+ count = pretty_escape(dst, dend - (emit_quotes ? 1 : 0), src, send);
+
+ if (count == -1)
+ return -1;
+
+ if (emit_quotes && (*dst < dend)) {
+ **dst = '"';
+ *dst++;
+
+ /* Includes quote prior to pretty_escape(); */
+ count += 2;
+ }
+
+ return count;
+}
+
+static int
+pretty_domain(char **dst, char *dend, const unsigned char **src,
+ const unsigned char *send)
+{
+ const unsigned char *tend;
+ int count = 2;
+ int tsiz, status;
+
+ if (dst == NULL || dend == NULL || src == NULL || send == NULL ||
+ *dst == NULL || *src == NULL ||
+ ((*dst + 2) > dend) || (*src >= send))
+ return -1;
+
+ **dst = '"';
+ *dst++;
+
+ do {
+ /* Continue loop until end of src buffer. */
+ if (*src >= send)
+ break;
+
+ /* Consume tag size. */
+ tsiz = **src;
+ *src++;
+
+ /* At root, finis. */
+ if (tsiz == 0)
+ break;
+
+ tend = *src + tsiz;
+
+ /* If the tag exceeds the source buffer, it's illegal.
+ * This should also trap compression pointers (which should
+ * not be in these buffers).
+ */
+ if (tend > send)
+ return -1;
+
+ /* dend-2 leaves room for a trailing dot and quote. */
+ status = pretty_escape(dst, dend-2, src, tend);
+
+ if ((status == -1) || ((*dst + 2) > dend))
+ return -1;
+
+ **dst = '.';
+ *dst++;
+ count += status + 1;
+ }
+ while(1);
+
+ **dst = '"';
+ *dst++;
+
+ return count;
+}
+