ATF_TC_BODY(buffer_reference, tc) { struct buffer *a, *b; /* * Create a buffer. */ a = NULL; if (!buffer_allocate(&a, 100, MDL)) { atf_tc_fail("failed on allocate 100 bytes"); } /** * Confirm buffer_reference() doesn't work if we pass in NULL. * * @TODO: we should confirm we get an error message here. */ if (buffer_reference(NULL, a, MDL)) { atf_tc_fail("succeeded on an error input"); } /** * @TODO: we should confirm we get an error message if we pass * a non-NULL target. */ /* * Confirm we work under normal circumstances. */ b = NULL; if (!buffer_reference(&b, a, MDL)) { atf_tc_fail("buffer_reference() failed"); } if (b != a) { atf_tc_fail("incorrect pointer returned"); } if (b->refcnt != 2) { atf_tc_fail("incorrect refcnt"); } /* * Clean up. */ if (!buffer_dereference(&b, MDL)) { atf_tc_fail("buffer_dereference() failed"); } if (!buffer_dereference(&a, MDL)) { atf_tc_fail("buffer_dereference() failed"); } }
ATF_TC_BODY(data_string_forget, tc) { struct buffer *buf; struct data_string a; const char *str = "Lorem ipsum dolor sit amet turpis duis."; /* * Create the string we want to forget. */ memset(&a, 0, sizeof(a)); a.len = strlen(str); buf = NULL; if (!buffer_allocate(&buf, a.len, MDL)) { atf_tc_fail("out of memory"); } if (!buffer_reference(&a.buffer, buf, MDL)) { atf_tc_fail("buffer_reference() failed"); } a.data = a.buffer->data; memcpy(a.buffer->data, str, a.len); /* * Forget and confirm we've forgotten. */ data_string_forget(&a, MDL); if (a.len != 0) { atf_tc_fail("incorrect length"); } if (a.data != NULL) { atf_tc_fail("incorrect data"); } if (a.terminated) { atf_tc_fail("incorrect terminated"); } if (a.buffer != NULL) { atf_tc_fail("incorrect buffer"); } if (buf->refcnt != 1) { atf_tc_fail("too many references to buf"); } /* * Clean up buffer. */ if (!buffer_dereference(&buf, MDL)) { atf_tc_fail("buffer_reference() failed"); } }
ATF_TC_BODY(buffer_allocate, tc) { struct buffer *buf = 0; /* * Check a 0-length buffer. */ buf = NULL; if (!buffer_allocate(&buf, 0, MDL)) { atf_tc_fail("failed on 0-len buffer"); } if (!buffer_dereference(&buf, MDL)) { atf_tc_fail("buffer_dereference() failed"); } if (buf != NULL) { atf_tc_fail("buffer_dereference() did not NULL-out buffer"); } /* * Check an actual buffer. */ buf = NULL; if (!buffer_allocate(&buf, 100, MDL)) { atf_tc_fail("failed on allocate 100 bytes\n"); } if (!buffer_dereference(&buf, MDL)) { atf_tc_fail("buffer_dereference() failed"); } if (buf != NULL) { atf_tc_fail("buffer_dereference() did not NULL-out buffer"); } /* * Okay, we're happy. */ atf_tc_pass(); }
int ddns_updates (struct packet *packet, struct lease *lease, struct lease *old, struct lease_state *state) { unsigned long ddns_ttl = DEFAULT_DDNS_TTL; struct data_string ddns_hostname; struct data_string ddns_domainname; struct data_string old_ddns_fwd_name; struct data_string ddns_fwd_name; struct data_string ddns_rev_name; struct data_string ddns_dhcid; unsigned len; struct data_string d1; struct option_cache *oc; int s1, s2; int result = 0; isc_result_t rcode1 = ISC_R_SUCCESS, rcode2 = ISC_R_SUCCESS; int server_updates_a = 1; int server_updates_ptr = 1; struct buffer *bp = (struct buffer *)0; int ignorep = 0, client_ignorep = 0; if (ddns_update_style != 2) return 0; /* Can only cope with IPv4 addrs at the moment. */ if (lease -> ip_addr . len != 4) return 0; memset(&d1, 0, sizeof(d1)); memset (&ddns_hostname, 0, sizeof (ddns_hostname)); memset (&ddns_domainname, 0, sizeof (ddns_domainname)); memset (&old_ddns_fwd_name, 0, sizeof (ddns_fwd_name)); memset (&ddns_fwd_name, 0, sizeof (ddns_fwd_name)); memset (&ddns_rev_name, 0, sizeof (ddns_rev_name)); memset (&ddns_dhcid, 0, sizeof (ddns_dhcid)); /* If we are allowed to accept the client's update of its own A record, see if the client wants to update its own A record. */ if (!(oc = lookup_option(&server_universe, state->options, SV_CLIENT_UPDATES)) || evaluate_boolean_option_cache(&client_ignorep, packet, lease, NULL, packet->options, state->options, &lease->scope, oc, MDL)) { /* If there's no fqdn.no-client-update or if it's nonzero, don't try to use the client-supplied XXX */ if (!(oc = lookup_option (&fqdn_universe, packet -> options, FQDN_SERVER_UPDATE)) || evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) goto noclient; /* Win98 and Win2k will happily claim to be willing to update an unqualified domain name. */ if (!(oc = lookup_option (&fqdn_universe, packet -> options, FQDN_DOMAINNAME))) goto noclient; if (!(oc = lookup_option (&fqdn_universe, packet -> options, FQDN_FQDN)) || !evaluate_option_cache (&ddns_fwd_name, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) goto noclient; server_updates_a = 0; goto client_updates; } noclient: /* If do-forward-updates is disabled, this basically means don't do an update unless the client is participating, so if we get here and do-forward-updates is disabled, we can stop. */ if ((oc = lookup_option (&server_universe, state -> options, SV_DO_FORWARD_UPDATES)) && !evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { return 0; } /* If it's a static lease, then don't do the DNS update unless we're specifically configured to do so. If the client asked to do its own update and we allowed that, we don't do this test. */ if (lease -> flags & STATIC_LEASE) { if (!(oc = lookup_option (&server_universe, state -> options, SV_UPDATE_STATIC_LEASES)) || !evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) return 0; } /* * Compute the name for the A record. */ oc = lookup_option (&server_universe, state -> options, SV_DDNS_HOST_NAME); if (oc) s1 = evaluate_option_cache (&ddns_hostname, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL); else s1 = 0; oc = lookup_option (&server_universe, state -> options, SV_DDNS_DOMAIN_NAME); if (oc) s2 = evaluate_option_cache (&ddns_domainname, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL); else s2 = 0; if (s1 && s2) { if (ddns_hostname.len + ddns_domainname.len > 253) { log_error ("ddns_update: host.domain name too long"); goto out; } buffer_allocate (&ddns_fwd_name.buffer, ddns_hostname.len + ddns_domainname.len + 2, MDL); if (ddns_fwd_name.buffer) { ddns_fwd_name.data = ddns_fwd_name.buffer -> data; data_string_append (&ddns_fwd_name, &ddns_hostname); ddns_fwd_name.buffer -> data [ddns_fwd_name.len] = '.'; ddns_fwd_name.len++; data_string_append (&ddns_fwd_name, &ddns_domainname); ddns_fwd_name.buffer -> data [ddns_fwd_name.len] ='\0'; ddns_fwd_name.terminated = 1; } } client_updates: /* See if there's a name already stored on the lease. */ if (find_bound_string (&old_ddns_fwd_name, lease -> scope, "ddns-fwd-name")) { /* If there is, see if it's different. */ if (old_ddns_fwd_name.len != ddns_fwd_name.len || memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data, old_ddns_fwd_name.len)) { /* If the name is different, try to delete the old A record. */ if (!ddns_removals (lease)) goto out; /* If the delete succeeded, go install the new record. */ goto in; } /* See if there's a DHCID on the lease. */ if (!find_bound_string (&ddns_dhcid, lease -> scope, "ddns-txt")) { /* If there's no DHCID, the update was probably done with the old-style ad-hoc DDNS updates. So if the expiry and release events look like they're the same, run them. This should delete the old DDNS data. */ if (old -> on_expiry == old -> on_release) { execute_statements ((struct binding_value **)0, (struct packet *)0, lease, (struct client_state *)0, (struct option_state *)0, (struct option_state *)0, &lease -> scope, old -> on_expiry); if (old -> on_expiry) executable_statement_dereference (&old -> on_expiry, MDL); if (old -> on_release) executable_statement_dereference (&old -> on_release, MDL); /* Now, install the DDNS data the new way. */ goto in; } } else data_string_forget(&ddns_dhcid, MDL); /* See if the administrator wants to do updates even in cases where the update already appears to have been done. */ if (!(oc = lookup_option (&server_universe, state -> options, SV_UPDATE_OPTIMIZATION)) || evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { result = 1; goto noerror; } /* If there's no "ddns-fwd-name" on the lease record, see if * there's a ddns-client-fqdn indicating a previous client * update (if it changes, we need to adjust the PTR). */ } else if (find_bound_string(&old_ddns_fwd_name, lease->scope, "ddns-client-fqdn")) { /* If the name is not different, no need to update the PTR record. */ if (old_ddns_fwd_name.len == ddns_fwd_name.len && !memcmp (old_ddns_fwd_name.data, ddns_fwd_name.data, old_ddns_fwd_name.len) && (!(oc = lookup_option (&server_universe, state -> options, SV_UPDATE_OPTIMIZATION)) || evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL))) { goto noerror; } } in: /* If we don't have a name that the client has been assigned, we can just skip all this. */ if (!ddns_fwd_name.len) goto out; if (ddns_fwd_name.len > 255) { log_error ("client provided fqdn: too long"); goto out; } /* * Compute the RR TTL. */ ddns_ttl = DEFAULT_DDNS_TTL; if ((oc = lookup_option (&server_universe, state -> options, SV_DDNS_TTL))) { if (evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { if (d1.len == sizeof (u_int32_t)) ddns_ttl = getULong (d1.data); data_string_forget (&d1, MDL); } } /* CC: see if we are configured NOT to do reverse ptr updates */ if ((oc = lookup_option (&server_universe, state -> options, SV_DO_REVERSE_UPDATES)) && !evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL)) { server_updates_ptr = 0; } /* * Compute the reverse IP name. */ oc = lookup_option (&server_universe, state -> options, SV_DDNS_REV_DOMAIN_NAME); if (oc) s1 = evaluate_option_cache (&d1, packet, lease, (struct client_state *)0, packet -> options, state -> options, &lease -> scope, oc, MDL); else s1 = 0; if (s1 && (d1.len > 238)) { log_error ("ddns_update: Calculated rev domain name too long."); s1 = 0; data_string_forget (&d1, MDL); } if (oc && s1) { /* Buffer length: XXX.XXX.XXX.XXX.<ddns-rev-domain-name>\0 */ buffer_allocate (&ddns_rev_name.buffer, d1.len + 17, MDL); if (ddns_rev_name.buffer) { ddns_rev_name.data = ddns_rev_name.buffer -> data; /* %Audit% Cannot exceed 17 bytes. %2004.06.17,Safe% */ sprintf ((char *)ddns_rev_name.buffer -> data, "%u.%u.%u.%u.", lease -> ip_addr . iabuf[3] & 0xff, lease -> ip_addr . iabuf[2] & 0xff, lease -> ip_addr . iabuf[1] & 0xff, lease -> ip_addr . iabuf[0] & 0xff); ddns_rev_name.len = strlen ((const char *)ddns_rev_name.data); data_string_append (&ddns_rev_name, &d1); ddns_rev_name.buffer -> data [ddns_rev_name.len] ='\0'; ddns_rev_name.terminated = 1; } data_string_forget (&d1, MDL); } /* * If we are updating the A record, compute the DHCID value. */ if (server_updates_a) { if (lease -> uid && lease -> uid_len) result = get_dhcid (&ddns_dhcid, DHO_DHCP_CLIENT_IDENTIFIER, lease -> uid, lease -> uid_len); else result = get_dhcid (&ddns_dhcid, 0, lease -> hardware_addr.hbuf, lease -> hardware_addr.hlen); if (!result) goto badfqdn; } /* * Start the resolver, if necessary. */ if (!resolver_inited) { minires_ninit (&resolver_state); resolver_inited = 1; resolver_state.retrans = 1; resolver_state.retry = 1; } /* * Perform updates. */ if (ddns_fwd_name.len && ddns_dhcid.len) { unsigned conflict; oc = lookup_option(&server_universe, state->options, SV_DDNS_CONFLICT_DETECT); if (!oc || evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, state->options, &lease->scope, oc, MDL)) conflict = 1; else conflict = 0; rcode1 = ddns_update_a (&ddns_fwd_name, lease -> ip_addr, &ddns_dhcid, ddns_ttl, 0, conflict); } if (rcode1 == ISC_R_SUCCESS && server_updates_ptr) { if (ddns_fwd_name.len && ddns_rev_name.len) rcode2 = ddns_update_ptr (&ddns_fwd_name, &ddns_rev_name, ddns_ttl); } else rcode2 = rcode1; if (rcode1 == ISC_R_SUCCESS && (server_updates_a || rcode2 == ISC_R_SUCCESS)) { bind_ds_value (&lease -> scope, (server_updates_a ? "ddns-fwd-name" : "ddns-client-fqdn"), &ddns_fwd_name); if (server_updates_a) bind_ds_value (&lease -> scope, "ddns-txt", &ddns_dhcid); } if (rcode2 == ISC_R_SUCCESS && server_updates_ptr) { bind_ds_value (&lease -> scope, "ddns-rev-name", &ddns_rev_name); } noerror: /* * If fqdn-reply option is disabled in dhcpd.conf, then don't * send the client an FQDN option at all, even if one was requested. * (WinXP clients allegedly misbehave if the option is present, * refusing to handle PTR updates themselves). */ if ((oc = lookup_option (&server_universe, state->options, SV_FQDN_REPLY)) && !evaluate_boolean_option_cache (&ignorep, packet, lease, (struct client_state *)0, packet->options, state->options, &lease->scope, oc, MDL)) { goto badfqdn; /* If we're ignoring client updates, then we tell a sort of 'white * lie'. We've already updated the name the server wants (per the * config written by the server admin). Now let the client do as * it pleases with the name they supplied (if any). * * We only form an FQDN option this way if the client supplied an * FQDN option that had FQDN_SERVER_UPDATE set false. */ } else if (client_ignorep && (oc = lookup_option(&fqdn_universe, packet->options, FQDN_SERVER_UPDATE)) && !evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, state->options, &lease->scope, oc, MDL)) { oc = lookup_option(&fqdn_universe, packet->options, FQDN_FQDN); if (oc && evaluate_option_cache(&d1, packet, lease, NULL, packet->options, state->options, &global_scope, oc, MDL)) { if (d1.len == 0 || !buffer_allocate(&bp, d1.len + 5, MDL)) goto badfqdn; /* Server pretends it is not updating. */ bp->data[0] = 0; if (!save_option_buffer(&fqdn_universe, state->options, bp, &bp->data[0], 1, FQDN_SERVER_UPDATE, 0)) goto badfqdn; /* Client is encouraged to update. */ bp->data[1] = 0; if (!save_option_buffer(&fqdn_universe, state->options, bp, &bp->data[1], 1, FQDN_NO_CLIENT_UPDATE, 0)) goto badfqdn; /* Use the encoding of client's FQDN option. */ oc = lookup_option(&fqdn_universe, packet->options, FQDN_ENCODED); if (oc && evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, state->options, &lease->scope, oc, MDL)) bp->data[2] = 1; /* FQDN is encoded. */ else bp->data[2] = 0; /* FQDN is not encoded. */ if (!save_option_buffer(&fqdn_universe, state->options, bp, &bp->data[2], 1, FQDN_ENCODED, 0)) goto badfqdn; /* Current FQDN drafts indicate 255 is mandatory. */ bp->data[3] = 255; if (!save_option_buffer(&fqdn_universe, state->options, bp, &bp->data[3], 1, FQDN_RCODE1, 0)) goto badfqdn; bp->data[4] = 255; if (!save_option_buffer(&fqdn_universe, state->options, bp, &bp->data[4], 1, FQDN_RCODE2, 0)) goto badfqdn; /* Copy in the FQDN supplied by the client. Note well * that the format of this option in the cache is going * to be in text format. If the fqdn supplied by the * client is encoded, it is decoded into the option * cache when parsed out of the packet. It will be * re-encoded when the option is assembled to be * transmitted if the client elects that encoding. */ memcpy(&bp->data[5], d1.data, d1.len); if (!save_option_buffer(&fqdn_universe, state->options, bp, &bp->data[5], 1, FQDN_FQDN, 0)) goto badfqdn; data_string_forget(&d1, MDL); } /* Set up the outgoing FQDN option if there was an incoming * FQDN option. If there's a valid FQDN option, there MUST * be an FQDN_SERVER_UPDATES suboption, it's part of the fixed * length head of the option contents, so we test the latter * to detect the presence of the former. */ } else if ((oc = lookup_option(&fqdn_universe, packet->options, FQDN_ENCODED)) && buffer_allocate(&bp, ddns_fwd_name.len + 5, MDL)) { bp -> data [0] = server_updates_a; if (!save_option_buffer (&fqdn_universe, state -> options, bp, &bp -> data [0], 1, FQDN_SERVER_UPDATE, 0)) goto badfqdn; bp -> data [1] = server_updates_a; if (!save_option_buffer (&fqdn_universe, state -> options, bp, &bp -> data [1], 1, FQDN_NO_CLIENT_UPDATE, 0)) goto badfqdn; /* Do the same encoding the client did. */ if (evaluate_boolean_option_cache(&ignorep, packet, lease, NULL, packet->options, state->options, &lease->scope, oc, MDL)) bp -> data [2] = 1; else bp -> data [2] = 0; if (!save_option_buffer (&fqdn_universe, state -> options, bp, &bp -> data [2], 1, FQDN_ENCODED, 0)) goto badfqdn; bp -> data [3] = isc_rcode_to_ns (rcode1); if (!save_option_buffer (&fqdn_universe, state -> options, bp, &bp -> data [3], 1, FQDN_RCODE1, 0)) goto badfqdn; bp -> data [4] = isc_rcode_to_ns (rcode2); if (!save_option_buffer (&fqdn_universe, state -> options, bp, &bp -> data [4], 1, FQDN_RCODE2, 0)) goto badfqdn; if (ddns_fwd_name.len) { memcpy (&bp -> data [5], ddns_fwd_name.data, ddns_fwd_name.len); if (!save_option_buffer (&fqdn_universe, state -> options, bp, &bp -> data [5], ddns_fwd_name.len, FQDN_FQDN, 0)) goto badfqdn; } } badfqdn: out: /* * Final cleanup. */ data_string_forget(&d1, MDL); data_string_forget(&ddns_hostname, MDL); data_string_forget(&ddns_domainname, MDL); data_string_forget(&old_ddns_fwd_name, MDL); data_string_forget(&ddns_fwd_name, MDL); data_string_forget(&ddns_rev_name, MDL); data_string_forget(&ddns_dhcid, MDL); if (bp) buffer_dereference(&bp, MDL); return result; }
ATF_TC_BODY(buffer_dereference, tc) { struct buffer *a, *b; /** * Confirm buffer_dereference() doesn't work if we pass in NULL. * * TODO: we should confirm we get an error message here. */ if (buffer_dereference(NULL, MDL)) { atf_tc_fail("succeeded on an error input"); } /** * Confirm buffer_dereference() doesn't work if we pass in * a pointer to NULL. * * @TODO: we should confirm we get an error message here. */ a = NULL; if (buffer_dereference(&a, MDL)) { atf_tc_fail("succeeded on an error input"); } /* * Confirm we work under normal circumstances. */ a = NULL; if (!buffer_allocate(&a, 100, MDL)) { atf_tc_fail("failed on allocate"); } if (!buffer_dereference(&a, MDL)) { atf_tc_fail("buffer_dereference() failed"); } if (a != NULL) { atf_tc_fail("non-null buffer after buffer_dereference()"); } /** * Confirm we get an error from negative refcnt. * * @TODO: we should confirm we get an error message here. */ a = NULL; if (!buffer_allocate(&a, 100, MDL)) { atf_tc_fail("failed on allocate"); } b = NULL; if (!buffer_reference(&b, a, MDL)) { atf_tc_fail("buffer_reference() failed"); } a->refcnt = 0; /* purposely set to invalid value */ if (buffer_dereference(&a, MDL)) { atf_tc_fail("buffer_dereference() succeeded on error input"); } a->refcnt = 2; if (!buffer_dereference(&b, MDL)) { atf_tc_fail("buffer_dereference() failed"); } if (!buffer_dereference(&a, MDL)) { atf_tc_fail("buffer_dereference() failed"); } }