mirror of
https://gitlab.isc.org/isc-projects/dhcp
synced 2025-08-23 18:38:00 +00:00
[master] Fixed concatenation of "Dc" formatted options such as domain-search
Merges in rt20558.
This commit is contained in:
parent
f3a44c1037
commit
04daf4fe4b
6
RELNOTES
6
RELNOTES
@ -164,6 +164,12 @@ by Eric Young (eay@cryptsoft.com).
|
||||
corrected.
|
||||
[ISC-Bugs #37368]
|
||||
|
||||
- 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.3.1b1
|
||||
|
||||
- Modify the linux and openwrt dhclient scripts to process information
|
||||
|
142
common/ns_name.c
142
common/ns_name.c
@ -645,3 +645,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 MRns_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 = MRns_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 MRns_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 = MRns_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);
|
||||
}
|
||||
|
@ -2267,6 +2267,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);
|
||||
|
64
common/tests/ns_name_test.c
Normal file
64
common/tests/ns_name_test.c
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2014 by Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* Tests the newly added functions: MRns_name_compress_list and
|
||||
* MRns_name_uncompress_list. These two functions rely on most of
|
||||
* the other functions in ns_name.c. If these tests pass, then the
|
||||
* majority of those functions work.
|
||||
*
|
||||
* This is not exhaustive test of these functions, much more could be
|
||||
* done.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include <atf-c.h>
|
||||
#include "dhcpd.h"
|
||||
|
||||
ATF_TC(MRns_name_list_funcs);
|
||||
|
||||
ATF_TC_HEAD(MRns_name_list_funcs, tc) {
|
||||
atf_tc_set_md_var(tc, "descr", "MRns_name list funcs test, "
|
||||
"compress from text, decompress to text");
|
||||
}
|
||||
|
||||
ATF_TC_BODY(MRns_name_list_funcs, tc) {
|
||||
|
||||
const char text_list[] = "one.two.com,three.two.com,four.two.com";
|
||||
unsigned char comp_list[] = {
|
||||
0x03,0x6f,0x6e,0x65,0x03,0x74,0x77,0x6f,0x03,0x63,0x6f,
|
||||
0x6d,0x00,0x05,0x74,0x68,0x72,0x65,0x65,0xc0,0x04,0x04,
|
||||
0x66,0x6f,0x75,0x72,0xc0,0x04};
|
||||
unsigned char compbuf[sizeof(comp_list)];
|
||||
char textbuf[sizeof(text_list)];
|
||||
int ret;
|
||||
|
||||
memset(compbuf, 0x00, sizeof(compbuf));
|
||||
|
||||
/* Compress the reference text list */
|
||||
ret = MRns_name_compress_list(text_list, sizeof(text_list),
|
||||
compbuf, sizeof(compbuf));
|
||||
|
||||
/* Verify compressed length is correct */
|
||||
ATF_REQUIRE_MSG((ret == sizeof(compbuf)), "compressed len %d wrong", ret);
|
||||
|
||||
/* Verify compressed content is correct */
|
||||
ATF_REQUIRE_MSG((memcmp(comp_list, compbuf, sizeof(compbuf)) == 0),
|
||||
"compressed buffer content wrong");
|
||||
|
||||
/* Decompress the new compressed list */
|
||||
ret = MRns_name_uncompress_list(compbuf, ret, textbuf, sizeof(textbuf));
|
||||
|
||||
/* Verify decompressed length is correct */
|
||||
ATF_REQUIRE_MSG((ret == strlen(text_list)),
|
||||
"uncompressed len %d wrong", ret);
|
||||
|
||||
/* Verify decompressed content is correct */
|
||||
ATF_REQUIRE_MSG((memcmp(textbuf, text_list, sizeof(textbuf)) == 0),
|
||||
"uncompressed buffer content wrong");
|
||||
}
|
||||
|
||||
ATF_TP_ADD_TCS(tp)
|
||||
{
|
||||
ATF_TP_ADD_TC(tp, MRns_name_list_funcs);
|
||||
|
||||
return (atf_no_error());
|
||||
}
|
172
common/tree.c
172
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: */
|
||||
|
@ -2218,6 +2218,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;
|
||||
|
@ -1,4 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014 by Internet Systems Consortium, Inc. ("ISC")
|
||||
* Copyright (c) 2004,2007-2009 by Internet Systems Consortium, Inc. ("ISC")
|
||||
* Copyright (c) 2001-2003 by Internet Software Consortium
|
||||
*
|
||||
@ -39,5 +40,7 @@ int MRns_name_pack (const unsigned char *, unsigned char *,
|
||||
unsigned, 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);
|
||||
|
||||
#endif /* MINIRES_H */
|
||||
|
@ -191,7 +191,8 @@ enum expr_op {
|
||||
expr_regex_match,
|
||||
expr_iregex_match,
|
||||
expr_gethostname,
|
||||
expr_v6relay
|
||||
expr_v6relay,
|
||||
expr_concat_dclist
|
||||
};
|
||||
|
||||
struct expression {
|
||||
|
Loading…
x
Reference in New Issue
Block a user