Commit 250a8a15 authored by Thomas Markwalder's avatar Thomas Markwalder
Browse files

[v4_1_esv] Fixed concatenation of "Dc" formatted options such as domain-search

    Merges in rt20558.
parent f0217670
......@@ -137,6 +137,12 @@ by Eric Young (eay@cryptsoft.com).
[ISC-Bugs #36810]
[ISC-Bugs #20352]
- Corrected an issue which caused dhclient to incorrectly form the result when
prepending or appending to the IPv4 domain-search option,received from the
server, when either of the values being combined contain compressed
components.
[ISC-Bugs #20558]
Changes since 4.1-ESV-R10rc1
- None
......
......@@ -2201,6 +2201,29 @@ void set_option (universe, options, option, op)
break;
}
}
/* If we are trying to combine compressed domain-lists then
* we need to change the expression opcode. The lists must
* be decompressed, combined, and then recompressed to work
* correctly. You cannot simply add two compressed lists
* together. */
switch (((memcmp(option->option->format, "Dc", 2) == 0) +
(memcmp(oc->option->format, "Dc", 2) == 0))) {
case 1:
/* Only one is "Dc", this won't work
* Not sure if you make this occur, but just
* in case. */
log_error ("Both options must be Dc format");
return;
case 2:
/* Both are "Dc", change the code */
noc->expression->op = expr_concat_dclist;
break;
default:
/* Neither are "Dc", so as you were */
break;
}
option_reference(&(noc->option), oc->option, MDL);
save_option (universe, options, noc);
option_cache_dereference (&noc, MDL);
......
......@@ -945,6 +945,7 @@ int evaluate_dns_expression (result, packet, lease, client_state, in_options,
case expr_config_option:
case expr_leased_address:
case expr_null:
case expr_concat_dclist:
log_error ("Data opcode in evaluate_dns_expression: %d",
expr -> op);
return 0;
......@@ -1369,6 +1370,7 @@ int evaluate_boolean_expression (result, packet, lease, client_state,
case expr_null:
case expr_filename:
case expr_sname:
case expr_concat_dclist:
log_error ("Data opcode in evaluate_boolean_expression: %d",
expr -> op);
return 0;
......@@ -2292,6 +2294,49 @@ int evaluate_data_expression (result, packet, lease, client_state,
#endif
return s0;
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:
......@@ -3383,6 +3428,7 @@ static int op_val (op)
case expr_binary_or:
case expr_binary_xor:
case expr_client_state:
case expr_concat_dclist:
return 100;
case expr_equal:
......@@ -3476,6 +3522,7 @@ enum expression_context op_context (op)
case expr_arg:
case expr_funcall:
case expr_function:
case expr_concat_dclist:
return context_any;
case expr_equal:
......@@ -4428,4 +4475,128 @@ 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:
*
* - ns_name_uncompress_list() - produces a null-terminated string of
* comma-separated domain-names from a buffer containing "Dc" formatted
* data
*
* - ns_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: */
......@@ -1951,6 +1951,8 @@ int find_bound_string (struct data_string *,
struct binding_scope *, const char *);
int unset (struct binding_scope *, const char *);
int data_string_sprintfa(struct data_string *ds, const char *fmt, ...);
int concat_dclists (struct data_string *, struct data_string *,
struct data_string *);
/* dhcp.c */
extern int outstanding_pings;
......
/*
* Copyright (c) 2004,2007-2010 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 2004,2007-2014 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 2001-2003 by Internet Software Consortium
*
* Permission to use, copy, modify, and distribute this software for any
......@@ -54,6 +54,10 @@ int MRns_name_unpack(const unsigned char *, const unsigned char *,
int MRns_name_ntop(const unsigned char *, char *, size_t);
int MRns_name_pton(const char *, u_char *, size_t);
int MRns_name_uncompress_list(const unsigned char*, int buflen, char*, size_t);
int MRns_name_compress_list(const char*, int buflen, unsigned char*, size_t);
#if defined (MINIRES_LIB)
#define res_update minires_update
#define res_mkupdate minires_mkupdate
......@@ -118,6 +122,8 @@ int MRns_name_pton(const char *, u_char *, size_t);
#define ns_verify_tcp_init MRns_verify_tcp_init
#define ns_verify_tcp MRns_verify_tcp
#define b64_ntop MRb64_ntop
#define ns_name_uncompress_list MRns_name_uncompress_list
#define ns_name_compress_list MRns_name_compress_list
extern const struct res_sym __p_type_syms[];
extern struct timeval cur_tv;
......
......@@ -192,7 +192,8 @@ enum expr_op {
expr_ucase,
expr_lcase,
expr_regex_match,
expr_iregex_match
expr_iregex_match,
expr_concat_dclist
};
struct expression {
......
......@@ -650,3 +650,145 @@ dn_find(const u_char *domain, const u_char *msg,
errno = ENOENT;
return (-1);
}
/*!
* \brief Creates a string of comma-separated domain-names from a
* compressed list
*
* Produces a null-terminated string of comma-separated domain-names from
* a buffer containing a compressed list of domain-names. The names will
* be dotted and without enclosing quotes. For example:
* If a compressed list contains the follwoing two domain names:
*
* a. one.two.com
* b. three.four.com
*
* The compressed data will look like this:
*
* 03 6f 6e 65 03 74 77 6f 03 63 6f 6d 00 05 74 68
* 72 65 65 04 66 6f 75 72 c0 08
*
* and will decompress into:
*
* one.two.com,three.four.com
*
* \param buf - buffer containing the compressed list of domain-names
* \param buflen - length of compressed list of domain-names
* \param dst_buf - buffer to receive the decompressed list
* \param dst_size - size of the destination buffer
*
* \return the length of the decompressed string when successful, -1 on
* error.
*/
int ns_name_uncompress_list(const unsigned char* buf, int buflen,
char* dst_buf, size_t dst_size)
{
const unsigned char* src = buf;
char* dst = dst_buf;
int consumed = 1;
int dst_remaining = dst_size;
int added_len = 0;
int first_pass = 1;
if (!buf || buflen == 0 || *buf == 0x00) {
/* nothing to do */
*dst = 0;
return (0);
}
while ((consumed > 0) && (src < (buf + buflen)))
{
if (dst_remaining <= 0) {
errno = EMSGSIZE;
return (-1);
}
if (!first_pass) {
*dst++ = ',';
*dst = '\0';
dst_remaining--;
}
consumed = ns_name_uncompress(buf, buf + buflen, src,
dst, dst_remaining);
if (consumed < 0) {
return (-1);
}
src += consumed;
added_len = strlen(dst);
dst_remaining -= added_len;
dst += added_len;
first_pass = 0;
}
*dst='\0';
/* return the length of the uncompressed list string */
return (strlen(dst_buf));
}
/*!
* \brief Creates a compressed list from a string of comma-separated
* domain-names
*
* Produces a buffer containing a compressed data version of a list of
* domain-names extracted from a comma-separated string. Given a string
* containing:
*
* one.two.com,three.four.com
*
* It will compress this into:
*
* 03 6f 6e 65 03 74 77 6f 03 63 6f 6d 00 05 74 68
* 72 65 65 04 66 6f 75 72 c0 08
*
* \param buf - buffer containing the uncompressed string of domain-names
* \param buflen - length of uncompressed string of domain-names
* \param compbuf - buffer to receive the compressed list
* \param compbuf_size - size of the compression buffer
*
* \return the length of the compressed data when successful, -1 on error.
*/
int ns_name_compress_list(const char* buf, int buflen,
unsigned char* compbuf, size_t compbuf_size)
{
char cur_name[NS_MAXCDNAME];
const unsigned char *dnptrs[256], **lastdnptr;
const char* src;
const char* src_end;
unsigned clen = 0;
int result = 0;
memset(compbuf, 0, compbuf_size);
memset(dnptrs, 0, sizeof(dnptrs));
dnptrs[0] = compbuf;
lastdnptr = &dnptrs[255];
src = buf;
src_end = buf + buflen;
while (src < src_end) {
char *comma = strchr(src, ',');
int copylen = ((comma != NULL) ? comma - src : strlen(src));
if (copylen > (sizeof(cur_name) - 1)) {
errno = EMSGSIZE;
return (-1);
}
memcpy(cur_name, src, copylen);
cur_name[copylen] = '\0';
src += copylen + 1;
result = ns_name_compress(cur_name, compbuf + clen,
compbuf_size - clen,
dnptrs, lastdnptr);
if (result < 0) {
return (-1);
}
clen += result;
}
/* return size of compressed list */
return(clen);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment