/* $NetBSD: generic.c,v 1.2 2018/04/07 22:37:30 christos Exp $ */
/* generic.c
Subroutines that support the generic object. */
/*
* Copyright (c) 2004-2017 by 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.
* 950 Charter Street
* Redwood City, CA 94063
* <info@isc.org>
* https://www.isc.org/
*
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: generic.c,v 1.2 2018/04/07 22:37:30 christos Exp $");
#include "dhcpd.h"
#include <omapip/omapip_p.h>
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;
}