diff options
author | Thomas Markwalder <tmark@isc.org> | 2014-11-25 15:21:39 -0500 |
---|---|---|
committer | Thomas Markwalder <tmark@isc.org> | 2014-11-25 15:22:52 -0500 |
commit | 04daf4fe4b5dd4afac1aed901bfe8dda3ec0edf3 (patch) | |
tree | e9f8f975accc8e3103e731f94fd3d2b581628c1e /common/tree.c | |
parent | f3a44c1037cc7ef5a16c9e5e033242ac08fbb21f (diff) | |
download | isc-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.c | 172 |
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: */ |