summaryrefslogtreecommitdiff
path: root/common/tree.c
diff options
context:
space:
mode:
authorThomas Markwalder <tmark@isc.org>2014-11-25 15:21:39 -0500
committerThomas Markwalder <tmark@isc.org>2014-11-25 15:22:52 -0500
commit04daf4fe4b5dd4afac1aed901bfe8dda3ec0edf3 (patch)
treee9f8f975accc8e3103e731f94fd3d2b581628c1e /common/tree.c
parentf3a44c1037cc7ef5a16c9e5e033242ac08fbb21f (diff)
downloadisc-dhcp-04daf4fe4b5dd4afac1aed901bfe8dda3ec0edf3.tar.gz
[master] Fixed concatenation of "Dc" formatted options such as domain-search
Merges in rt20558.
Diffstat (limited to 'common/tree.c')
-rw-r--r--common/tree.c172
1 files changed, 172 insertions, 0 deletions
diff --git a/common/tree.c b/common/tree.c
index bf5f0ec3..03089b21 100644
--- a/common/tree.c
+++ b/common/tree.c
@@ -1067,6 +1067,7 @@ int evaluate_boolean_expression (result, packet, lease, client_state,
case expr_sname:
case expr_gethostname:
case expr_v6relay:
+ case expr_concat_dclist:
log_error ("Data opcode in evaluate_boolean_expression: %d",
expr -> op);
return 0;
@@ -2113,6 +2114,49 @@ int evaluate_data_expression (result, packet, lease, client_state,
#endif
return (s1);
+ case expr_concat_dclist: {
+ /* Operands are compressed domain-name lists ("Dc" format)
+ * Fetch both compressed lists then call concat_dclists which
+ * combines them into a single compressed list. */
+ memset(&data, 0, sizeof data);
+ int outcome = 0;
+ s0 = evaluate_data_expression(&data, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr->data.concat[0], MDL);
+
+ memset (&other, 0, sizeof other);
+ s1 = evaluate_data_expression (&other, packet, lease,
+ client_state,
+ in_options, cfg_options, scope,
+ expr->data.concat[1], MDL);
+
+ if (s0 && s1) {
+ outcome = concat_dclists(result, &data, &other);
+ if (outcome == 0) {
+ log_error ("data: concat_dclist failed");
+ }
+ }
+
+#if defined (DEBUG_EXPRESSIONS)
+ log_debug ("data: concat_dclists (%s, %s) = %s",
+ (s0 ? print_hex_1(data.len, data.data, data.len)
+ : "NULL"),
+ (s1 ? print_hex_2(other.len, other.data, other.len)
+ : "NULL"),
+ (((s0 && s1) && result->len > 0)
+ ? print_hex_3 (result->len, result->data, result->len)
+ : "NULL"));
+#endif
+ if (s0)
+ data_string_forget (&data, MDL);
+
+ if (s1)
+ data_string_forget (&other, MDL);
+
+ return (outcome);
+ } /* expr_concat_dclist */
+
case expr_check:
case expr_equal:
case expr_not_equal:
@@ -2164,6 +2208,7 @@ int evaluate_data_expression (result, packet, lease, client_state,
case expr_arg:
break;
+
}
log_error ("Bogus opcode in evaluate_data_expression: %d", expr -> op);
@@ -3116,6 +3161,7 @@ static int op_val (op)
case expr_client_state:
case expr_gethostname:
case expr_v6relay:
+ case expr_concat_dclist:
return 100;
case expr_equal:
@@ -3209,6 +3255,7 @@ enum expression_context op_context (op)
case expr_function:
case expr_gethostname:
case expr_v6relay:
+ case expr_concat_dclist:
return context_any;
case expr_equal:
@@ -4087,4 +4134,129 @@ int unset (struct binding_scope *scope, const char *name)
return 0;
}
+/*!
+ * \brief Adds two Dc-formatted lists into a single Dc-formatted list
+ *
+ * Given two data_strings containing compressed lists, it constructs a
+ * third data_string containing a single compressed list:
+ *
+ * 1. Decompressing the first list into a buffer
+ * 2. Decompressing the second list onto the end of the buffer
+ * 3. Compressing the buffer into the result
+ *
+ * If either list is empty, the result will be the equal to the compressed
+ * content of the non-empty list. If both lists are empty, the result will
+ * be an "empty" list: a 1 byte buffer containing 0x00.
+ *
+ * It relies on two functions to decompress and compress:
+ *
+ * - MRns_name_uncompress_list() - produces a null-terminated string of
+ * comma-separated domain-names from a buffer containing "Dc" formatted
+ * data
+ *
+ * - MRns_name_compress_list() - produces a buffer containing "Dc" formatted
+ * data from a null-terminated string containing comma-separated domain-names
+ *
+ * \param result data_string which will contain the combined list
+ * in Dc format
+ * \param list1 data_string containing first Dc formatted list
+ * \param list2 data_string containing second Dc formatted list
+ * \return 0 if there is an error, the length of the new list when successful
+ */
+int concat_dclists (struct data_string* result,
+ struct data_string* list1,
+ struct data_string* list2)
+{
+ char uncompbuf[32*NS_MAXCDNAME];
+ char *uncomp = uncompbuf;
+ int uncomp_len = 0;
+ int compbuf_max = 0;
+ int list_len = 0;
+ int i;
+
+ /* If not empty, uncompress first list into the uncompressed buffer */
+ if ((list1->data) && (list1->len)) {
+ list_len = MRns_name_uncompress_list(list1->data,
+ list1->len, uncomp,
+ sizeof(uncompbuf));
+ if (list_len < 0) {
+ log_error ("concat_dclists:"
+ " error decompressing domain list 1");
+ return (0);
+ }
+
+ uncomp_len = list_len;
+ uncomp += list_len;
+ }
+
+ /* If not empty, uncompress second list into the uncompressed buffer */
+ if ((list2->data) && (list2->len)) {
+ /* If first list wasn't empty, add a comma */
+ if (uncomp_len > 0) {
+ *uncomp++ = ',';
+ uncomp_len++;
+ }
+
+ list_len = MRns_name_uncompress_list(list2->data, list2->len,
+ uncomp, (sizeof(uncompbuf)
+ - uncomp_len));
+ if (list_len < 0) {
+ log_error ("concat_dclists:"
+ " error decompressing domain list 2");
+ return (0);
+ }
+
+ uncomp_len += list_len;
+ uncomp += list_len;
+ }
+
+ /* If both lists were empty, return an "empty" result */
+ if (uncomp_len == 0) {
+ if (!buffer_allocate (&result->buffer, 1, MDL)) {
+ log_error ("concat_dclists: empty list allocate fail");
+ result->len = 0;
+ return (0);
+ }
+
+ result->len = 1;
+ result->data = result->buffer->data;
+ return (1);
+ }
+
+ /* Estimate the buffer size needed for decompression. The largest
+ * decompression would if one where there are no repeated portions,
+ * (i.e. no compressions). Therefore that size should be the
+ * decompressed string length + 2 for each comma + a final null. Each
+ * dot gets replaced with a length byte and is accounted for in string
+ * length. Mininum length is * uncomp_len + 3. */
+ compbuf_max = uncomp_len + 3;
+ uncomp = uncompbuf;
+ for (i = 0; i < uncomp_len; i++)
+ if (*uncomp++ == ',')
+ compbuf_max += 2;
+
+ /* Allocate compression buffer based on estimated max */
+ if (!buffer_allocate (&result->buffer, compbuf_max, MDL)) {
+ log_error ("concat_dclists: No memory for result");
+ result->len = 0;
+ return (0);
+ }
+
+ /* Compress the combined list into result */
+ list_len = MRns_name_compress_list(uncompbuf, uncomp_len,
+ result->buffer->data, compbuf_max);
+
+ if (list_len <= 0) {
+ log_error ("concat_dlists: error compressing result");
+ data_string_forget(result, MDL);
+ result->len = 0;
+ return (0);
+ }
+
+ /* Update result length to actual size */
+ result->len = list_len;
+ result->data = result->buffer->data;
+ return (list_len);
+}
+
/* vim: set tabstop=8: */