From f3ff03fc4892a162a0fb4a0e940615092237dadb Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Wed, 27 Oct 1999 22:24:32 +0000 Subject: [PATCH] initial copy from the DHCP source pool --- bin/tests/omapi_test.c | 85 +++++ lib/omapi/alloc.c | 451 +++++++++++++++++++++++ lib/omapi/buffer.c | 499 +++++++++++++++++++++++++ lib/omapi/connection.c | 337 +++++++++++++++++ lib/omapi/dispatch.c | 402 ++++++++++++++++++++ lib/omapi/generic.c | 251 +++++++++++++ lib/omapi/handle.c | 283 ++++++++++++++ lib/omapi/listener.c | 254 +++++++++++++ lib/omapi/message.c | 675 ++++++++++++++++++++++++++++++++++ lib/omapi/protocol.c | 817 +++++++++++++++++++++++++++++++++++++++++ lib/omapi/result.c | 81 ++++ lib/omapi/support.c | 682 ++++++++++++++++++++++++++++++++++ lib/omapi/test.c | 85 +++++ 13 files changed, 4902 insertions(+) create mode 100644 bin/tests/omapi_test.c create mode 100644 lib/omapi/alloc.c create mode 100644 lib/omapi/buffer.c create mode 100644 lib/omapi/connection.c create mode 100644 lib/omapi/dispatch.c create mode 100644 lib/omapi/generic.c create mode 100644 lib/omapi/handle.c create mode 100644 lib/omapi/listener.c create mode 100644 lib/omapi/message.c create mode 100644 lib/omapi/protocol.c create mode 100644 lib/omapi/result.c create mode 100644 lib/omapi/support.c create mode 100644 lib/omapi/test.c diff --git a/bin/tests/omapi_test.c b/bin/tests/omapi_test.c new file mode 100644 index 0000000000..d8065d9417 --- /dev/null +++ b/bin/tests/omapi_test.c @@ -0,0 +1,85 @@ +/* test.c + + Test code for omapip... */ + +/* + * Copyright (c) 1996-1999 Internet Software Consortium. + * Use is subject to license terms which appear in the file named + * ISC-LICENSE that should have accompanied this file when you + * received it. If a file named ISC-LICENSE did not accompany this + * file, or you are not sure the one you have is correct, you may + * obtain an applicable copy of the license at: + * + * http://www.isc.org/isc-license-1.0.html. + * + * This file is part of the ISC DHCP distribution. The documentation + * associated with this file is listed in the file DOCUMENTATION, + * included in the top-level directory of this release. + * + * Support and other services are available for ISC products - see + * http://www.isc.org for more information. + */ + +#include +#include +#include +#include +#include +#include + +int main (int argc, char **argv) +{ + omapi_object_t *listener = (omapi_object_t*)0; + omapi_object_t *connection = (omapi_object_t*)0; + isc_result_t status; + + omapi_init (); + + if (argc > 1 && !strcmp (argv [1], "listen")) { + if (argc < 3) { + fprintf (stderr, "Usage: test listen port\n"); + exit (1); + } + status = omapi_generic_new (&listener, "main"); + if (status != ISC_R_SUCCESS) { + fprintf (stderr, "omapi_generic_new: %s\n", + isc_result_totext (status)); + exit (1); + } + status = omapi_protocol_listen (listener, + atoi (argv [2]), 1); + if (status != ISC_R_SUCCESS) { + fprintf (stderr, "omapi_listen: %s\n", + isc_result_totext (status)); + exit (1); + } + omapi_dispatch (0); + } else if (argc > 1 && !strcmp (argv [1], "connect")) { + if (argc < 4) { + fprintf (stderr, "Usage: test listen address port\n"); + exit (1); + } + status = omapi_generic_new (&connection, "main"); + if (status != ISC_R_SUCCESS) { + fprintf (stderr, "omapi_generic_new: %s\n", + isc_result_totext (status)); + exit (1); + } + status = omapi_protocol_connect (connection, + argv [2], atoi (argv [3]), 0); + fprintf (stderr, "connect: %s\n", isc_result_totext (status)); + if (status != ISC_R_SUCCESS) + exit (1); + status = omapi_wait_for_completion (connection, 0); + fprintf (stderr, "completion: %s\n", + isc_result_totext (status)); + if (status != ISC_R_SUCCESS) + exit (1); + /* ... */ + } else { + fprintf (stderr, "Usage: test [listen | connect] ...\n"); + exit (1); + } + + return 0; +} diff --git a/lib/omapi/alloc.c b/lib/omapi/alloc.c new file mode 100644 index 0000000000..11b55b562a --- /dev/null +++ b/lib/omapi/alloc.c @@ -0,0 +1,451 @@ +/* alloc.c + + Functions supporting memory allocation for the object management + protocol... */ + +/* + * Copyright (c) 1996-1999 Internet Software Consortium. + * Use is subject to license terms which appear in the file named + * ISC-LICENSE that should have accompanied this file when you + * received it. If a file named ISC-LICENSE did not accompany this + * file, or you are not sure the one you have is correct, you may + * obtain an applicable copy of the license at: + * + * http://www.isc.org/isc-license-1.0.html. + * + * This file is part of the ISC DHCP distribution. The documentation + * associated with this file is listed in the file DOCUMENTATION, + * included in the top-level directory of this release. + * + * Support and other services are available for ISC products - see + * http://www.isc.org for more information. + */ + +#include + +isc_result_t omapi_object_reference (omapi_object_t **r, + omapi_object_t *h, + const char *name) +{ + if (!h || !r) + return ISC_R_INVALIDARG; + + if (*r) { +#if defined (ALLOCATION_DEBUGGING) + abort ("%s: reference store into non-null pointer!", name); +#else + return ISC_R_INVALIDARG; +#endif + } + *r = h; + h -> refcnt++; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_object_dereference (omapi_object_t **h, + const char *name) +{ + int outer_reference = 0; + int inner_reference = 0; + int handle_reference = 0; + int extra_references; + omapi_object_t *p; + + if (!h) + return ISC_R_INVALIDARG; + + if (!*h) { +#if defined (ALLOCATION_DEBUGGING) + abort ("%s: dereference of null pointer!", name); +#else + return ISC_R_INVALIDARG; +#endif + } + + if ((*h) -> refcnt <= 0) { +#if defined (ALLOCATION_DEBUGGING) + abort ("dereference of pointer with refcnt of zero!"); +#else + return ISC_R_INVALIDARG; +#endif + } + + /* See if this object's inner object refers to it, but don't + count this as a reference if we're being asked to free the + reference from the inner object. */ + if ((*h) -> inner && (*h) -> inner -> outer && + h != &((*h) -> inner -> outer)) + inner_reference = 1; + + /* Ditto for the outer object. */ + if ((*h) -> outer && (*h) -> outer -> inner && + h != &((*h) -> outer -> inner)) + outer_reference = 1; + + /* Ditto for the outer object. The code below assumes that + the only reason we'd get a dereference from the handle + table is if this function does it - otherwise we'd have to + traverse the handle table to find the address where the + reference is stored and compare against that, and we don't + want to do that if we can avoid it. */ + if ((*h) -> handle) + handle_reference = 1; + + /* If we are getting rid of the last reference other than + references to inner and outer objects, or from the handle + table, then we must examine all the objects in either + direction to see if they hold any non-inner, non-outer, + non-handle-table references. If not, we need to free the + entire chain of objects. */ + if ((*h) -> refcnt == + inner_reference + outer_reference + handle_reference + 1) { + if (inner_reference || outer_reference || handle_reference) { + /* XXX we could check for a reference from the + handle table here. */ + extra_references = 0; + for (p = (*h) -> inner; + p && !extra_references; p = p -> inner) { + extra_references += p -> refcnt - 1; + if (p -> inner) + --extra_references; + if (p -> handle) + --extra_references; + } + for (p = (*h) -> outer; + p && !extra_references; p = p -> outer) { + extra_references += p -> refcnt - 1; + if (p -> outer) + --extra_references; + if (p -> handle) + --extra_references; + } + } else + extra_references = 0; + + if (!extra_references) { + if (inner_reference) + omapi_object_dereference + (&(*h) -> inner -> outer, name); + if (outer_reference) + omapi_object_dereference + (&(*h) -> outer -> inner, name); + if ((*h) -> type -> destroy) + (*((*h) -> type -> destroy)) (*h, name); + free (*h); + } + } + *h = 0; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_buffer_new (omapi_buffer_t **h, + const char *name) +{ + omapi_buffer_t *t; + isc_result_t status; + + t = (omapi_buffer_t *)malloc (sizeof *t); + if (!t) + return ISC_R_NOMEMORY; + memset (t, 0, sizeof *t); + status = omapi_buffer_reference (h, t, name); + if (status != ISC_R_SUCCESS) + free (t); + (*h) -> head = sizeof ((*h) -> buf) - 1; + return status; +} + +isc_result_t omapi_buffer_reference (omapi_buffer_t **r, + omapi_buffer_t *h, + const char *name) +{ + if (!h || !r) + return ISC_R_INVALIDARG; + + if (*r) { +#if defined (ALLOCATION_DEBUGGING) + abort ("%s: reference store into non-null pointer!", name); +#else + return ISC_R_INVALIDARG; +#endif + } + *r = h; + h -> refcnt++; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_buffer_dereference (omapi_buffer_t **h, + const char *name) +{ + if (!h) + return ISC_R_INVALIDARG; + + if (!*h) { +#if defined (ALLOCATION_DEBUGGING) + abort ("%s: dereference of null pointer!", name); +#else + return ISC_R_INVALIDARG; +#endif + } + + if ((*h) -> refcnt <= 0) { +#if defined (ALLOCATION_DEBUGGING) + abort ("dereference of pointer with refcnt of zero!", name); +#else + return ISC_R_INVALIDARG; +#endif + } + if (--(*h) -> refcnt == 0) + free (*h); + *h = 0; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_typed_data_new (omapi_typed_data_t **t, + omapi_datatype_t type, ...) +{ + va_list l; + omapi_typed_data_t *new; + unsigned len; + unsigned val; + int intval; + char *s; + isc_result_t status; + + va_start (l, type); + + switch (type) { + case omapi_datatype_int: + len = OMAPI_TYPED_DATA_INT_LEN; + intval = va_arg (l, int); + break; + case omapi_datatype_string: + s = va_arg (l, char *); + val = strlen (s); + len = OMAPI_TYPED_DATA_NOBUFFER_LEN + val; + break; + case omapi_datatype_data: + val = va_arg (l, unsigned); + len = OMAPI_TYPED_DATA_NOBUFFER_LEN + val; + break; + case omapi_datatype_object: + len = OMAPI_TYPED_DATA_OBJECT_LEN; + break; + default: + return ISC_R_INVALIDARG; + } + + new = malloc (len); + if (!new) + return ISC_R_NOMEMORY; + memset (new, 0, len); + + switch (type) { + case omapi_datatype_int: + new -> u.integer = intval; + break; + case omapi_datatype_string: + memcpy (new -> u.buffer.value, s, val); + new -> u.buffer.len = val; + break; + case omapi_datatype_data: + new -> u.buffer.len = val; + break; + case omapi_datatype_object: + status = omapi_object_reference (&new -> u.object, + va_arg (l, omapi_object_t *), + "omapi_datatype_new"); + if (status != ISC_R_SUCCESS) { + free (new); + return status; + } + break; + } + new -> type = type; + return omapi_typed_data_reference (t, new, "omapi_typed_data_new"); +} + +isc_result_t omapi_typed_data_reference (omapi_typed_data_t **r, + omapi_typed_data_t *h, + const char *name) +{ + if (!h || !r) + return ISC_R_INVALIDARG; + + if (*r) { +#if defined (ALLOCATION_DEBUGGING) + abort ("%s: reference store into non-null pointer!", name); +#else + return ISC_R_INVALIDARG; +#endif + } + *r = h; + h -> refcnt++; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_typed_data_dereference (omapi_typed_data_t **h, + const char *name) +{ + if (!h) + return ISC_R_INVALIDARG; + + if (!*h) { +#if defined (ALLOCATION_DEBUGGING) + abort ("%s: dereference of null pointer!", name); +#else + return ISC_R_INVALIDARG; +#endif + } + + if ((*h) -> refcnt <= 0) { +#if defined (ALLOCATION_DEBUGGING) + abort ("dereference of pointer with refcnt of zero!"); +#else + return ISC_R_INVALIDARG; +#endif + } + + if (--((*h) -> refcnt) <= 0 ) { + switch ((*h) -> type) { + case omapi_datatype_int: + case omapi_datatype_string: + case omapi_datatype_data: + default: + break; + case omapi_datatype_object: + omapi_object_dereference (&(*h) -> u.object, + name); + break; + } + free (*h); + } + *h = 0; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_data_string_new (omapi_data_string_t **d, + unsigned len, const char *name) +{ + omapi_data_string_t *new; + + new = malloc (OMAPI_DATA_STRING_EMPTY_SIZE + len); + if (!new) + return ISC_R_NOMEMORY; + memset (new, 0, OMAPI_DATA_STRING_EMPTY_SIZE); + new -> len = len; + return omapi_data_string_reference (d, new, name); +} + +isc_result_t omapi_data_string_reference (omapi_data_string_t **r, + omapi_data_string_t *h, + const char *name) +{ + if (!h || !r) + return ISC_R_INVALIDARG; + + if (*r) { +#if defined (ALLOCATION_DEBUGGING) + abort ("%s: reference store into non-null pointer!", name); +#else + return ISC_R_INVALIDARG; +#endif + } + *r = h; + h -> refcnt++; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_data_string_dereference (omapi_data_string_t **h, + const char *name) +{ + if (!h) + return ISC_R_INVALIDARG; + + if (!*h) { +#if defined (ALLOCATION_DEBUGGING) + abort ("%s: dereference of null pointer!", name); +#else + return ISC_R_INVALIDARG; +#endif + } + + if ((*h) -> refcnt <= 0) { +#if defined (ALLOCATION_DEBUGGING) + abort ("dereference of pointer with refcnt of zero!"); +#else + return ISC_R_INVALIDARG; +#endif + } + + if (--((*h) -> refcnt) <= 0 ) { + free (*h); + } + *h = 0; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_value_new (omapi_value_t **d, + const char *name) +{ + omapi_value_t *new; + + new = malloc (sizeof *new); + if (!new) + return ISC_R_NOMEMORY; + memset (new, 0, sizeof *new); + return omapi_value_reference (d, new, name); +} + +isc_result_t omapi_value_reference (omapi_value_t **r, + omapi_value_t *h, + const char *name) +{ + if (!h || !r) + return ISC_R_INVALIDARG; + + if (*r) { +#if defined (ALLOCATION_DEBUGGING) + abort ("%s: reference store into non-null pointer!", name); +#else + return ISC_R_INVALIDARG; +#endif + } + *r = h; + h -> refcnt++; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_value_dereference (omapi_value_t **h, + const char *name) +{ + if (!h) + return ISC_R_INVALIDARG; + + if (!*h) { +#if defined (ALLOCATION_DEBUGGING) + abort ("%s: dereference of null pointer!", name); +#else + return ISC_R_INVALIDARG; +#endif + } + + if ((*h) -> refcnt <= 0) { +#if defined (ALLOCATION_DEBUGGING) + abort ("dereference of pointer with refcnt of zero!"); +#else + return ISC_R_INVALIDARG; +#endif + } + + if (--((*h) -> refcnt) <= 0 ) { + if ((*h) -> name) + omapi_data_string_dereference (&(*h) -> name, name); + if ((*h) -> value) + omapi_typed_data_dereference (&(*h) -> value, name); + free (*h); + } + *h = 0; + return ISC_R_SUCCESS; +} + diff --git a/lib/omapi/buffer.c b/lib/omapi/buffer.c new file mode 100644 index 0000000000..37cfc2cb63 --- /dev/null +++ b/lib/omapi/buffer.c @@ -0,0 +1,499 @@ +/* buffer.c + + Buffer access functions for the object management protocol... */ + +/* + * Copyright (c) 1996-1999 Internet Software Consortium. + * Use is subject to license terms which appear in the file named + * ISC-LICENSE that should have accompanied this file when you + * received it. If a file named ISC-LICENSE did not accompany this + * file, or you are not sure the one you have is correct, you may + * obtain an applicable copy of the license at: + * + * http://www.isc.org/isc-license-1.0.html. + * + * This file is part of the ISC DHCP distribution. The documentation + * associated with this file is listed in the file DOCUMENTATION, + * included in the top-level directory of this release. + * + * Support and other services are available for ISC products - see + * http://www.isc.org for more information. + */ + +#include + +/* Make sure that at least len bytes are in the input buffer, and if not, + read enough bytes to make up the difference. */ + +isc_result_t omapi_connection_reader (omapi_object_t *h) +{ + omapi_buffer_t *buffer; + isc_result_t status; + unsigned read_len; + int read_status; + omapi_connection_object_t *c; + unsigned bytes_to_read; + + if (!h || h -> type != omapi_type_connection) + return ISC_R_INVALIDARG; + c = (omapi_connection_object_t *)h; + + /* Make sure c -> bytes_needed is valid. */ + if (c -> bytes_needed < 0) + return ISC_R_INVALIDARG; + + /* See if there are enough bytes. */ + if (c -> in_bytes >= OMAPI_BUF_SIZE - 1 && + c -> in_bytes > c -> bytes_needed) + return ISC_R_SUCCESS; + + if (c -> inbufs) { + for (buffer = c -> inbufs; buffer -> next; + buffer = buffer -> next) + ; + if (!BUFFER_BYTES_FREE (buffer)) { + status = omapi_buffer_new (&buffer -> next, + "omapi_private_read"); + if (status != ISC_R_SUCCESS) + return status; + buffer = buffer -> next; + } + } else { + status = omapi_buffer_new (&c -> inbufs, + "omapi_private_read"); + if (status != ISC_R_SUCCESS) + return status; + buffer = c -> inbufs; + } + + bytes_to_read = BUFFER_BYTES_FREE (buffer); + + while (bytes_to_read) { + if (buffer -> tail > buffer -> head) + read_len = sizeof (buffer -> buf) - buffer -> tail; + else + read_len = buffer -> head - buffer -> tail; + + read_status = read (c -> socket, + &buffer -> buf [buffer -> tail], read_len); + if (read_status < 0) { + if (errno == EWOULDBLOCK) + break; + else if (errno == EIO) + return ISC_R_IOERROR; + else if (errno == EINVAL) + return ISC_R_INVALIDARG; + else if (errno == ECONNRESET) { + omapi_disconnect (h, 0); + return ISC_R_SHUTTINGDOWN; + } else + return ISC_R_UNEXPECTED; + } + /* If we got a zero-length read, as opposed to EWOULDBLOCK, + the remote end closed the connection. */ + if (read_status == 0) { + omapi_disconnect (h, 0); + return ISC_R_SHUTTINGDOWN; + } + buffer -> tail += read_status; + c -> in_bytes += read_status; + if (buffer -> tail == sizeof buffer -> buf) + buffer -> tail = 0; + if (read_status < read_len) + break; + bytes_to_read -= read_status; + } + + if (c -> bytes_needed <= c -> in_bytes) { + omapi_signal (h, "ready", c); + } + return ISC_R_SUCCESS; +} + +/* Put some bytes into the output buffer for a connection. */ + +isc_result_t omapi_connection_copyin (omapi_object_t *h, + const unsigned char *bufp, + unsigned len) +{ + omapi_buffer_t *buffer; + isc_result_t status; + int bytes_copied = 0; + unsigned copy_len; + omapi_connection_object_t *c; + + /* Make sure len is valid. */ + if (len < 0) + return ISC_R_INVALIDARG; + if (!h || h -> type != omapi_type_connection) + return ISC_R_INVALIDARG; + c = (omapi_connection_object_t *)h; + + if (c -> outbufs) { + for (buffer = c -> outbufs; + buffer -> next; buffer = buffer -> next) + ; + } else { + status = omapi_buffer_new (&c -> outbufs, + "omapi_private_buffer_copyin"); + if (status != ISC_R_SUCCESS) + return status; + buffer = c -> outbufs; + } + + while (bytes_copied < len) { + /* If there is no space available in this buffer, + allocate a new one. */ + if (!BUFFER_BYTES_FREE (buffer)) { + status = (omapi_buffer_new + (&buffer -> next, + "omapi_private_buffer_copyin")); + if (status != ISC_R_SUCCESS) + return status; + buffer = buffer -> next; + } + + if (buffer -> tail > buffer -> head) + copy_len = sizeof (buffer -> buf) - buffer -> tail; + else + copy_len = buffer -> head - buffer -> tail; + + if (copy_len > (len - bytes_copied)) + copy_len = len - bytes_copied; + + memcpy (&buffer -> buf [buffer -> tail], + &bufp [bytes_copied], copy_len); + buffer -> tail += copy_len; + c -> out_bytes += copy_len; + bytes_copied += copy_len; + if (buffer -> tail == sizeof buffer -> buf) + buffer -> tail = 0; + } + return ISC_R_SUCCESS; +} + +/* Copy some bytes from the input buffer, and advance the input buffer + pointer beyond the bytes copied out. */ + +isc_result_t omapi_connection_copyout (unsigned char *buf, + omapi_object_t *h, + unsigned size) +{ + unsigned bytes_remaining; + unsigned bytes_this_copy; + unsigned first_byte; + omapi_buffer_t *buffer; + unsigned char *bufp; + omapi_connection_object_t *c; + + if (!h || h -> type != omapi_type_connection) + return ISC_R_INVALIDARG; + c = (omapi_connection_object_t *)h; + + if (size > c -> in_bytes) + return ISC_R_NOMORE; + bufp = buf; + bytes_remaining = size; + buffer = c -> inbufs; + + while (bytes_remaining) { + if (!buffer) + return ISC_R_UNEXPECTED; + if (BYTES_IN_BUFFER (buffer)) { + if (buffer -> head == (sizeof buffer -> buf) - 1) + first_byte = 0; + else + first_byte = buffer -> head + 1; + + if (first_byte > buffer -> tail) { + bytes_this_copy = (sizeof buffer -> buf - + first_byte); + } else { + bytes_this_copy = + buffer -> tail - first_byte; + } + if (bytes_this_copy > bytes_remaining) + bytes_this_copy = bytes_remaining; + if (bufp) { + memcpy (bufp, &buffer -> buf [first_byte], + bytes_this_copy); + bufp += bytes_this_copy; + } + bytes_remaining -= bytes_this_copy; + buffer -> head = first_byte + bytes_this_copy - 1; + c -> in_bytes -= bytes_this_copy; + } + + if (!BYTES_IN_BUFFER (buffer)) + buffer = buffer -> next; + } + + /* Get rid of any input buffers that we emptied. */ + buffer = (omapi_buffer_t *)0; + while (c -> inbufs && + !BYTES_IN_BUFFER (c -> inbufs)) { + if (c -> inbufs -> next) { + omapi_buffer_reference + (&buffer, + c -> inbufs -> next, + "omapi_private_buffer_copyout"); + omapi_buffer_dereference + (&c -> inbufs -> next, + "omapi_private_buffer_copyout"); + } + omapi_buffer_dereference (&c -> inbufs, + "omapi_private_buffer_copyout"); + if (buffer) { + omapi_buffer_reference + (&c -> inbufs, buffer, + "omapi_private_buffer_copyout"); + omapi_buffer_dereference + (&buffer, "omapi_private_buffer_copyout"); + } + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_writer (omapi_object_t *h) +{ + unsigned bytes_this_write; + unsigned bytes_written; + unsigned first_byte; + omapi_buffer_t *buffer; + unsigned char *bufp; + omapi_connection_object_t *c; + + if (!h || h -> type != omapi_type_connection) + return ISC_R_INVALIDARG; + c = (omapi_connection_object_t *)h; + + /* Already flushed... */ + if (!c -> out_bytes) + return ISC_R_SUCCESS; + + buffer = c -> outbufs; + + while (c -> out_bytes) { + if (!buffer) + return ISC_R_UNEXPECTED; + if (BYTES_IN_BUFFER (buffer)) { + if (buffer -> head == (sizeof buffer -> buf) - 1) + first_byte = 0; + else + first_byte = buffer -> head + 1; + + if (first_byte > buffer -> tail) { + bytes_this_write = (sizeof buffer -> buf - + first_byte); + } else { + bytes_this_write = + buffer -> tail - first_byte; + } + bytes_written = write (c -> socket, + &buffer -> buf [first_byte], + bytes_this_write); + /* If the write failed with EWOULDBLOCK or we wrote + zero bytes, a further write would block, so we have + flushed as much as we can for now. Other errors + are really errors. */ + if (bytes_written < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) + return ISC_R_SUCCESS; + else if (errno == EPIPE) + return ISC_R_NOCONN; + else if (errno == EFBIG || errno == EDQUOT) + return ISC_R_NORESOURCES; + else if (errno == ENOSPC) + return ISC_R_NOSPACE; + else if (errno == EIO) + return ISC_R_IOERROR; + else if (errno == EINVAL) + return ISC_R_INVALIDARG; + else if (errno == ECONNRESET) + return ISC_R_SHUTTINGDOWN; + else + return ISC_R_UNEXPECTED; + } + if (bytes_written == 0) + return ISC_R_SUCCESS; + + buffer -> head = first_byte + bytes_written - 1; + c -> out_bytes -= bytes_written; + + /* If we didn't finish out the write, we filled the + O.S. output buffer and a further write would block, + so stop trying to flush now. */ + if (bytes_written != bytes_this_write) + return ISC_R_SUCCESS; + } + + if (!BYTES_IN_BUFFER (buffer)) + buffer = buffer -> next; + } + + /* Get rid of any output buffers we emptied. */ + buffer = (omapi_buffer_t *)0; + while (c -> outbufs && + !BYTES_IN_BUFFER (c -> outbufs)) { + if (c -> outbufs -> next) { + omapi_buffer_reference + (&buffer, c -> outbufs -> next, + "omapi_private_flush"); + omapi_buffer_dereference + (&c -> outbufs -> next, "omapi_private_flush"); + } + omapi_buffer_dereference (&c -> outbufs, + "omapi_private_flush"); + if (buffer) { + omapi_buffer_reference (&c -> outbufs, buffer, + "omapi_private_flush"); + omapi_buffer_dereference (&buffer, + "omapi_private_flush"); + } + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_get_uint32 (omapi_object_t *c, + u_int32_t *result) +{ + u_int32_t inbuf; + isc_result_t status; + + status = omapi_connection_copyout ((unsigned char *)&inbuf, + c, sizeof inbuf); + if (status != ISC_R_SUCCESS) + return status; + + *result = ntohl (inbuf); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_put_uint32 (omapi_object_t *c, + u_int32_t value) +{ + u_int32_t inbuf; + isc_result_t status; + + inbuf = htonl (value); + + return omapi_connection_copyin (c, (unsigned char *)&inbuf, + sizeof inbuf); +} + +isc_result_t omapi_connection_get_uint16 (omapi_object_t *c, + u_int16_t *result) +{ + u_int16_t inbuf; + isc_result_t status; + + status = omapi_connection_copyout ((unsigned char *)&inbuf, + c, sizeof inbuf); + if (status != ISC_R_SUCCESS) + return status; + + *result = ntohs (inbuf); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_put_uint16 (omapi_object_t *c, + u_int32_t value) +{ + u_int16_t inbuf; + isc_result_t status; + + inbuf = htons (value); + + return omapi_connection_copyin (c, (unsigned char *)&inbuf, + sizeof inbuf); +} + +isc_result_t omapi_connection_write_typed_data (omapi_object_t *c, + omapi_typed_data_t *data) +{ + isc_result_t status; + omapi_handle_t handle; + + switch (data -> type) { + case omapi_datatype_int: + status = omapi_connection_put_uint32 (c, sizeof (u_int32_t)); + if (status != ISC_R_SUCCESS) + return status; + return omapi_connection_put_uint32 (c, ((u_int32_t) + (data -> u.integer))); + + case omapi_datatype_string: + case omapi_datatype_data: + status = omapi_connection_put_uint32 (c, data -> u.buffer.len); + if (status != ISC_R_SUCCESS) + return status; + if (data -> u.buffer.len) + return omapi_connection_copyin + (c, data -> u.buffer.value, + data -> u.buffer.len); + return ISC_R_SUCCESS; + + case omapi_datatype_object: + if (data -> u.object) { + status = omapi_object_handle (&handle, + data -> u.object); + if (status != ISC_R_SUCCESS) + return status; + } else + handle = 0; + status = omapi_connection_put_uint32 (c, sizeof handle); + if (status != ISC_R_SUCCESS) + return status; + return omapi_connection_put_uint32 (c, handle); + + } + return ISC_R_INVALIDARG; +} + +isc_result_t omapi_connection_put_name (omapi_object_t *c, const char *name) +{ + isc_result_t status; + unsigned len = strlen (name); + + status = omapi_connection_put_uint16 (c, len); + if (status != ISC_R_SUCCESS) + return status; + return omapi_connection_copyin (c, (const unsigned char *)name, len); +} + +isc_result_t omapi_connection_put_string (omapi_object_t *c, + const char *string) +{ + isc_result_t status; + unsigned len; + + if (string) + len = strlen (string); + else + len = 0; + + status = omapi_connection_put_uint32 (c, len); + if (status != ISC_R_SUCCESS) + return status; + if (len) + return omapi_connection_copyin + (c, (const unsigned char *)string, len); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_put_handle (omapi_object_t *c, omapi_object_t *h) +{ + isc_result_t status; + omapi_handle_t handle; + + if (h) { + status = omapi_object_handle (&handle, h); + if (status != ISC_R_SUCCESS) + return status; + } else + handle = 0; /* The null handle. */ + status = omapi_connection_put_uint32 (c, sizeof handle); + if (status != ISC_R_SUCCESS) + return status; + return omapi_connection_put_uint32 (c, handle); +} diff --git a/lib/omapi/connection.c b/lib/omapi/connection.c new file mode 100644 index 0000000000..5fa4603d7b --- /dev/null +++ b/lib/omapi/connection.c @@ -0,0 +1,337 @@ +/* connection.c + + Subroutines for dealing with connections. */ + +/* + * Copyright (c) 1996-1999 Internet Software Consortium. + * Use is subject to license terms which appear in the file named + * ISC-LICENSE that should have accompanied this file when you + * received it. If a file named ISC-LICENSE did not accompany this + * file, or you are not sure the one you have is correct, you may + * obtain an applicable copy of the license at: + * + * http://www.isc.org/isc-license-1.0.html. + * + * This file is part of the ISC DHCP distribution. The documentation + * associated with this file is listed in the file DOCUMENTATION, + * included in the top-level directory of this release. + * + * Support and other services are available for ISC products - see + * http://www.isc.org for more information. + */ + +#include + +isc_result_t omapi_connect (omapi_object_t *c, + const char *server_name, + int port) +{ + struct hostent *he; + int hix; + isc_result_t status; + omapi_connection_object_t *obj; + int flag; + + obj = (omapi_connection_object_t *)malloc (sizeof *obj); + if (!obj) + return ISC_R_NOMEMORY; + memset (obj, 0, sizeof *obj); + obj -> refcnt = 1; + obj -> type = omapi_type_connection; + + status = omapi_object_reference (&c -> outer, (omapi_object_t *)obj, + "omapi_protocol_connect"); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_protocol_connect"); + return status; + } + status = omapi_object_reference (&obj -> inner, c, + "omapi_protocol_connect"); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_protocol_connect"); + return status; + } + + /* Set up all the constants in the address... */ + obj -> remote_addr.sin_port = htons (port); + + /* First try for a numeric address, since that's easier to check. */ + if (!inet_aton (server_name, &obj -> remote_addr.sin_addr)) { + /* If we didn't get a numeric address, try for a domain + name. It's okay for this call to block. */ + he = gethostbyname (server_name); + if (!he) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_connect"); + return ISC_R_HOSTUNKNOWN; + } + hix = 1; + memcpy (&obj -> remote_addr.sin_addr, + he -> h_addr_list [0], + sizeof obj -> remote_addr.sin_addr); + } else + he = (struct hostent *)0; + +#if defined (HAVE_SA_LEN) + obj -> remote_addr.sin_len = + sizeof (struct sockaddr_in); +#endif + obj -> remote_addr.sin_family = AF_INET; + memset (&(obj -> remote_addr.sin_zero), 0, + sizeof obj -> remote_addr.sin_zero); + + /* Create a socket on which to communicate. */ + obj -> socket = + socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (obj -> socket < 0) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_connect"); + if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS) + return ISC_R_NORESOURCES; + return ISC_R_UNEXPECTED; + } + + /* Set the SO_REUSEADDR flag (this should not fail). */ + flag = 1; + if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR, + (char *)&flag, sizeof flag) < 0) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_connect"); + return ISC_R_UNEXPECTED; + } + + /* Try to connect to the one IP address we were given, or any of + the IP addresses listed in the host's A RR. */ + while (connect (obj -> socket, + ((struct sockaddr *) + &obj -> remote_addr), + sizeof obj -> remote_addr)) { + if (!he || !he -> h_addr_list [hix]) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_connect"); + if (errno == ECONNREFUSED) + return ISC_R_CONNREFUSED; + if (errno == ENETUNREACH) + return ISC_R_NETUNREACH; + return ISC_R_UNEXPECTED; + } + memcpy (&obj -> remote_addr.sin_addr, + he -> h_addr_list [hix++], + sizeof obj -> remote_addr.sin_addr); + } + + obj -> state = omapi_connection_connected; + + /* I don't know why this would fail, so I'm tempted not to test + the return value. */ + hix = sizeof (obj -> local_addr); + if (getsockname (obj -> socket, + ((struct sockaddr *) + &obj -> local_addr), &hix) < 0) { + } + + if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_connect"); + return ISC_R_UNEXPECTED; + } + + status = omapi_register_io_object ((omapi_object_t *)obj, + omapi_connection_readfd, + omapi_connection_writefd, + omapi_connection_reader, + omapi_connection_writer, + omapi_connection_reaper); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_connect"); + return status; + } + + return ISC_R_SUCCESS; +} + +/* Disconnect a connection object from the remote end. If force is nonzero, + close the connection immediately. Otherwise, shut down the receiving end + but allow any unsent data to be sent before actually closing the socket. */ + +isc_result_t omapi_disconnect (omapi_object_t *h, + int force) +{ + omapi_connection_object_t *c; + + c = (omapi_connection_object_t *)h; + if (c -> type != omapi_type_connection) + return ISC_R_INVALIDARG; + + if (!force) { + /* If we're already disconnecting, we don't have to do + anything. */ + if (c -> state == omapi_connection_disconnecting) + return ISC_R_SUCCESS; + + /* Try to shut down the socket - this sends a FIN to the + remote end, so that it won't send us any more data. If + the shutdown succeeds, and we still have bytes left to + write, defer closing the socket until that's done. */ + if (!shutdown (c -> socket, SHUT_RD)) { + if (c -> out_bytes > 0) { + c -> state = omapi_connection_disconnecting; + return ISC_R_SUCCESS; + } + } + } + close (c -> socket); + c -> state = omapi_connection_closed; + + /* Disconnect from I/O object, if any. */ + if (h -> outer) + omapi_object_dereference (&h -> outer, "omapi_disconnect"); + + /* If whatever created us registered a signal handler, send it + a disconnect signal. */ + omapi_signal (h, "disconnect", h); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_require (omapi_object_t *h, unsigned bytes) +{ + omapi_connection_object_t *c; + + if (h -> type != omapi_type_connection) + return ISC_R_INVALIDARG; + c = (omapi_connection_object_t *)h; + + c -> bytes_needed = bytes; + if (c -> bytes_needed <= c -> in_bytes) { + return ISC_R_SUCCESS; + } + return ISC_R_NOTYET; +} + +/* Return the socket on which the dispatcher should wait for readiness + to read, for a connection object. If we already have more bytes than + we need to do the next thing, and we have at least a single full input + buffer, then don't indicate that we're ready to read. */ +int omapi_connection_readfd (omapi_object_t *h) +{ + omapi_connection_object_t *c; + if (h -> type != omapi_type_connection) + return -1; + c = (omapi_connection_object_t *)h; + if (c -> state != omapi_connection_connected) + return -1; + if (c -> in_bytes >= OMAPI_BUF_SIZE - 1 && + c -> in_bytes > c -> bytes_needed) + return -1; + return c -> socket; +} + +/* Return the socket on which the dispatcher should wait for readiness + to write, for a connection object. If there are no bytes buffered + for writing, then don't indicate that we're ready to write. */ +int omapi_connection_writefd (omapi_object_t *h) +{ + omapi_connection_object_t *c; + if (h -> type != omapi_type_connection) + return -1; + c = (omapi_connection_object_t *)h; + if (c -> out_bytes) + return c -> socket; + else + return -1; +} + +/* Reaper function for connection - if the connection is completely closed, + reap it. If it's in the disconnecting state, there were bytes left + to write when the user closed it, so if there are now no bytes left to + write, we can close it. */ +isc_result_t omapi_connection_reaper (omapi_object_t *h) +{ + omapi_connection_object_t *c; + + if (h -> type != omapi_type_connection) + return ISC_R_INVALIDARG; + + c = (omapi_connection_object_t *)h; + if (c -> state == omapi_connection_disconnecting && + c -> out_bytes == 0) + omapi_disconnect (h, 1); + if (c -> state == omapi_connection_closed) + return ISC_R_NOTCONNECTED; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + if (h -> type != omapi_type_connection) + return ISC_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> set_value) + return (*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_connection_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + if (h -> type != omapi_type_connection) + return ISC_R_INVALIDARG; + + 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_connection_destroy (omapi_object_t *h, const char *name) +{ + omapi_connection_object_t *c; + + if (h -> type != omapi_type_connection) + return ISC_R_UNEXPECTED; + c = (omapi_connection_object_t *)(h); + if (c -> state == omapi_connection_connected) + omapi_disconnect (h, 1); + if (c -> listener) + omapi_object_dereference (&c -> listener, name); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_connection_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + if (h -> type != omapi_type_connection) + return ISC_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_connection_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *m) +{ + int i; + + if (m -> type != omapi_type_connection) + return ISC_R_INVALIDARG; + + if (m -> inner && m -> inner -> type -> stuff_values) + return (*(m -> inner -> type -> stuff_values)) (c, id, + m -> inner); + return ISC_R_SUCCESS; +} diff --git a/lib/omapi/dispatch.c b/lib/omapi/dispatch.c new file mode 100644 index 0000000000..659f998a22 --- /dev/null +++ b/lib/omapi/dispatch.c @@ -0,0 +1,402 @@ +/* dispatch.c + + I/O dispatcher. */ + +/* + * Copyright (c) 1996-1999 Internet Software Consortium. + * Use is subject to license terms which appear in the file named + * ISC-LICENSE that should have accompanied this file when you + * received it. If a file named ISC-LICENSE did not accompany this + * file, or you are not sure the one you have is correct, you may + * obtain an applicable copy of the license at: + * + * http://www.isc.org/isc-license-1.0.html. + * + * This file is part of the ISC DHCP distribution. The documentation + * associated with this file is listed in the file DOCUMENTATION, + * included in the top-level directory of this release. + * + * Support and other services are available for ISC products - see + * http://www.isc.org for more information. + */ + +#include + +static omapi_io_object_t omapi_io_states; +u_int32_t cur_time; + +/* Register an I/O handle so that we can do asynchronous I/O on it. */ + +isc_result_t omapi_register_io_object (omapi_object_t *h, + int (*readfd) (omapi_object_t *), + int (*writefd) (omapi_object_t *), + isc_result_t (*reader) + (omapi_object_t *), + isc_result_t (*writer) + (omapi_object_t *), + isc_result_t (*reaper) + (omapi_object_t *)) +{ + isc_result_t status; + omapi_io_object_t *obj, *p; + + /* omapi_io_states is a static object. If its reference count + is zero, this is the first I/O handle to be registered, so + we need to initialize it. Because there is no inner or outer + pointer on this object, and we're setting its refcnt to 1, it + will never be freed. */ + if (!omapi_io_states.refcnt) { + omapi_io_states.refcnt = 1; + omapi_io_states.type = omapi_type_io_object; + } + + obj = malloc (sizeof *obj); + if (!obj) + return ISC_R_NOMEMORY; + memset (obj, 0, sizeof *obj); + + obj -> refcnt = 1; + obj -> type = omapi_type_io_object; + + status = omapi_object_reference (&obj -> inner, h, + "omapi_register_io_object"); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_register_io_object"); + return status; + } + + status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj, + "omapi_register_io_object"); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_register_io_object"); + return status; + } + + /* Find the last I/O state, if there are any. */ + for (p = omapi_io_states.next; + p && p -> next; p = p -> next) + ; + if (p) + p -> next = obj; + else + omapi_io_states.next = obj; + + obj -> readfd = readfd; + obj -> writefd = writefd; + obj -> reader = reader; + obj -> writer = writer; + obj -> reaper = reaper; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_dispatch (struct timeval *t) +{ + return omapi_wait_for_completion ((omapi_object_t *)&omapi_io_states, + t); +} + +isc_result_t omapi_wait_for_completion (omapi_object_t *object, + struct timeval *t) +{ + isc_result_t status; + omapi_waiter_object_t *waiter; + omapi_object_t *inner; + + if (object) { + waiter = malloc (sizeof *waiter); + if (!waiter) + return ISC_R_NOMEMORY; + memset (waiter, 0, sizeof *waiter); + waiter -> refcnt = 1; + waiter -> type = omapi_type_waiter; + + /* Paste the waiter object onto the inner object we're + waiting on. */ + for (inner = object; inner -> inner; inner = inner -> inner) + ; + + status = omapi_object_reference (&waiter -> outer, inner, + "omapi_wait_for_completion"); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&waiter, + "omapi_wait_for_completion"); + return status; + } + + status = omapi_object_reference (&inner -> inner, + (omapi_object_t *)waiter, + "omapi_wait_for_completion"); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&waiter, + "omapi_wait_for_completion"); + return status; + } + } else + waiter = (omapi_waiter_object_t *)0; + + do { + status = omapi_one_dispatch ((omapi_object_t *)waiter, t); + if (status != ISC_R_SUCCESS) + return status; + } while (!waiter || !waiter -> ready); + + if (waiter -> outer) { + if (waiter -> outer -> inner) { + omapi_object_dereference (&waiter -> outer -> inner, + "omapi_wait_for_completion"); + if (waiter -> inner) + omapi_object_reference + (&waiter -> outer -> inner, + waiter -> inner, + "omapi_wait_for_completion"); + } + omapi_object_dereference (&waiter -> outer, + "omapi_wait_for_completion"); + } + if (waiter -> inner) + omapi_object_dereference (&waiter -> inner, + "omapi_wait_for_completion"); + + omapi_object_dereference ((omapi_object_t **)&waiter, + "omapi_wait_for_completion"); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_one_dispatch (omapi_object_t *wo, + struct timeval *t) +{ + fd_set r, w, x; + int max = 0; + int count; + int desc; + struct timeval now, to; + omapi_io_object_t *io, *prev; + isc_result_t status; + omapi_waiter_object_t *waiter; + + if (!wo || wo -> type != omapi_type_waiter) + waiter = (omapi_waiter_object_t *)0; + else + waiter = (omapi_waiter_object_t *)wo; + + FD_ZERO (&x); + + /* First, see if the timeout has expired, and if so return. */ + if (t) { + gettimeofday (&now, (struct timezone *)0); + cur_time = now.tv_sec; + if (now.tv_sec > t -> tv_sec || + (now.tv_sec == t -> tv_sec && now.tv_usec >= t -> tv_usec)) + return ISC_R_TIMEDOUT; + + /* We didn't time out, so figure out how long until + we do. */ + to.tv_sec = t -> tv_sec - now.tv_sec; + to.tv_usec = t -> tv_usec - now.tv_usec; + if (to.tv_usec < 0) { + to.tv_usec += 1000000; + to.tv_sec--; + } + } + + /* If the object we're waiting on has reached completion, + return now. */ + if (waiter && waiter -> ready) + return ISC_R_SUCCESS; + + /* If we have no I/O state, we can't proceed. */ + if (!(io = omapi_io_states.next)) + return ISC_R_NOMORE; + + /* Set up the read and write masks. */ + FD_ZERO (&r); + FD_ZERO (&w); + + for (; io; io = io -> next) { + /* Check for a read socket. If we shouldn't be + trying to read for this I/O object, either there + won't be a readfd function, or it'll return -1. */ + if (io -> readfd && + (desc = (*(io -> readfd)) (io -> inner)) >= 0) { + FD_SET (desc, &r); + if (desc > max) + max = desc; + } + + /* Same deal for write fdets. */ + if (io -> writefd && + (desc = (*(io -> writefd)) (io -> inner)) >= 0) { + FD_SET (desc, &w); + if (desc > max) + max = desc; + } + } + + /* Wait for a packet or a timeout... XXX */ + count = select (max + 1, &r, &w, &x, t ? &to : (struct timeval *)0); + + /* Get the current time... */ + gettimeofday (&now, (struct timezone *)0); + cur_time = now.tv_sec; + + /* Not likely to be transitory... */ + if (count < 0) + return ISC_R_UNEXPECTED; + + for (io = omapi_io_states.next; io; io = io -> next) { + /* Check for a read descriptor, and if there is one, + see if we got input on that socket. */ + if (io -> readfd && + (desc = (*(io -> readfd)) (io -> inner)) >= 0) { + if (FD_ISSET (desc, &r)) + status = ((*(io -> reader)) (io -> inner)); + /* XXX what to do with status? */ + } + + /* Same deal for write descriptors. */ + if (io -> writefd && + (desc = (*(io -> writefd)) (io -> inner)) >= 0) + { + if (FD_ISSET (desc, &w)) + status = ((*(io -> writer)) (io -> inner)); + /* XXX what to do with status? */ + } + } + + /* Now check for I/O handles that are no longer valid, + and remove them from the list. */ + prev = (omapi_io_object_t *)0; + for (io = omapi_io_states.next; io; io = io -> next) { + if (io -> reaper) { + status = (*(io -> reaper)) (io -> inner); + if (status != ISC_R_SUCCESS) { + omapi_io_object_t *tmp = + (omapi_io_object_t *)0; + /* Save a reference to the next + pointer, if there is one. */ + if (io -> next) + omapi_object_reference + ((omapi_object_t **)&tmp, + (omapi_object_t *)io -> next, + "omapi_wfc"); + if (prev) { + omapi_object_dereference + (((omapi_object_t **) + &prev -> next), "omapi_wfc"); + if (tmp) + omapi_object_reference + (((omapi_object_t **) + &prev -> next), + (omapi_object_t *)tmp, + "omapi_wfc"); + } else { + omapi_object_dereference + (((omapi_object_t **) + &omapi_io_states.next), + "omapi_wfc"); + if (tmp) + omapi_object_reference + (((omapi_object_t **) + &omapi_io_states.next), + (omapi_object_t *)tmp, + "omapi_wfc"); + else + omapi_signal_in + ((omapi_object_t *) + &omapi_io_states, + "ready"); + } + if (tmp) + omapi_object_dereference + ((omapi_object_t **)&tmp, + "omapi_wfc"); + } + } + prev = io; + } + + return ISC_R_SUCCESS; +} + +isc_result_t omapi_io_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + if (h -> type != omapi_type_io_object) + return ISC_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> set_value) + return (*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_io_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + if (h -> type != omapi_type_io_object) + return ISC_R_INVALIDARG; + + 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_io_destroy (omapi_object_t *h, const char *name) +{ + if (h -> type != omapi_type_io_object) + return ISC_R_INVALIDARG; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_io_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + if (h -> type != omapi_type_io_object) + return ISC_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> signal_handler) + return (*(h -> inner -> type -> signal_handler)) (h -> inner, + name, ap); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_io_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *i) +{ + if (i -> type != omapi_type_io_object) + return ISC_R_INVALIDARG; + + if (i -> inner && i -> inner -> type -> stuff_values) + return (*(i -> inner -> type -> stuff_values)) (c, id, + i -> inner); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_waiter_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + omapi_waiter_object_t *waiter; + + if (h -> type != omapi_type_waiter) + return ISC_R_INVALIDARG; + + if (!strcmp (name, "ready")) { + waiter = (omapi_waiter_object_t *)h; + waiter -> ready = 1; + return ISC_R_SUCCESS; + } + + if (h -> inner && h -> inner -> type -> signal_handler) + return (*(h -> inner -> type -> signal_handler)) (h -> inner, + name, ap); + return ISC_R_NOTFOUND; +} + diff --git a/lib/omapi/generic.c b/lib/omapi/generic.c new file mode 100644 index 0000000000..ad63b92165 --- /dev/null +++ b/lib/omapi/generic.c @@ -0,0 +1,251 @@ +/* generic.c + + Subroutines that support the generic object. */ + +/* + * Copyright (c) 1996-1999 Internet Software Consortium. + * Use is subject to license terms which appear in the file named + * ISC-LICENSE that should have accompanied this file when you + * received it. If a file named ISC-LICENSE did not accompany this + * file, or you are not sure the one you have is correct, you may + * obtain an applicable copy of the license at: + * + * http://www.isc.org/isc-license-1.0.html. + * + * This file is part of the ISC DHCP distribution. The documentation + * associated with this file is listed in the file DOCUMENTATION, + * included in the top-level directory of this release. + * + * Support and other services are available for ISC products - see + * http://www.isc.org for more information. + */ + +#include + +isc_result_t omapi_generic_new (omapi_object_t **gen, const char *name) +{ + omapi_generic_object_t *obj; + + obj = malloc (sizeof *obj); + if (!obj) + return ISC_R_NOMEMORY; + memset (obj, 0, sizeof *obj); + obj -> refcnt = 0; + obj -> type = omapi_type_generic; + + return omapi_object_reference (gen, (omapi_object_t *)obj, name); +} + +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; + int vm_new; + int i; + isc_result_t status; + + if (h -> type != omapi_type_generic) + return ISC_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 (!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, + "omapi_message_get_value")); + if (status != ISC_R_SUCCESS) + return status; + omapi_data_string_reference + (&new -> name, name, + "omapi_message_get_value"); + if (value) + omapi_typed_data_reference + (&new -> value, value, + "omapi_generic_set_value"); + + omapi_value_dereference (&(g -> values [i]), + "omapi_message_set_value"); + status = (omapi_value_reference + (&(g -> values [i]), new, + "omapi_message_set_value")); + omapi_value_dereference (&new, + "omapi_message_set_value"); + return status; + } + } + + /* 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 (g -> nvalues == g -> va_max) { + if (g -> va_max) + vm_new = 2 * g -> va_max; + else + vm_new = 10; + va = malloc (vm_new * sizeof *va); + if (!va) + return ISC_R_NOMEMORY; + if (g -> va_max) + memcpy (va, g -> values, g -> va_max * sizeof *va); + memset (va + g -> va_max, 0, + (vm_new - g -> va_max) * sizeof *va); + free (g -> values); + g -> values = va; + g -> va_max = vm_new; + } + status = omapi_value_new (&g -> values [g -> nvalues], + "omapi_generic_set_value"); + if (status != ISC_R_SUCCESS) + return status; + omapi_data_string_reference (&g -> values [g -> nvalues] -> name, name, + "omapi_generic_set_value"); + if (value) + omapi_typed_data_reference + (&g -> values [g -> nvalues] -> value, value, + "omapi_generic_set_value"); + 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 ISC_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 (!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], + "omapi_message_get_value"); + } + } + + 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 *name) +{ + 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], + name); + } + free (g -> values); + g -> values = (omapi_value_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 ISC_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 ISC_R_INVALIDARG; + src = (omapi_generic_object_t *)g; + + for (i = 0; i < src -> nvalues; i++) { + if (src -> values [i] && src -> values [i] -> name -> len) { + 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; +} + diff --git a/lib/omapi/handle.c b/lib/omapi/handle.c new file mode 100644 index 0000000000..9c0154149b --- /dev/null +++ b/lib/omapi/handle.c @@ -0,0 +1,283 @@ +/* handle.c + + Functions for maintaining handles on objects. */ + +/* + * Copyright (c) 1996-1999 Internet Software Consortium. + * Use is subject to license terms which appear in the file named + * ISC-LICENSE that should have accompanied this file when you + * received it. If a file named ISC-LICENSE did not accompany this + * file, or you are not sure the one you have is correct, you may + * obtain an applicable copy of the license at: + * + * http://www.isc.org/isc-license-1.0.html. + * + * This file is part of the ISC DHCP distribution. The documentation + * associated with this file is listed in the file DOCUMENTATION, + * included in the top-level directory of this release. + * + * Support and other services are available for ISC products - see + * http://www.isc.org for more information. + */ + +#include + +/* The handle table is a hierarchical tree designed for quick mapping + of handle identifiers to objects. Objects contain their own handle + identifiers if they have them, so the reverse mapping is also + quick. The hierarchy is made up of table objects, each of which + has 120 entries, a flag indicating whether the table is a leaf + table or an indirect table, the handle of the first object covered + by the table and the first object after that that's *not* covered + by the table, a count of how many objects of either type are + currently stored in the table, and an array of 120 entries pointing + either to objects or tables. + + When we go to add an object to the table, we look to see if the + next object handle to be assigned is covered by the outermost + table. If it is, we find the place within that table where the + next handle should go, and if necessary create additional nodes in + the tree to contain the new handle. The pointer to the object is + then stored in the correct position. + + Theoretically, we could have some code here to free up handle + tables as they go out of use, but by and large handle tables won't + go out of use, so this is being skipped for now. It shouldn't be + too hard to implement in the future if there's a different + application. */ + +omapi_handle_table_t *omapi_handle_table; +omapi_handle_t omapi_next_handle = 1; /* Next handle to be assigned. */ + +static isc_result_t omapi_handle_lookup_in (omapi_object_t **, + omapi_handle_t, + omapi_handle_table_t *); +static isc_result_t omapi_object_handle_in_table (omapi_handle_t, + omapi_handle_table_t *, + omapi_object_t *); +static isc_result_t omapi_handle_table_enclose (omapi_handle_table_t **); + +isc_result_t omapi_object_handle (omapi_handle_t *h, omapi_object_t *o) +{ + int tabix; + isc_result_t status; + + if (o -> handle) { + *h = o -> handle; + return ISC_R_SUCCESS; + } + + if (!omapi_handle_table) { + omapi_handle_table = malloc (sizeof *omapi_handle_table); + if (!omapi_handle_table) + return ISC_R_NOMEMORY; + memset (omapi_handle_table, 0, sizeof *omapi_handle_table); + omapi_handle_table -> first = 0; + omapi_handle_table -> limit = OMAPI_HANDLE_TABLE_SIZE; + omapi_handle_table -> leafp = 1; + } + + /* If this handle doesn't fit in the outer table, we need to + make a new outer table. This is a while loop in case for + some reason we decide to do disjoint handle allocation, + where the next level of indirection still isn't big enough + to enclose the next handle ID. */ + + while (omapi_next_handle >= omapi_handle_table -> limit) { + omapi_handle_table_t *new; + + new = malloc (sizeof *new); + if (!new) + return ISC_R_NOMEMORY; + memset (new, 0, sizeof *new); + new -> first = 0; + new -> limit = (omapi_handle_table -> limit * + OMAPI_HANDLE_TABLE_SIZE); + new -> leafp = 0; + new -> children [0].table = omapi_handle_table; + omapi_handle_table = new; + } + + /* Try to cram this handle into the existing table. */ + status = omapi_object_handle_in_table (omapi_next_handle, + omapi_handle_table, o); + /* If it worked, return the next handle and increment it. */ + if (status == ISC_R_SUCCESS) { + *h = omapi_next_handle; + omapi_next_handle++; + return ISC_R_SUCCESS; + } + if (status != ISC_R_NOSPACE) + return status; + + status = omapi_handle_table_enclose (&omapi_handle_table); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_handle_in_table (omapi_next_handle, + omapi_handle_table, o); + if (status != ISC_R_SUCCESS) + return status; + *h = omapi_next_handle; + omapi_next_handle++; + + return ISC_R_SUCCESS; +} + +static isc_result_t omapi_object_handle_in_table (omapi_handle_t h, + omapi_handle_table_t *table, + omapi_object_t *o) +{ + omapi_handle_table_t *inner; + omapi_handle_t scale, index; + isc_result_t status; + + if (table -> first > h || table -> limit <= h) + return ISC_R_NOSPACE; + + /* If this is a leaf table, just stash the object in the + appropriate place. */ + if (table -> leafp) { + status = (omapi_object_reference + (&table -> children [h - table -> first].object, + o, "omapi_object_handle_in_table")); + if (status != ISC_R_SUCCESS) + return status; + o -> handle = h; + return ISC_R_SUCCESS; + } + + /* Scale is the number of handles represented by each child of this + table. For a leaf table, scale would be 1. For a first level + of indirection, 120. For a second, 120 * 120. Et cetera. */ + scale = (table -> limit - table -> first) / OMAPI_HANDLE_TABLE_SIZE; + + /* So the next most direct table from this one that contains the + handle must be the subtable of this table whose index into this + table's array of children is the handle divided by the scale. */ + index = (h - table -> first) / scale; + inner = table -> children [index].table; + + /* If there is no more direct table than this one in the slot + we came up with, make one. */ + if (!inner) { + inner = malloc (sizeof *inner); + if (!inner) + return ISC_R_NOMEMORY; + memset (inner, 0, sizeof *inner); + inner -> first = index * scale + table -> first; + inner -> limit = inner -> first + scale; + if (scale == OMAPI_HANDLE_TABLE_SIZE) + inner -> leafp = 1; + table -> children [index].table = inner; + } + + status = omapi_object_handle_in_table (h, inner, o); + if (status == ISC_R_NOSPACE) { + status = (omapi_handle_table_enclose + (&table -> children [index].table)); + if (status != ISC_R_SUCCESS) + return status; + + return omapi_object_handle_in_table + (h, table -> children [index].table, o); + } + return status; +} + +static isc_result_t omapi_handle_table_enclose (omapi_handle_table_t **table) +{ + omapi_handle_table_t *inner = *table; + omapi_handle_table_t *new; + int index, base, scale; + + /* The scale of the table we're enclosing is going to be the + difference between its "first" and "limit" members. So the + scale of the table enclosing it is going to be that multiplied + by the table size. */ + scale = (inner -> first - inner -> limit) * OMAPI_HANDLE_TABLE_SIZE; + + /* The range that the enclosing table covers is going to be + the result of subtracting the remainder of dividing the + enclosed table's first entry number by the enclosing + table's scale. If handle IDs are being allocated + sequentially, the enclosing table's "first" value will be + the same as the enclosed table's "first" value. */ + base = inner -> first - inner -> first % scale; + + /* The index into the enclosing table at which the enclosed table + will be stored is going to be the difference between the "first" + value of the enclosing table and the enclosed table - zero, if + we are allocating sequentially. */ + index = (base - inner -> first) / OMAPI_HANDLE_TABLE_SIZE; + + new = malloc (sizeof *new); + if (!new) + return ISC_R_NOMEMORY; + memset (new, 0, sizeof *new); + new -> first = base; + new -> limit = base + scale; + if (scale == OMAPI_HANDLE_TABLE_SIZE) + new -> leafp = 0; + new -> children [index].table = inner; + *table = new; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_handle_lookup (omapi_object_t **o, omapi_handle_t h) +{ + return omapi_handle_lookup_in (o, h, omapi_handle_table); +} + +static isc_result_t omapi_handle_lookup_in (omapi_object_t **o, + omapi_handle_t h, + omapi_handle_table_t *table) + +{ + omapi_handle_table_t *inner; + omapi_handle_t scale, index; + + if (!table || table -> first > h || table -> limit <= h) + return ISC_R_NOTFOUND; + + /* If this is a leaf table, just grab the object. */ + if (table -> leafp) { + /* Not there? */ + if (!table -> children [h - table -> first].object) + return ISC_R_NOTFOUND; + return omapi_object_reference + (o, table -> children [h - table -> first].object, + "omapi_handle_lookup_in"); + } + + /* Scale is the number of handles represented by each child of this + table. For a leaf table, scale would be 1. For a first level + of indirection, 120. For a second, 120 * 120. Et cetera. */ + scale = (table -> limit - table -> first) / OMAPI_HANDLE_TABLE_SIZE; + + /* So the next most direct table from this one that contains the + handle must be the subtable of this table whose index into this + table's array of children is the handle divided by the scale. */ + index = (h - table -> first) / scale; + inner = table -> children [index].table; + + return omapi_handle_lookup_in (o, h, table -> children [index].table); +} + +/* For looking up objects based on handles that have been sent on the wire. */ +isc_result_t omapi_handle_td_lookup (omapi_object_t **obj, + omapi_typed_data_t *handle) +{ + isc_result_t status; + omapi_handle_t h; + + if (handle -> type == omapi_datatype_int) + h = handle -> u.integer; + else if (handle -> type == omapi_datatype_data && + handle -> u.buffer.len == sizeof h) { + memcpy (&h, handle -> u.buffer.value, sizeof h); + h = ntohl (h); + } else + return ISC_R_INVALIDARG; + return omapi_handle_lookup (obj, h); +} diff --git a/lib/omapi/listener.c b/lib/omapi/listener.c new file mode 100644 index 0000000000..cb58cbbfaf --- /dev/null +++ b/lib/omapi/listener.c @@ -0,0 +1,254 @@ +/* listener.c + + Subroutines that support the generic listener object. */ + +/* + * Copyright (c) 1996-1999 Internet Software Consortium. + * Use is subject to license terms which appear in the file named + * ISC-LICENSE that should have accompanied this file when you + * received it. If a file named ISC-LICENSE did not accompany this + * file, or you are not sure the one you have is correct, you may + * obtain an applicable copy of the license at: + * + * http://www.isc.org/isc-license-1.0.html. + * + * This file is part of the ISC DHCP distribution. The documentation + * associated with this file is listed in the file DOCUMENTATION, + * included in the top-level directory of this release. + * + * Support and other services are available for ISC products - see + * http://www.isc.org for more information. + */ + +#include + +isc_result_t omapi_listen (omapi_object_t *h, + int port, + int max) +{ + struct hostent *he; + int hix; + isc_result_t status; + omapi_listener_object_t *obj; + + /* Get the handle. */ + obj = (omapi_listener_object_t *)malloc (sizeof *obj); + if (!obj) + return ISC_R_NOMEMORY; + memset (obj, 0, sizeof *obj); + obj -> refcnt = 1; + obj -> type = omapi_type_listener; + + /* Connect this object to the inner object. */ + status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj, + "omapi_protocol_listen"); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_protocol_listen"); + return status; + } + status = omapi_object_reference (&obj -> inner, h, + "omapi_protocol_listen"); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_protocol_listen"); + return status; + } + + /* Set up the address on which we will listen... */ + obj -> address.sin_port = htons (port); + obj -> address.sin_addr.s_addr = htonl (INADDR_ANY); + + /* Create a socket on which to listen. */ + obj -> socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (!obj -> socket) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_listen"); + if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS) + return ISC_R_NORESOURCES; + return ISC_R_UNEXPECTED; + } + + /* Try to bind to the wildcard address using the port number + we were given. */ + if (bind (obj -> socket, + (struct sockaddr *)&obj -> address, sizeof obj -> address)) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_listen"); + if (errno == EADDRINUSE) + return ISC_R_ADDRNOTAVAIL; + if (errno == EPERM) + return ISC_R_NOPERM; + return ISC_R_UNEXPECTED; + } + + /* Now tell the kernel to listen for connections. */ + if (listen (obj -> socket, max)) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_listen"); + return ISC_R_UNEXPECTED; + } + + if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_connect"); + return ISC_R_UNEXPECTED; + } + + status = omapi_register_io_object ((omapi_object_t *)obj, + omapi_listener_readfd, 0, + omapi_accept, 0, 0); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_listen"); + return status; + } + + return ISC_R_SUCCESS; +} + +/* Return the socket on which the dispatcher should wait for readiness + to read, for a listener object. */ +int omapi_listener_readfd (omapi_object_t *h) +{ + omapi_listener_object_t *l; + + if (h -> type != omapi_type_listener) + return -1; + l = (omapi_listener_object_t *)h; + + return l -> socket; +} + +/* Reader callback for a listener object. Accept an incoming connection. */ +isc_result_t omapi_accept (omapi_object_t *h) +{ + isc_result_t status; + int len; + omapi_connection_object_t *obj; + omapi_listener_object_t *listener; + + if (h -> type != omapi_type_listener) + return ISC_R_INVALIDARG; + listener = (omapi_listener_object_t *)h; + + /* Get the handle. */ + obj = (omapi_connection_object_t *)malloc (sizeof *obj); + if (!obj) + return ISC_R_NOMEMORY; + memset (obj, 0, sizeof *obj); + obj -> refcnt = 1; + obj -> type = omapi_type_connection; + + /* Accept the connection. */ + len = sizeof obj -> remote_addr; + obj -> socket = + accept (listener -> socket, + ((struct sockaddr *) + &(obj -> remote_addr)), &len); + if (obj -> socket < 0) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_accept"); + if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS) + return ISC_R_NORESOURCES; + return ISC_R_UNEXPECTED; + } + + obj -> state = omapi_connection_connected; + + status = omapi_register_io_object ((omapi_object_t *)obj, + omapi_connection_readfd, + omapi_connection_writefd, + omapi_connection_reader, + omapi_connection_writer, + omapi_connection_reaper); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_accept"); + return status; + } + + omapi_object_reference (&obj -> listener, (omapi_object_t *)listener, + "omapi_accept"); + + status = omapi_signal (h, "connect", obj); + + /* Lose our reference to the connection, so it'll be gc'd when it's + reaped. */ + omapi_object_dereference ((omapi_object_t **)&obj, "omapi_accept"); + return status; +} + +isc_result_t omapi_listener_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + if (h -> type != omapi_type_listener) + return ISC_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> set_value) + return (*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_listener_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + if (h -> type != omapi_type_listener) + return ISC_R_INVALIDARG; + + 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_listener_destroy (omapi_object_t *h, const char *name) +{ + omapi_listener_object_t *l; + + if (h -> type != omapi_type_listener) + return ISC_R_INVALIDARG; + l = (omapi_listener_object_t *)(h); + + if (l -> socket != -1) { + close (l -> socket); + l -> socket = -1; + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_listener_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + if (h -> type != omapi_type_listener) + return ISC_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_listener_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *l) +{ + int i; + + if (l -> type != omapi_type_listener) + return ISC_R_INVALIDARG; + + if (l -> inner && l -> inner -> type -> stuff_values) + return (*(l -> inner -> type -> stuff_values)) (c, id, + l -> inner); + return ISC_R_SUCCESS; +} + diff --git a/lib/omapi/message.c b/lib/omapi/message.c new file mode 100644 index 0000000000..20e8fb265e --- /dev/null +++ b/lib/omapi/message.c @@ -0,0 +1,675 @@ +/* message.c + + Subroutines for dealing with message objects. */ + +/* + * Copyright (c) 1996-1999 Internet Software Consortium. + * Use is subject to license terms which appear in the file named + * ISC-LICENSE that should have accompanied this file when you + * received it. If a file named ISC-LICENSE did not accompany this + * file, or you are not sure the one you have is correct, you may + * obtain an applicable copy of the license at: + * + * http://www.isc.org/isc-license-1.0.html. + * + * This file is part of the ISC DHCP distribution. The documentation + * associated with this file is listed in the file DOCUMENTATION, + * included in the top-level directory of this release. + * + * Support and other services are available for ISC products - see + * http://www.isc.org for more information. + */ + +#include + +omapi_message_object_t *omapi_registered_messages; + +isc_result_t omapi_message_new (omapi_object_t **o, const char *name) +{ + omapi_message_object_t *m; + omapi_object_t *g; + isc_result_t status; + + m = malloc (sizeof *m); + if (!m) + return ISC_R_NOMEMORY; + memset (m, 0, sizeof *m); + m -> type = omapi_type_message; + m -> refcnt = 1; + + g = (omapi_object_t *)0; + status = omapi_generic_new (&g, name); + if (status != ISC_R_SUCCESS) { + free (m); + return status; + } + status = omapi_object_reference (&m -> inner, g, name); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&m, name); + omapi_object_dereference (&g, name); + return status; + } + status = omapi_object_reference (&g -> outer, + (omapi_object_t *)m, name); + + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&m, name); + omapi_object_dereference (&g, name); + return status; + } + + status = omapi_object_reference (o, (omapi_object_t *)m, name); + omapi_object_dereference ((omapi_object_t **)&m, name); + omapi_object_dereference (&g, name); + if (status != ISC_R_SUCCESS) + return status; + + return status; +} + +isc_result_t omapi_message_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + omapi_message_object_t *m; + isc_result_t status; + + if (h -> type != omapi_type_message) + return ISC_R_INVALIDARG; + m = (omapi_message_object_t *)h; + + /* Can't set authlen. */ + + /* Can set authenticator, but the value must be typed data. */ + if (!omapi_ds_strcmp (name, "authenticator")) { + if (m -> authenticator) + omapi_typed_data_dereference + (&m -> authenticator, + "omapi_message_set_value"); + omapi_typed_data_reference (&m -> authenticator, + value, + "omapi_message_set_value"); + return ISC_R_SUCCESS; + + } else if (!omapi_ds_strcmp (name, "object")) { + if (value -> type != omapi_datatype_object) + return ISC_R_INVALIDARG; + if (m -> object) + omapi_object_dereference + (&m -> object, + "omapi_message_set_value"); + omapi_object_reference (&m -> object, + value -> u.object, + "omapi_message_set_value"); + return ISC_R_SUCCESS; + + } else if (!omapi_ds_strcmp (name, "notify-object")) { + if (value -> type != omapi_datatype_object) + return ISC_R_INVALIDARG; + if (m -> notify_object) + omapi_object_dereference + (&m -> notify_object, + "omapi_message_set_value"); + omapi_object_reference (&m -> notify_object, + value -> u.object, + "omapi_message_set_value"); + return ISC_R_SUCCESS; + + /* Can set authid, but it has to be an integer. */ + } else if (!omapi_ds_strcmp (name, "authid")) { + if (value -> type != omapi_datatype_int) + return ISC_R_INVALIDARG; + m -> authid = value -> u.integer; + return ISC_R_SUCCESS; + + /* Can set op, but it has to be an integer. */ + } else if (!omapi_ds_strcmp (name, "op")) { + if (value -> type != omapi_datatype_int) + return ISC_R_INVALIDARG; + m -> op = value -> u.integer; + return ISC_R_SUCCESS; + + /* Handle also has to be an integer. */ + } else if (!omapi_ds_strcmp (name, "handle")) { + if (value -> type != omapi_datatype_int) + return ISC_R_INVALIDARG; + m -> h = value -> u.integer; + return ISC_R_SUCCESS; + + /* Transaction ID has to be an integer. */ + } else if (!omapi_ds_strcmp (name, "id")) { + if (value -> type != omapi_datatype_int) + return ISC_R_INVALIDARG; + m -> id = value -> u.integer; + return ISC_R_SUCCESS; + + /* Remote transaction ID has to be an integer. */ + } else if (!omapi_ds_strcmp (name, "rid")) { + if (value -> type != omapi_datatype_int) + return ISC_R_INVALIDARG; + m -> rid = value -> u.integer; + return ISC_R_SUCCESS; + } + + /* Try to find some inner object that can take the value. */ + if (h -> inner && h -> inner -> type -> set_value) { + status = ((*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value)); + if (status == ISC_R_SUCCESS) + return status; + } + + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_message_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + omapi_message_object_t *m; + if (h -> type != omapi_type_message) + return ISC_R_INVALIDARG; + m = (omapi_message_object_t *)h; + + /* Look for values that are in the message data structure. */ + if (!omapi_ds_strcmp (name, "authlen")) + return omapi_make_int_value (value, name, (int)m -> authlen, + "omapi_message_get_value"); + else if (!omapi_ds_strcmp (name, "authenticator")) { + if (m -> authenticator) + return omapi_make_value (value, + name, m -> authenticator, + "omapi_message_get_value"); + else + return ISC_R_NOTFOUND; + } else if (!omapi_ds_strcmp (name, "authid")) { + return omapi_make_int_value (value, name, (int)m -> authid, + "omapi_message_get_value"); + } else if (!omapi_ds_strcmp (name, "op")) { + return omapi_make_int_value (value, name, (int)m -> op, + "omapi_message_get_value"); + } else if (!omapi_ds_strcmp (name, "handle")) { + return omapi_make_int_value (value, name, (int)m -> handle, + "omapi_message_get_value"); + } else if (!omapi_ds_strcmp (name, "id")) { + return omapi_make_int_value (value, name, (int)m -> id, + "omapi_message_get_value"); + } else if (!omapi_ds_strcmp (name, "rid")) { + return omapi_make_int_value (value, name, (int)m -> rid, + "omapi_message_get_value"); + } + + /* See if there's an inner object that has the value. */ + 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_message_destroy (omapi_object_t *h, const char *name) +{ + int i; + + omapi_message_object_t *m; + if (h -> type != omapi_type_message) + return ISC_R_INVALIDARG; + if (m -> authenticator) { + omapi_typed_data_dereference (&m -> authenticator, name); + } + if (!m -> prev && omapi_registered_messages != m) + omapi_message_unregister (h); + if (m -> prev) + omapi_object_dereference ((omapi_object_t **)&m -> prev, name); + if (m -> next) + omapi_object_dereference ((omapi_object_t **)&m -> next, name); + if (m -> id_object) + omapi_object_dereference ((omapi_object_t **)&m -> id_object, + name); + if (m -> object) + omapi_object_dereference ((omapi_object_t **)&m -> object, + name); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_message_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + omapi_message_object_t *m; + if (h -> type != omapi_type_message) + return ISC_R_INVALIDARG; + m = (omapi_message_object_t *)h; + + if (!strcmp (name, "status") && + (m -> object || m -> notify_object)) { + if (m -> object) + return ((m -> object -> type -> signal_handler)) + (m -> object, name, ap); + else + return ((m -> notify_object -> type -> signal_handler)) + (m -> notify_object, name, ap); + } + 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_message_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *m) +{ + int i; + + if (m -> type != omapi_type_message) + return ISC_R_INVALIDARG; + + if (m -> inner && m -> inner -> type -> stuff_values) + return (*(m -> inner -> type -> stuff_values)) (c, id, + m -> inner); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_message_register (omapi_object_t *mo) +{ + omapi_message_object_t *m; + + if (mo -> type != omapi_type_message) + return ISC_R_INVALIDARG; + m = (omapi_message_object_t *)mo; + + /* Already registered? */ + if (m -> prev || m -> next || omapi_registered_messages == m) + return ISC_R_INVALIDARG; + + if (omapi_registered_messages) { + omapi_object_reference + ((omapi_object_t **)&m -> next, + (omapi_object_t *)omapi_registered_messages, + "omapi_message_register"); + omapi_object_reference + ((omapi_object_t **)&omapi_registered_messages -> prev, + (omapi_object_t *)m, "omapi_message_register"); + omapi_object_dereference + ((omapi_object_t **)&omapi_registered_messages, + "omapi_message_register"); + } + omapi_object_reference + ((omapi_object_t **)&omapi_registered_messages, + (omapi_object_t *)m, "omapi_message_register"); + return ISC_R_SUCCESS;; +} + +isc_result_t omapi_message_unregister (omapi_object_t *mo) +{ + omapi_message_object_t *m; + omapi_message_object_t *n; + + if (mo -> type != omapi_type_message) + return ISC_R_INVALIDARG; + m = (omapi_message_object_t *)mo; + + /* Not registered? */ + if (!m -> prev && omapi_registered_messages != m) + return ISC_R_INVALIDARG; + + n = (omapi_message_object_t *)0; + if (m -> next) { + omapi_object_reference ((omapi_object_t **)&n, + (omapi_object_t *)m -> next, + "omapi_message_unregister"); + omapi_object_dereference ((omapi_object_t **)&m -> next, + "omapi_message_unregister"); + } + if (m -> prev) { + omapi_message_object_t *tmp = (omapi_message_object_t *)0; + omapi_object_reference ((omapi_object_t **)&tmp, + (omapi_object_t *)m -> prev, + "omapi_message_register"); + omapi_object_dereference ((omapi_object_t **)&m -> prev, + "omapi_message_unregister"); + if (tmp -> next) + omapi_object_dereference + ((omapi_object_t **)&tmp -> next, + "omapi_message_unregister"); + if (n) + omapi_object_reference + ((omapi_object_t **)&tmp -> next, + (omapi_object_t *)n, + "omapi_message_unregister"); + omapi_object_dereference ((omapi_object_t **)&tmp, + "omapi_message_unregister"); + } else { + omapi_object_dereference + ((omapi_object_t **)&omapi_registered_messages, + "omapi_unregister_message"); + if (n) + omapi_object_reference + ((omapi_object_t **)&omapi_registered_messages, + (omapi_object_t *)n, + "omapi_message_unregister"); + } + if (n) + omapi_object_dereference ((omapi_object_t **)&n, + "omapi_message_unregister"); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_message_process (omapi_object_t *mo, omapi_object_t *po) +{ + omapi_message_object_t *message, *m; + omapi_object_t *object = (omapi_object_t *)0; + omapi_value_t *tv = (omapi_value_t *)0; + unsigned long create, update, exclusive; + unsigned long wsi; + isc_result_t status, waitstatus; + omapi_object_type_t *type; + + if (mo -> type != omapi_type_message) + return ISC_R_INVALIDARG; + message = (omapi_message_object_t *)mo; + + if (message -> rid) { + for (m = omapi_registered_messages; m; m = m -> next) + if (m -> id == message -> rid) + break; + /* If we don't have a real message corresponding to + the message ID to which this message claims it is a + response, something's fishy. */ + if (!m) + return ISC_R_NOTFOUND; + } else + m = (omapi_message_object_t *)0; + + switch (message -> op) { + case OMAPI_OP_OPEN: + if (m) { + return omapi_protocol_send_status + (po, (omapi_object_t *)0, ISC_R_INVALIDARG, + message -> id, "OPEN can't be a response"); + } + + /* Get the type of the requested object, if one was + specified. */ + status = omapi_get_value_str (mo, (omapi_object_t *)0, + "type", &tv); + if (status == ISC_R_SUCCESS && + (tv -> value -> type == omapi_datatype_data || + tv -> value -> type == omapi_datatype_string)) { + for (type = omapi_object_types; + type; type = type -> next) + if (!omapi_td_strcmp (tv -> value, + type -> name)) + break; + } else + type = (omapi_object_type_t *)0; + if (tv) + omapi_value_dereference (&tv, + "omapi_message_process"); + + /* Get the create flag. */ + status = omapi_get_value_str (mo, + (omapi_object_t *)0, + "create", &tv); + if (status == ISC_R_SUCCESS) { + status = omapi_get_int_value (&create, tv -> value); + omapi_value_dereference (&tv, + "omapi_message_process"); + if (status != ISC_R_SUCCESS) { + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + status, message -> id, + "invalid create flag value"); + } + } else + create = 0; + + /* Get the update flag. */ + status = omapi_get_value_str (mo, + (omapi_object_t *)0, + "update", &tv); + if (status == ISC_R_SUCCESS) { + status = omapi_get_int_value (&update, tv -> value); + omapi_value_dereference (&tv, + "omapi_message_process"); + if (status != ISC_R_SUCCESS) { + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + status, message -> id, + "invalid update flag value"); + } + } else + update = 0; + + /* Get the exclusive flag. */ + status = omapi_get_value_str (mo, + (omapi_object_t *)0, + "exclusive", &tv); + if (status == ISC_R_SUCCESS) { + status = omapi_get_int_value (&exclusive, tv -> value); + omapi_value_dereference (&tv, + "omapi_message_process"); + if (status != ISC_R_SUCCESS) { + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + status, message -> id, + "invalid exclusive flag value"); + } + } else + exclusive = 0; + + /* If we weren't given a type, look the object up with + the handle. */ + if (!type) { + if (create) { + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + ISC_R_INVALIDARG, message -> id, + "type required on create"); + } + goto refresh; + } + + /* If the type doesn't provide a lookup method, we can't + look up the object. */ + if (!type -> lookup) { + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + ISC_R_NOTIMPLEMENTED, message -> id, + "unsearchable object type"); + } + + if (!message -> object) { + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + ISC_R_NOTFOUND, message -> id, + "no lookup key specified"); + } + status = (*(type -> lookup)) (&object, (omapi_object_t *)0, + message -> object); + + if (status != ISC_R_SUCCESS && + status != ISC_R_NOTFOUND && + status != ISC_R_NOKEYS) { + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + status, message -> id, + "object lookup failed"); + } + + /* If we didn't find the object and we aren't supposed to + create it, return an error. */ + if (status == ISC_R_NOTFOUND && !create) { + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + ISC_R_NOTFOUND, message -> id, + "no object matches specification"); + } + + /* If we found an object, we're supposed to be creating an + object, and we're not supposed to have found an object, + return an error. */ + if (status == ISC_R_SUCCESS && create && exclusive) { + omapi_object_dereference + (&object, "omapi_message_process"); + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + ISC_R_EXISTS, message -> id, + "specified object already exists"); + } + + /* If we're creating the object, do it now. */ + if (!object) { + status = omapi_object_create (&object, + (omapi_object_t *)0, + type); + if (status != ISC_R_SUCCESS) { + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + status, message -> id, + "can't create new object"); + } + } + + /* If we're updating it, do so now. */ + if (create || update) { + status = omapi_object_update (object, + (omapi_object_t *)0, + message -> object, + message -> handle); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference + (&object, "omapi_message_process"); + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + status, message -> id, + "can't update object"); + } + } + + /* Now send the new contents of the object back in + response. */ + goto send; + + case OMAPI_OP_REFRESH: + refresh: + status = omapi_handle_lookup (&object, + message -> handle); + if (status != ISC_R_SUCCESS) { + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + status, message -> id, + "no matching handle"); + } + send: + status = omapi_protocol_send_update (po, (omapi_object_t *)0, + message -> id, object); + omapi_object_dereference (&object, + "omapi_message_process"); + return status; + + case OMAPI_OP_UPDATE: + if (m -> object) { + omapi_object_reference (&object, m -> object, + "omapi_message_process"); + } else { + status = omapi_handle_lookup (&object, + message -> handle); + if (status != ISC_R_SUCCESS) { + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + status, message -> id, + "no matching handle"); + } + } + + status = omapi_object_update (object, (omapi_object_t *)0, + message -> object, + message -> handle); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference + (&object, "omapi_message_process"); + if (!message -> rid) + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + status, message -> id, + "can't update object"); + if (m) + omapi_signal ((omapi_object_t *)m, + "status", status, + (omapi_typed_data_t *)0); + return ISC_R_SUCCESS; + } + if (!message -> rid) + status = omapi_protocol_send_status + (po, (omapi_object_t *)0, ISC_R_SUCCESS, + message -> id, (char *)0); + if (m) + omapi_signal ((omapi_object_t *)m, + "status", ISC_R_SUCCESS, + (omapi_typed_data_t *)0); + return status; + + case OMAPI_OP_NOTIFY: + return omapi_protocol_send_status + (po, (omapi_object_t *)0, ISC_R_NOTIMPLEMENTED, + message -> id, "notify not implemented yet"); + + case OMAPI_OP_STATUS: + /* The return status of a request. */ + if (!m) + return ISC_R_UNEXPECTED; + + /* Get the wait status. */ + status = omapi_get_value_str (mo, + (omapi_object_t *)0, + "result", &tv); + if (status == ISC_R_SUCCESS) { + status = omapi_get_int_value (&wsi, tv -> value); + waitstatus = wsi; + omapi_value_dereference (&tv, + "omapi_message_process"); + if (status != ISC_R_SUCCESS) + waitstatus = ISC_R_UNEXPECTED; + } else + waitstatus = ISC_R_UNEXPECTED; + + status = omapi_get_value_str (mo, + (omapi_object_t *)0, + "message", &tv); + omapi_signal ((omapi_object_t *)m, "status", waitstatus, tv); + if (status == ISC_R_SUCCESS) + omapi_value_dereference (&tv, "omapi_message_process"); + return ISC_R_SUCCESS; + + case OMAPI_OP_DELETE: + status = omapi_handle_lookup (&object, + message -> handle); + if (status != ISC_R_SUCCESS) { + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + status, message -> id, + "no matching handle"); + } + + if (!object -> type -> remove) + return omapi_protocol_send_status + (po, (omapi_object_t *)0, + ISC_R_NOTIMPLEMENTED, message -> id, + "no remove method for object"); + + status = (*(object -> type -> remove)) (object, + (omapi_object_t *)0); + omapi_object_dereference (&object, + "omapi_message_process"); + + return omapi_protocol_send_status (po, (omapi_object_t *)0, + status, message -> id, + (char *)0); + } + return ISC_R_NOTIMPLEMENTED; +} diff --git a/lib/omapi/protocol.c b/lib/omapi/protocol.c new file mode 100644 index 0000000000..cfb59c4eda --- /dev/null +++ b/lib/omapi/protocol.c @@ -0,0 +1,817 @@ +/* protocol.c + + Functions supporting the object management protocol... */ + +/* + * Copyright (c) 1996-1999 Internet Software Consortium. + * Use is subject to license terms which appear in the file named + * ISC-LICENSE that should have accompanied this file when you + * received it. If a file named ISC-LICENSE did not accompany this + * file, or you are not sure the one you have is correct, you may + * obtain an applicable copy of the license at: + * + * http://www.isc.org/isc-license-1.0.html. + * + * This file is part of the ISC DHCP distribution. The documentation + * associated with this file is listed in the file DOCUMENTATION, + * included in the top-level directory of this release. + * + * Support and other services are available for ISC products - see + * http://www.isc.org for more information. + */ + +#include + +isc_result_t omapi_protocol_connect (omapi_object_t *h, + const char *server_name, + int port, + omapi_object_t *authinfo) +{ + isc_result_t status; + omapi_protocol_object_t *obj; + + obj = (omapi_protocol_object_t *)malloc (sizeof *obj); + if (!obj) + return ISC_R_NOMEMORY; + memset (obj, 0, sizeof *obj); + obj -> refcnt = 1; + obj -> type = omapi_type_protocol; + + status = omapi_connect ((omapi_object_t *)obj, server_name, port); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_protocol_connect"); + return status; + } + status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj, + "omapi_protocol_connect"); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_protocol_connect"); + return status; + } + status = omapi_object_reference (&obj -> inner, h, + "omapi_protocol_connect"); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_protocol_connect"); + return status; + } + + /* Send the introductory message. */ + status = omapi_protocol_send_intro ((omapi_object_t *)obj, + OMAPI_PROTOCOL_VERSION, + sizeof (omapi_protocol_header_t)); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_protocol_connect"); + return status; + } + + if (authinfo) + omapi_object_reference (&obj -> authinfo, authinfo, + "omapi_protocol_connect"); + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_protocol_accept"); + return ISC_R_SUCCESS; +} + +/* Send the protocol introduction message. */ +isc_result_t omapi_protocol_send_intro (omapi_object_t *h, + unsigned ver, + unsigned hsize) +{ + isc_result_t status; + omapi_protocol_object_t *p; + + if (h -> type != omapi_type_protocol) + return ISC_R_INVALIDARG; + p = (omapi_protocol_object_t *)h; + + if (!h -> outer || h -> outer -> type != omapi_type_connection) + return ISC_R_NOTCONNECTED; + + status = omapi_connection_put_uint32 (h -> outer, ver); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_connection_put_uint32 (h -> outer, hsize); + + if (status != ISC_R_SUCCESS) + return status; + + /* Require the other end to send an intro - this kicks off the + protocol input state machine. */ + p -> state = omapi_protocol_intro_wait; + status = omapi_connection_require (h -> outer, 8); + if (status != ISC_R_SUCCESS && status != ISC_R_NOTYET) + return status; + + /* Make up an initial transaction ID for this connection. */ + p -> next_xid = random (); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_protocol_send_message (omapi_object_t *po, + omapi_object_t *id, + omapi_object_t *mo, + omapi_object_t *omo) +{ + omapi_protocol_object_t *p; + omapi_object_t *c; + omapi_message_object_t *m; + omapi_message_object_t *om; + isc_result_t status; + u_int32_t foo; + + if (po -> type != omapi_type_protocol || + !po -> outer || po -> outer -> type != omapi_type_connection || + mo -> type != omapi_type_message) + return ISC_R_INVALIDARG; + if (omo && omo -> type != omapi_type_message) + return ISC_R_INVALIDARG; + p = (omapi_protocol_object_t *)po; + c = (omapi_object_t *)(po -> outer); + m = (omapi_message_object_t *)mo; + om = (omapi_message_object_t *)omo; + + /* XXX Write the authenticator length */ + status = omapi_connection_put_uint32 (c, 0); + if (status != ISC_R_SUCCESS) + return status; + /* XXX Write the ID of the authentication key we're using. */ + status = omapi_connection_put_uint32 (c, 0); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Write the opcode. */ + status = omapi_connection_put_uint32 (c, m -> op); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Write the handle. If we've been given an explicit handle, use + that. Otherwise, use the handle of the object we're sending. + The caller is responsible for arranging for one of these handles + to be set (or not). */ + status = omapi_connection_put_uint32 (c, (m -> h + ? m -> h + : (m -> object + ? m -> object -> handle + : 0))); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Set and write the transaction ID. */ + m -> id = p -> next_xid++; + status = omapi_connection_put_uint32 (c, m -> id); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Write the transaction ID of the message to which this is a + response, if there is such a message. */ + status = omapi_connection_put_uint32 (c, om ? om -> id : m -> rid); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Stuff out the name/value pairs specific to this message. */ + status = omapi_stuff_values (c, id, (omapi_object_t *)m); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Write the zero-length name that terminates the list of name/value + pairs specific to the message. */ + status = omapi_connection_put_uint16 (c, 0); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Stuff out all the published name/value pairs in the object that's + being sent in the message, if there is one. */ + if (m -> object) { + status = omapi_stuff_values (c, id, m -> object); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + } + + /* Write the zero-length name that terminates the list of name/value + pairs for the associated object. */ + status = omapi_connection_put_uint16 (c, 0); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* XXX Write the authenticator... */ + + return ISC_R_SUCCESS; +} + + +isc_result_t omapi_protocol_signal_handler (omapi_object_t *h, + const char *name, va_list ap) +{ + isc_result_t status; + omapi_protocol_object_t *p; + omapi_object_t *c; + u_int16_t nlen; + u_int32_t vlen; + + if (h -> type != omapi_type_protocol) { + /* XXX shouldn't happen. Put an assert here? */ + return ISC_R_UNEXPECTED; + } + p = (omapi_protocol_object_t *)h; + + /* Not a signal we recognize? */ + if (strcmp (name, "ready")) { + if (p -> inner && p -> inner -> type -> signal_handler) + return (*(p -> inner -> type -> signal_handler)) (h, + name, + ap); + return ISC_R_NOTFOUND; + } + + if (!p -> outer || p -> outer -> type != omapi_type_connection) + return ISC_R_INVALIDARG; + c = p -> outer; + + /* We get here because we requested that we be woken up after + some number of bytes were read, and that number of bytes + has in fact been read. */ + switch (p -> state) { + case omapi_protocol_intro_wait: + /* Get protocol version and header size in network + byte order. */ + omapi_connection_get_uint32 + (c, (u_int32_t *)&p -> protocol_version); + omapi_connection_get_uint32 + (c, (u_int32_t *)&p -> header_size); + + /* We currently only support the current protocol version. */ + if (p -> protocol_version != OMAPI_PROTOCOL_VERSION) { + omapi_disconnect (c, 1); + return ISC_R_VERSIONMISMATCH; + } + + if (p -> header_size < sizeof (omapi_protocol_header_t)) { + omapi_disconnect (c, 1); + return ISC_R_PROTOCOLERROR; + } + + status = omapi_signal_in (h -> inner, "ready"); + + to_header_wait: + /* The next thing we're expecting is a message header. */ + p -> state = omapi_protocol_header_wait; + + /* Register a need for the number of bytes in a + header, and if we already have that many, process + them immediately. */ + if ((omapi_connection_require + (c, p -> header_size)) != ISC_R_SUCCESS) + break; + /* If we already have the data, fall through. */ + + case omapi_protocol_header_wait: + status = omapi_message_new ((omapi_object_t **)&p -> message, + "omapi_protocol_signal_handler"); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + + /* Swap in the header... */ + omapi_connection_get_uint32 + (c, (u_int32_t *)&p -> message -> authid); + + /* XXX bind the authenticator here! */ + omapi_connection_get_uint32 + (c, (u_int32_t *)&p -> message -> authlen); + omapi_connection_get_uint32 + (c, (u_int32_t *)&p -> message -> op); + omapi_connection_get_uint32 + (c, (u_int32_t *)&p -> message -> handle); + omapi_connection_get_uint32 + (c, (u_int32_t *)&p -> message -> id); + omapi_connection_get_uint32 + (c, (u_int32_t *)&p -> message -> rid); + + /* If there was any extra header data, skip over it. */ + if (p -> header_size > sizeof (omapi_protocol_header_t)) { + omapi_connection_copyout + (0, c, (p -> header_size - + sizeof (omapi_protocol_header_t))); + } + + /* XXX must compute partial signature across the + XXX preceding bytes. Also, if authenticator + specifies encryption as well as signing, we may + have to decrypt the data on the way in. */ + + /* First we read in message-specific values, then object + values. */ + p -> reading_message_values = 1; + + need_name_length: + /* The next thing we're expecting is length of the + first name. */ + p -> state = omapi_protocol_name_length_wait; + + /* Wait for a 16-bit length. */ + if ((omapi_connection_require (c, 2)) != ISC_R_SUCCESS) + break; + /* If it's already here, fall through. */ + + case omapi_protocol_name_length_wait: + omapi_connection_get_uint16 (c, &nlen); + /* A zero-length name means that we're done reading name+value + pairs. */ + if (nlen == 0) { + /* If we've already read in the object, we are + done reading the message, but if we've just + finished reading in the values associated + with the message, we need to read the + object. */ + if (p -> reading_message_values) { + p -> reading_message_values = 0; + goto need_name_length; + } + + /* If the authenticator length is zero, there's no + signature to read in, so go straight to processing + the message. */ + if (p -> message -> authlen == 0) + goto message_done; + + /* The next thing we're expecting is the + message signature. */ + p -> state = omapi_protocol_signature_wait; + + /* Wait for the number of bytes specified for + the authenticator. If we already have it, + go read it in. */ + if (omapi_connection_require + (c, p -> message -> authlen) == ISC_R_SUCCESS) + goto signature_wait; + break; + } + + /* Allocate a buffer for the name. */ + status = (omapi_data_string_new + (&p -> name, nlen, "omapi_protocol_signal_handler")); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return ISC_R_NOMEMORY; + } + p -> state = omapi_protocol_name_wait; + if (omapi_connection_require (c, nlen) != ISC_R_SUCCESS) + break; + /* If it's already here, fall through. */ + + case omapi_protocol_name_wait: + omapi_connection_copyout (p -> name -> value, c, + p -> name -> len); + /* Wait for a 32-bit length. */ + p -> state = omapi_protocol_value_length_wait; + if ((omapi_connection_require (c, 4)) != ISC_R_SUCCESS) + break; + /* If it's already here, fall through. */ + + case omapi_protocol_value_length_wait: + omapi_connection_get_uint32 (c, &vlen); + + /* Zero-length values are allowed - if we get one, we + don't have to read any data for the value - just + get the next one, if there is a next one. */ + if (!vlen) + goto insert_new_value; + + status = (omapi_typed_data_new + (&p -> value, omapi_datatype_data, vlen, + "omapi_protocol_signal_handler")); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return ISC_R_NOMEMORY; + } + + p -> state = omapi_protocol_value_wait; + if (omapi_connection_require (c, vlen) != ISC_R_SUCCESS) + break; + /* If it's already here, fall through. */ + + case omapi_protocol_value_wait: + omapi_connection_copyout (p -> value -> u.buffer.value, c, + p -> value -> u.buffer.len); + + insert_new_value: + if (p -> reading_message_values) { + status = (omapi_set_value + ((omapi_object_t *)p -> message, + p -> message -> id_object, + p -> name, p -> value)); + } else { + if (!p -> message -> object) { + /* We need a generic object to hang off of the + incoming message. */ + status = (omapi_generic_new + (&p -> message -> object, + "omapi_protocol_signal_handler")); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + } + status = (omapi_set_value + ((omapi_object_t *)p -> message -> object, + p -> message -> id_object, + p -> name, p -> value)); + } + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return status; + } + omapi_data_string_dereference + (&p -> name, "omapi_protocol_signal_handler"); + omapi_typed_data_dereference (&p -> value, + "omapi_protocol_signal_handler"); + goto need_name_length; + + signature_wait: + case omapi_protocol_signature_wait: + status = omapi_typed_data_new (&p -> message -> authenticator, + omapi_datatype_data, + p -> message -> authlen); + + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return ISC_R_NOMEMORY; + } + omapi_connection_copyout + (p -> message -> authenticator -> u.buffer.value, c, + p -> message -> authlen); + /* XXX now do something to verify the signature. */ + + /* Process the message. */ + message_done: + status = omapi_message_process ((omapi_object_t *)p -> message, + h); + if (status != ISC_R_SUCCESS) { + omapi_disconnect (c, 1); + return ISC_R_NOMEMORY; + } + + /* XXX unbind the authenticator. */ + auth_unbind: + omapi_object_dereference ((omapi_object_t **)&p -> message, + "omapi_protocol_signal_handler"); + + /* Now wait for the next message. */ + goto to_header_wait; + + default: + /* XXX should never get here. Assertion? */ + break; + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_protocol_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + if (h -> type != omapi_type_protocol) + return ISC_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> set_value) + return (*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_protocol_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + if (h -> type != omapi_type_protocol) + return ISC_R_INVALIDARG; + + 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_protocol_destroy (omapi_object_t *h, const char *name) +{ + omapi_protocol_object_t *p; + if (h -> type != omapi_type_protocol) + return ISC_R_INVALIDARG; + p = (omapi_protocol_object_t *)h; + if (p -> message) + omapi_object_dereference ((omapi_object_t **)&p -> message, + name); + if (p -> authinfo) + return omapi_object_dereference (&p -> authinfo, name); + return ISC_R_SUCCESS; +} + +/* Write all the published values associated with the object through the + specified connection. */ + +isc_result_t omapi_protocol_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *p) +{ + int i; + + if (p -> type != omapi_type_protocol) + return ISC_R_INVALIDARG; + + if (p -> inner && p -> inner -> type -> stuff_values) + return (*(p -> inner -> type -> stuff_values)) (c, id, + p -> inner); + return ISC_R_SUCCESS; +} + +/* Set up a listener for the omapi protocol. The handle stored points to + a listener object, not a protocol object. */ + +isc_result_t omapi_protocol_listen (omapi_object_t *h, + int port, + int max) +{ + isc_result_t status; + omapi_protocol_listener_object_t *obj; + + obj = (omapi_protocol_listener_object_t *)malloc (sizeof *obj); + if (!obj) + return ISC_R_NOMEMORY; + memset (obj, 0, sizeof *obj); + obj -> refcnt = 1; + obj -> type = omapi_type_protocol_listener; + + status = omapi_object_reference (&h -> outer, (omapi_object_t *)obj, + "omapi_protocol_listen"); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_protocol_listen"); + return status; + } + status = omapi_object_reference (&obj -> inner, h, + "omapi_protocol_listen"); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_protocol_listen"); + return status; + } + + status = omapi_listen ((omapi_object_t *)obj, port, max); + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_protocol_listen"); + return status; +} + +/* Signal handler for protocol listener - if we get a connect signal, + create a new protocol connection, otherwise pass the signal down. */ + +isc_result_t omapi_protocol_listener_signal (omapi_object_t *o, + const char *name, va_list ap) +{ + isc_result_t status; + omapi_object_t *c; + omapi_protocol_object_t *obj; + omapi_protocol_listener_object_t *p; + + if (!o || o -> type != omapi_type_protocol_listener) + return ISC_R_INVALIDARG; + p = (omapi_protocol_listener_object_t *)o; + + /* Not a signal we recognize? */ + if (strcmp (name, "connect")) { + if (p -> inner && p -> inner -> type -> signal_handler) + return (*(p -> inner -> type -> signal_handler)) + (p -> inner, name, ap); + return ISC_R_NOTFOUND; + } + + c = va_arg (ap, omapi_object_t *); + if (!c || c -> type != omapi_type_connection) + return ISC_R_INVALIDARG; + + obj = (omapi_protocol_object_t *)malloc (sizeof *obj); + if (!obj) + return ISC_R_NOMEMORY; + memset (obj, 0, sizeof *obj); + obj -> refcnt = 1; + obj -> type = omapi_type_protocol; + + status = omapi_object_reference (&obj -> outer, c, + "omapi_protocol_accept"); + if (status != ISC_R_SUCCESS) { + lose: + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_protocol_accept"); + omapi_disconnect (c, 1); + return status; + } + + status = omapi_object_reference (&c -> inner, (omapi_object_t *)obj, + "omapi_protocol_accept"); + if (status != ISC_R_SUCCESS) + goto lose; + + /* Send the introductory message. */ + status = omapi_protocol_send_intro ((omapi_object_t *)obj, + OMAPI_PROTOCOL_VERSION, + sizeof (omapi_protocol_header_t)); + if (status != ISC_R_SUCCESS) + goto lose; + + omapi_object_dereference ((omapi_object_t **)&obj, + "omapi_protocol_accept"); + return status; +} + +isc_result_t omapi_protocol_listener_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + if (h -> type != omapi_type_protocol_listener) + return ISC_R_INVALIDARG; + + if (h -> inner && h -> inner -> type -> set_value) + return (*(h -> inner -> type -> set_value)) + (h -> inner, id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_protocol_listener_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + if (h -> type != omapi_type_protocol_listener) + return ISC_R_INVALIDARG; + + 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_protocol_listener_destroy (omapi_object_t *h, + const char *name) +{ + if (h -> type != omapi_type_protocol_listener) + return ISC_R_INVALIDARG; + return ISC_R_SUCCESS; +} + +/* Write all the published values associated with the object through the + specified connection. */ + +isc_result_t omapi_protocol_listener_stuff (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *p) +{ + int i; + + if (p -> type != omapi_type_protocol_listener) + return ISC_R_INVALIDARG; + + if (p -> inner && p -> inner -> type -> stuff_values) + return (*(p -> inner -> type -> stuff_values)) (c, id, + p -> inner); + return ISC_R_SUCCESS; +} + +isc_result_t omapi_protocol_send_status (omapi_object_t *po, + omapi_object_t *id, + isc_result_t waitstatus, + unsigned rid, const char *msg) +{ + isc_result_t status; + omapi_object_t *message = (omapi_object_t *)0; + + if (po -> type != omapi_type_protocol) + return ISC_R_INVALIDARG; + + status = omapi_message_new (&message, "omapi_protocol_send_status"); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_set_int_value (message, (omapi_object_t *)0, + "op", OMAPI_OP_STATUS); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, + "omapi_protocol_send_status"); + return status; + } + + status = omapi_set_int_value (message, (omapi_object_t *)0, + "rid", (int)rid); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, + "omapi_protocol_send_status"); + return status; + } + + status = omapi_set_int_value (message, (omapi_object_t *)0, + "result", (int)waitstatus); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, + "omapi_protocol_send_status"); + return status; + } + + /* If a message has been provided, send it. */ + if (msg) { + status = omapi_set_string_value (message, (omapi_object_t *)0, + "message", msg); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference + (&message, "omapi_protocol_send_status"); + return status; + } + } + + return omapi_protocol_send_message (po, + id, message, (omapi_object_t *)0); +} + +isc_result_t omapi_protocol_send_update (omapi_object_t *po, + omapi_object_t *id, + unsigned rid, + omapi_object_t *object) +{ + isc_result_t status; + omapi_object_t *message = (omapi_object_t *)0; + + if (po -> type != omapi_type_protocol) + return ISC_R_INVALIDARG; + + status = omapi_message_new (&message, "omapi_protocol_send_update"); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_set_int_value (message, (omapi_object_t *)0, + "op", OMAPI_OP_UPDATE); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, + "omapi_protocol_send_update"); + return status; + } + + if (rid) { + omapi_handle_t handle; + status = omapi_set_int_value (message, (omapi_object_t *)0, + "rid", (int)rid); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference + (&message, "omapi_protocol_send_update"); + return status; + } + + status = omapi_object_handle (&handle, object); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference + (&message, "omapi_protocol_send_update"); + return status; + } + status = omapi_set_int_value (message, (omapi_object_t *)0, + "handle", (int)handle); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference + (&message, "omapi_protocol_send_update"); + return status; + } + } + + status = omapi_set_object_value (message, (omapi_object_t *)0, + "object", object); + if (status != ISC_R_SUCCESS) { + omapi_object_dereference (&message, "dhcpctl_open_object"); + return status; + } + + return omapi_protocol_send_message (po, + id, message, (omapi_object_t *)0); +} diff --git a/lib/omapi/result.c b/lib/omapi/result.c new file mode 100644 index 0000000000..0f9349edea --- /dev/null +++ b/lib/omapi/result.c @@ -0,0 +1,81 @@ +/* result.c + + Cheap knock-off of libisc result table code. This is just a place-holder + until the actual libisc merge. */ + +/* + * Copyright (c) 1996-1999 Internet Software Consortium. + * Use is subject to license terms which appear in the file named + * ISC-LICENSE that should have accompanied this file when you + * received it. If a file named ISC-LICENSE did not accompany this + * file, or you are not sure the one you have is correct, you may + * obtain an applicable copy of the license at: + * + * http://www.isc.org/isc-license-1.0.html. + * + * This file is part of the ISC DHCP distribution. The documentation + * associated with this file is listed in the file DOCUMENTATION, + * included in the top-level directory of this release. + * + * Support and other services are available for ISC products - see + * http://www.isc.org for more information. + */ + +#include + +static const char *text[ISC_R_NRESULTS] = { + "success", /* 0 */ + "out of memory", /* 1 */ + "timed out", /* 2 */ + "no available threads", /* 3 */ + "address not available", /* 4 */ + "address in use", /* 5 */ + "permission denied", /* 6 */ + "no pending connections", /* 7 */ + "network unreachable", /* 8 */ + "host unreachable", /* 9 */ + "network down", /* 10 */ + "host down", /* 11 */ + "connection refused", /* 12 */ + "not enough free resources", /* 13 */ + "end of file", /* 14 */ + "socket already bound", /* 15 */ + "task is done", /* 16 */ + "lock busy", /* 17 */ + "already exists", /* 18 */ + "ran out of space", /* 19 */ + "operation canceled", /* 20 */ + "sending events is not allowed", /* 21 */ + "shutting down", /* 22 */ + "not found", /* 23 */ + "unexpected end of input", /* 24 */ + "failure", /* 25 */ + "I/O error", /* 26 */ + "not implemented", /* 27 */ + "unbalanced parentheses", /* 28 */ + "no more", /* 29 */ + "invalid file", /* 30 */ + "bad base64 encoding", /* 31 */ + "unexpected token", /* 32 */ + "quota reached", /* 33 */ + "unexpected error", /* 34 */ + "already running", /* 35 */ + "host unknown", /* 36 */ + "protocol version mismatch", /* 37 */ + "protocol error", /* 38 */ + "invalid argument", /* 39 */ + "not connected", /* 40 */ + "data not yet available", /* 41 */ + "object unchanged", /* 42 */ + "more than one object matches key", /* 43 */ + "key conflict", /* 44 */ + "parse error(s) occurred", /* 45 */ + "no key specified", /* 46 */ +}; + +const char *isc_result_totext (isc_result_t result) +{ + if (result >= ISC_R_SUCCESS && result < ISC_R_NRESULTS) + return text [result]; + return "unknown error."; +} diff --git a/lib/omapi/support.c b/lib/omapi/support.c new file mode 100644 index 0000000000..dfd06704aa --- /dev/null +++ b/lib/omapi/support.c @@ -0,0 +1,682 @@ +/* support.c + + Subroutines providing general support for objects. */ + +/* + * Copyright (c) 1996-1999 Internet Software Consortium. + * Use is subject to license terms which appear in the file named + * ISC-LICENSE that should have accompanied this file when you + * received it. If a file named ISC-LICENSE did not accompany this + * file, or you are not sure the one you have is correct, you may + * obtain an applicable copy of the license at: + * + * http://www.isc.org/isc-license-1.0.html. + * + * This file is part of the ISC DHCP distribution. The documentation + * associated with this file is listed in the file DOCUMENTATION, + * included in the top-level directory of this release. + * + * Support and other services are available for ISC products - see + * http://www.isc.org for more information. + */ + +#include + +omapi_object_type_t *omapi_type_connection; +omapi_object_type_t *omapi_type_listener; +omapi_object_type_t *omapi_type_io_object; +omapi_object_type_t *omapi_type_datagram; +omapi_object_type_t *omapi_type_generic; +omapi_object_type_t *omapi_type_protocol; +omapi_object_type_t *omapi_type_protocol_listener; +omapi_object_type_t *omapi_type_waiter; +omapi_object_type_t *omapi_type_remote; +omapi_object_type_t *omapi_type_message; + +omapi_object_type_t *omapi_object_types; +int omapi_object_type_count; +static int ot_max; + +isc_result_t omapi_init (void) +{ + isc_result_t status; + + /* Register all the standard object types... */ + status = omapi_object_type_register (&omapi_type_connection, + "connection", + omapi_connection_set_value, + omapi_connection_get_value, + omapi_connection_destroy, + omapi_connection_signal_handler, + omapi_connection_stuff_values, + 0, 0, 0); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&omapi_type_listener, + "listener", + omapi_listener_set_value, + omapi_listener_get_value, + omapi_listener_destroy, + omapi_listener_signal_handler, + omapi_listener_stuff_values, + 0, 0, 0); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&omapi_type_io_object, + "io", + omapi_io_set_value, + omapi_io_get_value, + omapi_io_destroy, + omapi_io_signal_handler, + omapi_io_stuff_values, + 0, 0, 0); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&omapi_type_generic, + "generic", + omapi_generic_set_value, + omapi_generic_get_value, + omapi_generic_destroy, + omapi_generic_signal_handler, + omapi_generic_stuff_values, + 0, 0, 0); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&omapi_type_protocol, + "protocol", + omapi_protocol_set_value, + omapi_protocol_get_value, + omapi_protocol_destroy, + omapi_protocol_signal_handler, + omapi_protocol_stuff_values, + 0, 0, 0); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&omapi_type_protocol_listener, + "protocol-listener", + omapi_protocol_listener_set_value, + omapi_protocol_listener_get_value, + omapi_protocol_listener_destroy, + omapi_protocol_listener_signal, + omapi_protocol_listener_stuff, + 0, 0, 0); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&omapi_type_message, + "message", + omapi_message_set_value, + omapi_message_get_value, + omapi_message_destroy, + omapi_message_signal_handler, + omapi_message_stuff_values, + 0, 0, 0); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_object_type_register (&omapi_type_waiter, + "waiter", + 0, + 0, + 0, + omapi_waiter_signal_handler, 0, + 0, 0, 0); + if (status != ISC_R_SUCCESS) + return status; + + /* This seems silly, but leave it. */ + return ISC_R_SUCCESS; +} + +isc_result_t omapi_object_type_register (omapi_object_type_t **type, + const char *name, + isc_result_t (*set_value) + (omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_typed_data_t *), + isc_result_t (*get_value) + (omapi_object_t *, + omapi_object_t *, + omapi_data_string_t *, + omapi_value_t **), + isc_result_t (*destroy) + (omapi_object_t *, + const char *), + isc_result_t (*signal_handler) + (omapi_object_t *, + const char *, va_list), + isc_result_t (*stuff_values) + (omapi_object_t *, + omapi_object_t *, + omapi_object_t *), + isc_result_t (*lookup) + (omapi_object_t **, + omapi_object_t *, + omapi_object_t *), + isc_result_t (*create) + (omapi_object_t **, + omapi_object_t *), + isc_result_t (*remove) + (omapi_object_t *, + omapi_object_t *)) +{ + omapi_object_type_t *t; + + t = malloc (sizeof *t); + if (!t) + return ISC_R_NOMEMORY; + memset (t, 0, sizeof *t); + + t -> name = name; + t -> set_value = set_value; + t -> get_value = get_value; + t -> destroy = destroy; + t -> signal_handler = signal_handler; + t -> stuff_values = stuff_values; + t -> lookup = lookup; + t -> create = create; + t -> remove = remove; + t -> next = omapi_object_types; + omapi_object_types = t; + if (type) + *type = t; + return ISC_R_SUCCESS; +} + +isc_result_t omapi_signal (omapi_object_t *handle, const char *name, ...) +{ + va_list ap; + omapi_object_t *outer; + isc_result_t status; + + va_start (ap, name); + for (outer = handle; outer -> outer; outer = outer -> outer) + ; + if (outer -> type -> signal_handler) + status = (*(outer -> type -> signal_handler)) (outer, + name, ap); + else + status = ISC_R_NOTFOUND; + va_end (ap); + return status; +} + +isc_result_t omapi_signal_in (omapi_object_t *handle, const char *name, ...) +{ + va_list ap; + omapi_object_t *outer; + isc_result_t status; + + if (!handle) + return ISC_R_NOTFOUND; + va_start (ap, name); + + if (handle -> type -> signal_handler) + status = (*(handle -> type -> signal_handler)) (handle, + name, ap); + else + status = ISC_R_NOTFOUND; + va_end (ap); + return status; +} + +isc_result_t omapi_set_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_typed_data_t *value) +{ + omapi_object_t *outer; + + for (outer = h; outer -> outer; outer = outer -> outer) + ; + if (outer -> type -> set_value) + return (*(outer -> type -> set_value)) (outer, + id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_set_value_str (omapi_object_t *h, + omapi_object_t *id, + const char *name, + omapi_typed_data_t *value) +{ + omapi_object_t *outer; + omapi_data_string_t *nds; + isc_result_t status; + + nds = (omapi_data_string_t *)0; + status = omapi_data_string_new (&nds, strlen (name), + "omapi_set_value_str"); + if (status != ISC_R_SUCCESS) + return status; + memcpy (nds -> value, name, strlen (name)); + + return omapi_set_value (h, id, nds, value); +} + +isc_result_t omapi_set_boolean_value (omapi_object_t *h, omapi_object_t *id, + const char *name, int value) +{ + isc_result_t status; + omapi_typed_data_t *tv = (omapi_typed_data_t *)0; + omapi_data_string_t *n = (omapi_data_string_t *)0; + int len; + int ip; + + status = omapi_data_string_new (&n, strlen (name), + "omapi_set_boolean_value"); + if (status != ISC_R_SUCCESS) + return status; + memcpy (n -> value, name, strlen (name)); + + status = omapi_typed_data_new (&tv, omapi_datatype_int, value); + if (status != ISC_R_SUCCESS) { + omapi_data_string_dereference (&n, + "omapi_set_boolean_value"); + return status; + } + + status = omapi_set_value (h, id, n, tv); + omapi_data_string_dereference (&n, "omapi_set_boolean_value"); + omapi_typed_data_dereference (&tv, "omapi_set_boolean_value"); + return status; +} + +isc_result_t omapi_set_int_value (omapi_object_t *h, omapi_object_t *id, + const char *name, int value) +{ + isc_result_t status; + omapi_typed_data_t *tv = (omapi_typed_data_t *)0; + omapi_data_string_t *n = (omapi_data_string_t *)0; + int len; + int ip; + + status = omapi_data_string_new (&n, strlen (name), + "omapi_set_int_value"); + if (status != ISC_R_SUCCESS) + return status; + memcpy (n -> value, name, strlen (name)); + + status = omapi_typed_data_new (&tv, omapi_datatype_int, value); + if (status != ISC_R_SUCCESS) { + omapi_data_string_dereference (&n, + "omapi_set_int_value"); + return status; + } + + status = omapi_set_value (h, id, n, tv); + omapi_data_string_dereference (&n, "omapi_set_int_value"); + omapi_typed_data_dereference (&tv, "omapi_set_int_value"); + return status; +} + +isc_result_t omapi_set_object_value (omapi_object_t *h, omapi_object_t *id, + const char *name, omapi_object_t *value) +{ + isc_result_t status; + omapi_typed_data_t *tv = (omapi_typed_data_t *)0; + omapi_data_string_t *n = (omapi_data_string_t *)0; + int len; + int ip; + + status = omapi_data_string_new (&n, strlen (name), + "omapi_set_object_value"); + if (status != ISC_R_SUCCESS) + return status; + memcpy (n -> value, name, strlen (name)); + + status = omapi_typed_data_new (&tv, omapi_datatype_object, value); + if (status != ISC_R_SUCCESS) { + omapi_data_string_dereference (&n, + "omapi_set_object_value"); + return status; + } + + status = omapi_set_value (h, id, n, tv); + omapi_data_string_dereference (&n, "omapi_set_object_value"); + omapi_typed_data_dereference (&tv, "omapi_set_object_value"); + return status; +} + +isc_result_t omapi_set_string_value (omapi_object_t *h, omapi_object_t *id, + const char *name, const char *value) +{ + isc_result_t status; + omapi_typed_data_t *tv = (omapi_typed_data_t *)0; + omapi_data_string_t *n = (omapi_data_string_t *)0; + int len; + int ip; + + status = omapi_data_string_new (&n, strlen (name), + "omapi_set_string_value"); + if (status != ISC_R_SUCCESS) + return status; + memcpy (n -> value, name, strlen (name)); + + status = omapi_typed_data_new (&tv, omapi_datatype_string, value); + if (status != ISC_R_SUCCESS) { + omapi_data_string_dereference (&n, + "omapi_set_string_value"); + return status; + } + + status = omapi_set_value (h, id, n, tv); + omapi_data_string_dereference (&n, "omapi_set_string_value"); + omapi_typed_data_dereference (&tv, "omapi_set_string_value"); + return status; +} + +isc_result_t omapi_get_value (omapi_object_t *h, + omapi_object_t *id, + omapi_data_string_t *name, + omapi_value_t **value) +{ + omapi_object_t *outer; + + for (outer = h; outer -> outer; outer = outer -> outer) + ; + if (outer -> type -> get_value) + return (*(outer -> type -> get_value)) (outer, + id, name, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_get_value_str (omapi_object_t *h, + omapi_object_t *id, + const char *name, + omapi_value_t **value) +{ + omapi_object_t *outer; + omapi_data_string_t *nds; + isc_result_t status; + + nds = (omapi_data_string_t *)0; + status = omapi_data_string_new (&nds, strlen (name), + "omapi_get_value_str"); + if (status != ISC_R_SUCCESS) + return status; + memcpy (nds -> value, name, strlen (name)); + + for (outer = h; outer -> outer; outer = outer -> outer) + ; + if (outer -> type -> get_value) + return (*(outer -> type -> get_value)) (outer, + id, nds, value); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_stuff_values (omapi_object_t *c, + omapi_object_t *id, + omapi_object_t *o) +{ + omapi_object_t *outer; + + for (outer = o; outer -> outer; outer = outer -> outer) + ; + if (outer -> type -> stuff_values) + return (*(outer -> type -> stuff_values)) (c, id, outer); + return ISC_R_NOTFOUND; +} + +isc_result_t omapi_object_create (omapi_object_t **obj, omapi_object_t *id, + omapi_object_type_t *type) +{ + if (!type -> create) + return ISC_R_NOTIMPLEMENTED; + return (*(type -> create)) (obj, id); +} + +isc_result_t omapi_object_update (omapi_object_t *obj, omapi_object_t *id, + omapi_object_t *src, omapi_handle_t handle) +{ + omapi_generic_object_t *gsrc; + isc_result_t status; + int i; + + if (!src) + return ISC_R_INVALIDARG; + if (src -> type != omapi_type_generic) + return ISC_R_NOTIMPLEMENTED; + gsrc = (omapi_generic_object_t *)src; + for (i = 0; i < gsrc -> nvalues; i++) { + status = omapi_set_value (obj, id, + gsrc -> values [i] -> name, + gsrc -> values [i] -> value); + if (status != ISC_R_SUCCESS) + return status; + } + if (handle) + omapi_set_int_value (obj, id, "remote-handle", (int)handle); + status = omapi_signal (obj, "updated"); + if (status != ISC_R_NOTFOUND) + return status; + return ISC_R_SUCCESS; +} + +int omapi_data_string_cmp (omapi_data_string_t *s1, omapi_data_string_t *s2) +{ + unsigned len; + int rv; + + if (s1 -> len > s2 -> len) + len = s2 -> len; + else + len = s1 -> len; + rv = memcmp (s1 -> value, s2 -> value, len); + if (rv) + return rv; + if (s1 -> len > s2 -> len) + return 1; + else if (s1 -> len < s2 -> len) + return -1; + return 0; +} + +int omapi_ds_strcmp (omapi_data_string_t *s1, const char *s2) +{ + unsigned len, slen; + int rv; + + slen = strlen (s2); + if (slen > s1 -> len) + len = s1 -> len; + else + len = slen; + rv = memcmp (s1 -> value, s2, len); + if (rv) + return rv; + if (s1 -> len > slen) + return 1; + else if (s1 -> len < slen) + return -1; + return 0; +} + +int omapi_td_strcmp (omapi_typed_data_t *s1, const char *s2) +{ + unsigned len, slen; + int rv; + + /* If the data type is not compatible, never equal. */ + if (s1 -> type != omapi_datatype_data && + s1 -> type != omapi_datatype_string) + return -1; + + slen = strlen (s2); + if (slen > s1 -> u.buffer.len) + len = s1 -> u.buffer.len; + else + len = slen; + rv = memcmp (s1 -> u.buffer.value, s2, len); + if (rv) + return rv; + if (s1 -> u.buffer.len > slen) + return 1; + else if (s1 -> u.buffer.len < slen) + return -1; + return 0; +} + +isc_result_t omapi_make_value (omapi_value_t **vp, omapi_data_string_t *name, + omapi_typed_data_t *value, const char *caller) +{ + isc_result_t status; + + status = omapi_value_new (vp, caller); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_data_string_reference (&(*vp) -> name, name, caller); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, caller); + return status; + } + if (value) { + status = omapi_typed_data_reference (&(*vp) -> value, + value, caller); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, caller); + return status; + } + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_make_const_value (omapi_value_t **vp, + omapi_data_string_t *name, + const unsigned char *value, + unsigned len, const char *caller) +{ + isc_result_t status; + + status = omapi_value_new (vp, caller); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_data_string_reference (&(*vp) -> name, name, caller); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, caller); + return status; + } + if (value) { + status = omapi_typed_data_new (&(*vp) -> value, + omapi_datatype_data, len); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, caller); + return status; + } + memcpy ((*vp) -> value -> u.buffer.value, value, len); + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_make_int_value (omapi_value_t **vp, + omapi_data_string_t *name, + int value, const char *caller) +{ + isc_result_t status; + + status = omapi_value_new (vp, caller); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_data_string_reference (&(*vp) -> name, name, caller); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, caller); + return status; + } + if (value) { + status = omapi_typed_data_new (&(*vp) -> value, + omapi_datatype_int); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, caller); + return status; + } + (*vp) -> value -> u.integer = value; + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_make_handle_value (omapi_value_t **vp, + omapi_data_string_t *name, + omapi_object_t *value, + const char *caller) +{ + isc_result_t status; + + status = omapi_value_new (vp, caller); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_data_string_reference (&(*vp) -> name, name, caller); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, caller); + return status; + } + if (value) { + status = omapi_typed_data_new (&(*vp) -> value, + omapi_datatype_int); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, caller); + return status; + } + status = (omapi_object_handle + ((omapi_handle_t *)&(*vp) -> value -> u.integer, + value)); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, caller); + return status; + } + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_make_string_value (omapi_value_t **vp, + omapi_data_string_t *name, + char *value, const char *caller) +{ + isc_result_t status; + + status = omapi_value_new (vp, caller); + if (status != ISC_R_SUCCESS) + return status; + + status = omapi_data_string_reference (&(*vp) -> name, name, caller); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, caller); + return status; + } + if (value) { + status = omapi_typed_data_new (&(*vp) -> value, + omapi_datatype_string, value); + if (status != ISC_R_SUCCESS) { + omapi_value_dereference (vp, caller); + return status; + } + } + return ISC_R_SUCCESS; +} + +isc_result_t omapi_get_int_value (unsigned long *v, omapi_typed_data_t *t) +{ + u_int32_t rv; + + if (t -> type == omapi_datatype_int) { + *v = t -> u.integer; + return ISC_R_SUCCESS; + } else if (t -> type == omapi_datatype_string || + t -> type == omapi_datatype_data) { + if (t -> u.buffer.len != sizeof (rv)) + return ISC_R_INVALIDARG; + memcpy (&rv, t -> u.buffer.value, sizeof rv); + *v = ntohl (rv); + return ISC_R_SUCCESS; + } + return ISC_R_INVALIDARG; +} diff --git a/lib/omapi/test.c b/lib/omapi/test.c new file mode 100644 index 0000000000..d8065d9417 --- /dev/null +++ b/lib/omapi/test.c @@ -0,0 +1,85 @@ +/* test.c + + Test code for omapip... */ + +/* + * Copyright (c) 1996-1999 Internet Software Consortium. + * Use is subject to license terms which appear in the file named + * ISC-LICENSE that should have accompanied this file when you + * received it. If a file named ISC-LICENSE did not accompany this + * file, or you are not sure the one you have is correct, you may + * obtain an applicable copy of the license at: + * + * http://www.isc.org/isc-license-1.0.html. + * + * This file is part of the ISC DHCP distribution. The documentation + * associated with this file is listed in the file DOCUMENTATION, + * included in the top-level directory of this release. + * + * Support and other services are available for ISC products - see + * http://www.isc.org for more information. + */ + +#include +#include +#include +#include +#include +#include + +int main (int argc, char **argv) +{ + omapi_object_t *listener = (omapi_object_t*)0; + omapi_object_t *connection = (omapi_object_t*)0; + isc_result_t status; + + omapi_init (); + + if (argc > 1 && !strcmp (argv [1], "listen")) { + if (argc < 3) { + fprintf (stderr, "Usage: test listen port\n"); + exit (1); + } + status = omapi_generic_new (&listener, "main"); + if (status != ISC_R_SUCCESS) { + fprintf (stderr, "omapi_generic_new: %s\n", + isc_result_totext (status)); + exit (1); + } + status = omapi_protocol_listen (listener, + atoi (argv [2]), 1); + if (status != ISC_R_SUCCESS) { + fprintf (stderr, "omapi_listen: %s\n", + isc_result_totext (status)); + exit (1); + } + omapi_dispatch (0); + } else if (argc > 1 && !strcmp (argv [1], "connect")) { + if (argc < 4) { + fprintf (stderr, "Usage: test listen address port\n"); + exit (1); + } + status = omapi_generic_new (&connection, "main"); + if (status != ISC_R_SUCCESS) { + fprintf (stderr, "omapi_generic_new: %s\n", + isc_result_totext (status)); + exit (1); + } + status = omapi_protocol_connect (connection, + argv [2], atoi (argv [3]), 0); + fprintf (stderr, "connect: %s\n", isc_result_totext (status)); + if (status != ISC_R_SUCCESS) + exit (1); + status = omapi_wait_for_completion (connection, 0); + fprintf (stderr, "completion: %s\n", + isc_result_totext (status)); + if (status != ISC_R_SUCCESS) + exit (1); + /* ... */ + } else { + fprintf (stderr, "Usage: test [listen | connect] ...\n"); + exit (1); + } + + return 0; +}