]> git.ipfire.org Git - thirdparty/dhcp.git/commitdiff
[v4_2] Fixed concatenation of "Dc" formatted options such as domain-search
authortmarkwalder <tmark@isc.org>
Tue, 25 Nov 2014 20:45:42 +0000 (15:45 -0500)
committertmarkwalder <tmark@isc.org>
Tue, 25 Nov 2014 20:45:42 +0000 (15:45 -0500)
    Merges in rt20558.

RELNOTES
common/ns_name.c
common/options.c
common/tests/ns_name_test.c [new file with mode: 0644]
common/tree.c
includes/dhcpd.h
includes/minires.h
includes/tree.h

index f59bb449809733a8654da2256ad0dfec357b3e5e..42c042b0a7a1d341f6ed65a6ed8471f1160b77ff 100644 (file)
--- a/RELNOTES
+++ b/RELNOTES
@@ -142,6 +142,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.2.7rc1
 
 - None
index 44b2495b0dc3fcee8e070ab0c3c13ccaef49bce4..829fe146e108c5b2af724276301f8d0d6163f71b 100644 (file)
@@ -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);
+}
index 85ec183f9d45cf21807a2b4f56c788cf54bf9ae3..757beae1df4336e68715c7a67b7a17c7582559b0 100644 (file)
@@ -2207,6 +2207,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);
diff --git a/common/tests/ns_name_test.c b/common/tests/ns_name_test.c
new file mode 100644 (file)
index 0000000..adbae60
--- /dev/null
@@ -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());
+}
index 5dc2bb06d3c68ec45ff38dbcd5677464614398f8..4c2e58720528b1499546c921d71ae79f594df0b0 100644 (file)
@@ -1365,6 +1365,7 @@ int evaluate_boolean_expression (result, packet, lease, client_state,
              case expr_filename:
              case expr_sname:
              case expr_gethostname:
+             case expr_concat_dclist:
                log_error ("Data opcode in evaluate_boolean_expression: %d",
                      expr -> op);
                return 0;
@@ -2321,6 +2322,49 @@ int evaluate_data_expression (result, packet, lease, client_state,
                data_string_forget(result, MDL);
                return 0;
 
+             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:
@@ -3417,6 +3461,7 @@ static int op_val (op)
              case expr_binary_xor:
              case expr_client_state:
              case expr_gethostname:
+             case expr_concat_dclist:
                return 100;
 
              case expr_equal:
@@ -3511,6 +3556,7 @@ enum expression_context op_context (op)
              case expr_funcall:
              case expr_function:
              case expr_gethostname:
+             case expr_concat_dclist:
                return context_any;
 
              case expr_equal:
@@ -4469,4 +4515,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: */
index e90c262e40fb932ce70bcad452667aec8a72c430..59328d722fb0019fe9ea8d9b020a4434e293328c 100644 (file)
@@ -2150,6 +2150,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;
index 3555bbb03d7c2aa42b385512f58be50de3f6db75..2e6126c9a36696037df314102078640a6d876162 100644 (file)
@@ -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 */
index 574165e74ffa8c01f6db0c1f76381d426d8fcc17..332e6b6c4a95af270ef9b44e7b769571c08bcb62 100644 (file)
@@ -193,7 +193,8 @@ enum expr_op {
        expr_lcase,
        expr_regex_match,
        expr_iregex_match,
-       expr_gethostname
+       expr_gethostname,
+       expr_concat_dclist
 };
 
 struct expression {