diff --git a/RELNOTES b/RELNOTES index 93acdfd6..ef89b551 100644 --- a/RELNOTES +++ b/RELNOTES @@ -146,6 +146,19 @@ by Eric Young (eay@cryptsoft.com). [ISC-Bugs #36810] [ISC-Bugs #20352] +- By default, the server will now choose the value to use in the forward DNS + name from the following in order of preference: + + 1. FQDN option if provided by the client + 2. Host name option if provided by the client + 3. Configured option host-name if defined + + As before, this may be overridden by defining ddns-hostname to the desired + value (or expression). In addition, the server logic has been extended to + use the value of the host name declaration if use-host-decl-names is enabled + and no other value is available. + [ISC-Bugs #21323] + Changes since 4.3.1b1 - Modify the linux and openwrt dhclient scripts to process information diff --git a/common/alloc.c b/common/alloc.c index 0280cbc4..a55f4712 100644 --- a/common/alloc.c +++ b/common/alloc.c @@ -1253,6 +1253,70 @@ int binding_scope_reference (ptr, bp, file, line) return 1; } +/*! + * \brief Constructs a null-terminated data_string from a char* and length. + * + * Allocates a data_string and copies into it the given length of bytes + * from the given source, adding a terminating null if not present in the source + * at length-1. + * + * \param new_string pointer to the data_string to construct. Cannot be + * NULL. Note that its contents will be overwritten. Passing in the address + * of an allocated data_string will result in memory leaks. + * \param src data to be copied. Cannot be NULL. + * \param len length of the data to copied + * + * \return 1 - if the data_string is constructed successfully, 0 if + * target data_struct is NULL or the buffer allocation fails. + */ +int +data_string_new(struct data_string *new_string, + const char *src, unsigned int len, + const char *file, int line) +{ + unsigned int copy_len = 0; + + if (new_string == NULL) { + log_error("data_string_new: new_string cannot be NULL %s(%d)", + file, line); + return (0); + } + + if (src == NULL) { + log_error("data_string_new: src cannot be NULL %s(%d)", + file, line); + return (0); + } + + memset(new_string, 0, sizeof (struct data_string)); + + /* If we already have a NULL back off length by one. This lets + * us always just add a NULL at the end. */ + copy_len = (len > 0 && src[len - 1 ] == 0) ? len - 1 : len; + + /* Allocate the buffer, accounting for terminating null */ + if (!buffer_allocate(&(new_string->buffer), copy_len + 1, MDL)) { + log_error("data_string_new: No memory %s(%d)", file, line); + return (0); + } + + /* Only copy if there's something to copy */ + if (copy_len > 0) { + memcpy(new_string->buffer->data, src, copy_len); + } + + /* Always tack on the null */ + new_string->buffer->data[copy_len + 1] = 0; + + /* Update data_string accessor values. Note len does NOT include + * the NULL. */ + new_string->data = new_string->buffer->data; + new_string->len = copy_len; + new_string->terminated = 1; + + return (1); +} + /* Make a copy of the data in data_string, upping the buffer reference count if there's a buffer. */ diff --git a/common/tests/test_alloc.c b/common/tests/test_alloc.c index d941c8fb..9e082135 100644 --- a/common/tests/test_alloc.c +++ b/common/tests/test_alloc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007,2009,2012 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 2007,2009-2014 by Internet Systems Consortium, Inc. ("ISC") * * We test the functions provided in alloc.c here. These are very * basic functions, and it is very important that they work correctly. @@ -27,6 +27,8 @@ #include #include "dhcpd.h" +static const char* checkString (struct data_string* ds, const char *src); + ATF_TC(buffer_allocate); ATF_TC_HEAD(buffer_allocate, tc) { @@ -187,7 +189,7 @@ ATF_TC_BODY(buffer_dereference, tc) { if (!buffer_reference(&b, a, MDL)) { atf_tc_fail("buffer_reference() failed"); } - a->refcnt = 0; /* purposely set to invalid value */ + a->refcnt = 0; /* purposely set to invalid value */ if (buffer_dereference(&a, MDL)) { atf_tc_fail("buffer_dereference() succeeded on error input"); } @@ -387,6 +389,103 @@ ATF_TC_BODY(data_string_copy_nobuf, tc) { } + +ATF_TC(data_string_new); + +ATF_TC_HEAD(data_string_new, tc) { + atf_tc_set_md_var(tc, "descr", "data_string_new test, " + "exercises data_string_new function"); +} + +ATF_TC_BODY(data_string_new, tc) { + struct data_string new_string; + const char *src = "Really? Latin? ... geeks"; + int len_arg = 0; + const char *error; + + /* Case 1: Call with an invalid data_string pointer, should fail */ + if (data_string_new(NULL, src, len_arg, MDL)) { + atf_tc_fail("case 1: call should have failed"); + } + + /* Case 2: Passing in NULL src should fail */ + if (data_string_new(&new_string, NULL, 10, MDL)) { + atf_tc_fail("case 2: did not return success"); + } + + /* Case 3: Call with valid params, length includes NULL */ + len_arg = strlen(src) + 1; + if (data_string_new(&new_string, src, len_arg, MDL) == 0) { + atf_tc_fail("case 3: did not return success"); + } + + error = checkString(&new_string, src); + ATF_REQUIRE_MSG((error == NULL), "case 3: %s", error); + data_string_forget(&new_string, MDL); + + + /* Case 4: Call with valid params, length does not include NULL */ + len_arg = 7; + if (data_string_new(&new_string, src, len_arg, MDL) == 0) { + atf_tc_fail("case 4: did not return success"); + } + + error = checkString(&new_string, "Really?"); + ATF_REQUIRE_MSG((error == NULL), "case 4: %s", error); + data_string_forget(&new_string, MDL); + + + /* Case 5: Call with valid params, source string is "" */ + len_arg = 0; + if (data_string_new(&new_string, "", len_arg, MDL) == 0) { + atf_tc_fail("case 5: did not return success"); + } + + error = checkString(&new_string, ""); + ATF_REQUIRE_MSG((error == NULL), "case 4: %s", error); + data_string_forget(&new_string, MDL); + + +} + +/* Helper function which tests validity of a data_string +* +* Verifies that the given data_string contains a null-terminated string +* equal to a given string. +* +* \param string data_string to test +* \param src text content string should contain +* \return returns NULL if data_string is validate or an error message +* describing why it is invalid +*/ +const char* checkString (struct data_string* string, + const char* src) { + int src_len = strlen(src); + + if (string->buffer == NULL) { + return ("buffer is NULL"); + } + + if (string->data != string->buffer->data) { + return ("data not set to buffer->data"); + } + + if (string->len != src_len) { + return ("len is wrong "); + } + + if (string->terminated != 1) { + return ("terminated flag not set"); + } + + if (memcmp(string->data, src, src_len + 1)) { + return ("data content wrong"); + } + + return (NULL); +} + + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, buffer_allocate); @@ -396,6 +495,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, data_string_forget_nobuf); ATF_TP_ADD_TC(tp, data_string_copy); ATF_TP_ADD_TC(tp, data_string_copy_nobuf); + ATF_TP_ADD_TC(tp, data_string_new); return (atf_no_error()); } diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 9ad641be..edc9b89e 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -2384,6 +2384,8 @@ int option_state_reference (struct option_state **, struct option_state *, const char *, int); int option_state_dereference (struct option_state **, const char *, int); +int data_string_new(struct data_string *, const char *, unsigned int, + const char *, int); void data_string_copy(struct data_string *, const struct data_string *, const char *, int); void data_string_forget (struct data_string *, const char *, int); diff --git a/server/ddns.c b/server/ddns.c index 6cbd3e3d..b5827870 100644 --- a/server/ddns.c +++ b/server/ddns.c @@ -221,6 +221,22 @@ ddns_updates(struct packet *packet, struct lease *lease, struct lease *old, else s1 = 0; + /* If we don't have a host name based on ddns-hostname then use + * the host declaration name if there is one and use-host-decl-names + * is turned on. */ + if ((s1 == 0) && (lease && lease->host && lease->host->name)) { + oc = lookup_option(&server_universe, options, + SV_USE_HOST_DECL_NAMES); + if (evaluate_boolean_option_cache(NULL, packet, lease, + NULL, packet->options, + options, scope, oc, MDL)) { + s1 = ((data_string_new(&ddns_hostname, + lease->host->name, + strlen(lease->host->name), + MDL) && ddns_hostname.len > 0)); + } + } + oc = lookup_option(&server_universe, options, SV_DDNS_DOMAIN_NAME); if (oc) s2 = evaluate_option_cache(&ddns_domainname, packet, lease, diff --git a/server/dhcpd.c b/server/dhcpd.c index e0d45e17..6747c209 100644 --- a/server/dhcpd.c +++ b/server/dhcpd.c @@ -63,10 +63,9 @@ int server_identifier_matched; /* This stuff is always executed to figure the default values for certain ddns variables. */ - char std_nsupdate [] = " \n\ option server.ddns-hostname = \n\ - pick (option fqdn.hostname, option host-name); \n\ + pick (option fqdn.hostname, option host-name, config-option host-name); \n\ option server.ddns-domainname = config-option domain-name; \n\ option server.ddns-rev-domainname = \"in-addr.arpa.\";"; diff --git a/server/dhcpd.conf.5 b/server/dhcpd.conf.5 index 2f7eb780..9d7178da 100644 --- a/server/dhcpd.conf.5 +++ b/server/dhcpd.conf.5 @@ -1153,17 +1153,23 @@ IP address, it can update its own A record, assuming that the .PP If the server is configured not to allow client updates, or if the client doesn\'t want to do its own update, the server will simply -choose a name for the client from either the \fBfqdn\fR option (if present) -or the hostname option (if present). It will use its own -domain name for the client. It will then update both the A and PTR -record, using the name that it chose for the client. If the client -sends a fully-qualified domain name in the \fBfqdn\fR option, the -server uses only the leftmost part of the domain name - in the -example above, "jschmoe" instead of "jschmoe.radish.org". +choose a name for the client. By default, the server will choose +from the following three values: .PP -If the defaults for choosing the host name are not appropriate + 1. \fBfqdn\fR option (if present) + 2. hostname option (if present) + 3. Configured hostname option (if defined). +.PP +If these defaults for choosing the host name are not appropriate you can write your own statement to set the ddns-hostname variable -as you wish. +as you wish. If none of the above are found the server will use +the host declaration name (if one) and use-host-decl-names is on. +.PP +It will use its own domain name for the client. It will then update +both the A and PTR record, using the name that it chose for the client. +If the client sends a fully-qualified domain name in the \fBfqdn\fR option, +the server uses only the leftmost part of the domain name - in the example +above, "jschmoe" instead of "jschmoe.radish.org". .PP Further, if the \fIignore client-updates;\fR directive is used, then the server will in addition send a response in the DHCP packet, using @@ -2931,6 +2937,11 @@ is equivalent to } .fi .PP +Additionally, enabling use-host-decl-names instructs the server to use +the host declaration name in the the forward DNS name, if no other values +are available. This value selection process is discussed in more detail +under DNS updates. +.PP An \fIoption host-name\fR statement within a host declaration will override the use of the name in the host declaration. .PP