/* $NetBSD: generic.c,v 1.2.6.1 2024/02/29 11:39:57 martin Exp $ */ /* generic.c Subroutines that support the generic object. */ /* * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC") * Copyright (c) 1999-2003 by Internet Software Consortium * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Internet Systems Consortium, Inc. * PO Box 360 * Newmarket, NH 03857 USA * * https://www.isc.org/ * */ #include __RCSID("$NetBSD: generic.c,v 1.2.6.1 2024/02/29 11:39:57 martin Exp $"); #include "dhcpd.h" #include OMAPI_OBJECT_ALLOC (omapi_generic, omapi_generic_object_t, omapi_type_generic) isc_result_t omapi_generic_new (omapi_object_t **gen, const char *file, int line) { /* Backwards compatibility. */ return omapi_generic_allocate ((omapi_generic_object_t **)gen, file, line); } isc_result_t omapi_generic_set_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_typed_data_t *value) { omapi_generic_object_t *g; omapi_value_t *new; omapi_value_t **va; u_int8_t *ca; int vm_new; int i, vfree = -1; isc_result_t status; if (h -> type != omapi_type_generic) return DHCP_R_INVALIDARG; g = (omapi_generic_object_t *)h; /* See if there's already a value with this name attached to the generic object, and if so, replace the current value with the new one. */ for (i = 0; i < g -> nvalues; i++) { if (!g -> values[i]) continue; if (!omapi_data_string_cmp (name, g -> values [i] -> name)) { /* There's an inconsistency here: the standard behaviour of a set_values method when passed a matching name and a null value is to delete the value associated with that name (where possible). In the generic object, we remember the name/null pair, because generic objects are generally used to pass messages around, and this is the way that remote entities delete values from local objects. If the get_value method of a generic object is called for a name that maps to a name/null pair, ISC_R_NOTFOUND is returned. */ new = (omapi_value_t *)0; status = (omapi_value_new (&new, MDL)); if (status != ISC_R_SUCCESS) return status; omapi_data_string_reference (&new -> name, name, MDL); if (value) omapi_typed_data_reference (&new -> value, value, MDL); omapi_value_dereference (&(g -> values [i]), MDL); status = (omapi_value_reference (&(g -> values [i]), new, MDL)); omapi_value_dereference (&new, MDL); g -> changed [i] = 1; return status; } /* Notice a free slot if we pass one. */ else if (vfree == -1 && !g -> values [i]) vfree = i; } /* If the name isn't already attached to this object, see if an inner object has it. */ if (h -> inner && h -> inner -> type -> set_value) { status = ((*(h -> inner -> type -> set_value)) (h -> inner, id, name, value)); if (status != ISC_R_NOTFOUND) return status; } /* Okay, so it's a value that no inner object knows about, and (implicitly, since the outer object set_value method would have called this object's set_value method) it's an object that no outer object knows about, it's this object's responsibility to remember it - that's what generic objects do. */ /* Arrange for there to be space for the pointer to the new name/value pair if necessary: */ if (vfree == -1) { vfree = g -> nvalues; if (vfree == g -> va_max) { if (g -> va_max) vm_new = 2 * g -> va_max; else vm_new = 10; va = dmalloc (vm_new * sizeof *va, MDL); if (!va) return ISC_R_NOMEMORY; ca = dmalloc (vm_new * sizeof *ca, MDL); if (!ca) { dfree (va, MDL); return ISC_R_NOMEMORY; } if (g -> va_max) { memcpy (va, g -> values, g -> va_max * sizeof *va); memcpy (ca, g -> changed, g -> va_max * sizeof *ca); } memset (va + g -> va_max, 0, (vm_new - g -> va_max) * sizeof *va); memset (ca + g -> va_max, 0, (vm_new - g -> va_max) * sizeof *ca); if (g -> values) dfree (g -> values, MDL); if (g -> changed) dfree (g -> changed, MDL); g -> values = va; g -> changed = ca; g -> va_max = vm_new; } } status = omapi_value_new (&g -> values [vfree], MDL); if (status != ISC_R_SUCCESS) return status; omapi_data_string_reference (&g -> values [vfree] -> name, name, MDL); if (value) omapi_typed_data_reference (&g -> values [vfree] -> value, value, MDL); g -> changed [vfree] = 1; if (vfree == g -> nvalues) g -> nvalues++; return ISC_R_SUCCESS; } isc_result_t omapi_generic_get_value (omapi_object_t *h, omapi_object_t *id, omapi_data_string_t *name, omapi_value_t **value) { int i; omapi_generic_object_t *g; if (h -> type != omapi_type_generic) return DHCP_R_INVALIDARG; g = (omapi_generic_object_t *)h; /* Look up the specified name in our list of objects. */ for (i = 0; i < g -> nvalues; i++) { if (!g -> values[i]) continue; if (!omapi_data_string_cmp (name, g -> values [i] -> name)) { /* If this is a name/null value pair, this is the same as if there were no value that matched the specified name, so return ISC_R_NOTFOUND. */ if (!g -> values [i] -> value) return ISC_R_NOTFOUND; /* Otherwise, return the name/value pair. */ return omapi_value_reference (value, g -> values [i], MDL); } } if (h -> inner && h -> inner -> type -> get_value) return (*(h -> inner -> type -> get_value)) (h -> inner, id, name, value); return ISC_R_NOTFOUND; } isc_result_t omapi_generic_destroy (omapi_object_t *h, const char *file, int line) { omapi_generic_object_t *g; int i; if (h -> type != omapi_type_generic) return ISC_R_UNEXPECTED; g = (omapi_generic_object_t *)h; if (g -> values) { for (i = 0; i < g -> nvalues; i++) { if (g -> values [i]) omapi_value_dereference (&g -> values [i], file, line); } dfree (g -> values, file, line); dfree (g -> changed, file, line); g -> values = (omapi_value_t **)0; g -> changed = (u_int8_t *)0; g -> va_max = 0; } return ISC_R_SUCCESS; } isc_result_t omapi_generic_signal_handler (omapi_object_t *h, const char *name, va_list ap) { if (h -> type != omapi_type_generic) return DHCP_R_INVALIDARG; if (h -> inner && h -> inner -> type -> signal_handler) return (*(h -> inner -> type -> signal_handler)) (h -> inner, name, ap); return ISC_R_NOTFOUND; } /* Write all the published values associated with the object through the specified connection. */ isc_result_t omapi_generic_stuff_values (omapi_object_t *c, omapi_object_t *id, omapi_object_t *g) { omapi_generic_object_t *src; int i; isc_result_t status; if (g -> type != omapi_type_generic) return DHCP_R_INVALIDARG; src = (omapi_generic_object_t *)g; for (i = 0; i < src -> nvalues; i++) { if (src -> values [i] && src -> values [i] -> name -> len && src -> changed [i]) { status = (omapi_connection_put_uint16 (c, src -> values [i] -> name -> len)); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_copyin (c, src -> values [i] -> name -> value, src -> values [i] -> name -> len)); if (status != ISC_R_SUCCESS) return status; status = (omapi_connection_write_typed_data (c, src -> values [i] -> value)); if (status != ISC_R_SUCCESS) return status; } } if (g -> inner && g -> inner -> type -> stuff_values) return (*(g -> inner -> type -> stuff_values)) (c, id, g -> inner); return ISC_R_SUCCESS; } /* Clear the changed flags on the object. This has the effect that if generic_stuff is called, any attributes that still have a cleared changed flag aren't sent to the peer. This also deletes any values that are null, presuming that these have now been properly handled. */ isc_result_t omapi_generic_clear_flags (omapi_object_t *o) { int i; omapi_generic_object_t *g; if (o -> type != omapi_type_generic) return DHCP_R_INVALIDARG; g = (omapi_generic_object_t *)o; for (i = 0; i < g -> nvalues; i++) { g -> changed [i] = 0; if (g -> values [i] && !g -> values [i] -> value) omapi_value_dereference (&g -> values [i], MDL); } return ISC_R_SUCCESS; }