aboutsummaryrefslogtreecommitdiff
path: root/contrib/isc-dhcp/omapip/connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/isc-dhcp/omapip/connection.c')
-rw-r--r--contrib/isc-dhcp/omapip/connection.c1026
1 files changed, 1026 insertions, 0 deletions
diff --git a/contrib/isc-dhcp/omapip/connection.c b/contrib/isc-dhcp/omapip/connection.c
new file mode 100644
index 000000000000..77ce113d22fa
--- /dev/null
+++ b/contrib/isc-dhcp/omapip/connection.c
@@ -0,0 +1,1026 @@
+/* connection.c
+
+ Subroutines for dealing with connections. */
+
+/*
+ * Copyright (c) 1999-2001 Internet Software Consortium.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of The Internet Software Consortium nor the names
+ * of its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This software has been written for the Internet Software Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about the Internet Software Consortium, see
+ * ``http://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include <omapip/omapip_p.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+
+#if defined (TRACING)
+static void trace_connect_input (trace_type_t *, unsigned, char *);
+static void trace_connect_stop (trace_type_t *);
+static void trace_disconnect_input (trace_type_t *, unsigned, char *);
+static void trace_disconnect_stop (trace_type_t *);
+trace_type_t *trace_connect;
+trace_type_t *trace_disconnect;
+extern omapi_array_t *trace_listeners;
+#endif
+static isc_result_t omapi_connection_connect_internal (omapi_object_t *);
+
+OMAPI_OBJECT_ALLOC (omapi_connection,
+ omapi_connection_object_t, omapi_type_connection)
+
+isc_result_t omapi_connect (omapi_object_t *c,
+ const char *server_name,
+ unsigned port)
+{
+ struct hostent *he;
+ unsigned i, hix;
+ omapi_addr_list_t *addrs = (omapi_addr_list_t *)0;
+ struct in_addr foo;
+ isc_result_t status;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_connect(%s, port=%d)", server_name, port);
+#endif
+
+ if (!inet_aton (server_name, &foo)) {
+ /* 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)
+ return ISC_R_HOSTUNKNOWN;
+ for (i = 0; he -> h_addr_list [i]; i++)
+ ;
+ if (i == 0)
+ return ISC_R_HOSTUNKNOWN;
+ hix = i;
+
+ status = omapi_addr_list_new (&addrs, hix, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ for (i = 0; i < hix; i++) {
+ addrs -> addresses [i].addrtype = he -> h_addrtype;
+ addrs -> addresses [i].addrlen = he -> h_length;
+ memcpy (addrs -> addresses [i].address,
+ he -> h_addr_list [i],
+ (unsigned)he -> h_length);
+ addrs -> addresses [i].port = port;
+ }
+ } else {
+ status = omapi_addr_list_new (&addrs, 1, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ addrs -> addresses [0].addrtype = AF_INET;
+ addrs -> addresses [0].addrlen = sizeof foo;
+ memcpy (addrs -> addresses [0].address, &foo, sizeof foo);
+ addrs -> addresses [0].port = port;
+ hix = 1;
+ }
+ status = omapi_connect_list (c, addrs, (omapi_addr_t *)0);
+ omapi_addr_list_dereference (&addrs, MDL);
+ return status;
+}
+
+isc_result_t omapi_connect_list (omapi_object_t *c,
+ omapi_addr_list_t *remote_addrs,
+ omapi_addr_t *local_addr)
+{
+ isc_result_t status;
+ omapi_connection_object_t *obj;
+ int flag;
+ struct sockaddr_in local_sin;
+#if defined (TRACING)
+ trace_addr_t *addrs;
+ u_int16_t naddrs;
+#endif
+
+ obj = (omapi_connection_object_t *)0;
+ status = omapi_connection_allocate (&obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_object_reference (&c -> outer, (omapi_object_t *)obj,
+ MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_connection_dereference (&obj, MDL);
+ return status;
+ }
+ status = omapi_object_reference (&obj -> inner, c, MDL);
+ if (status != ISC_R_SUCCESS) {
+ omapi_connection_dereference (&obj, MDL);
+ return status;
+ }
+
+ /* Store the address list on the object. */
+ omapi_addr_list_reference (&obj -> connect_list, remote_addrs, MDL);
+ obj -> cptr = 0;
+ obj -> state = omapi_connection_unconnected;
+
+#if defined (TRACING)
+ /* If we're playing back, don't actually try to connect - just leave
+ the object available for a subsequent connect or disconnect. */
+ if (!trace_playback ()) {
+#endif
+ /* Create a socket on which to communicate. */
+ obj -> socket =
+ socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (obj -> socket < 0) {
+ omapi_connection_dereference (&obj, MDL);
+ if (errno == EMFILE || errno == ENFILE
+ || errno == ENOBUFS)
+ return ISC_R_NORESOURCES;
+ return ISC_R_UNEXPECTED;
+ }
+
+ /* Set up the local address, if any. */
+ if (local_addr) {
+ /* Only do TCPv4 so far. */
+ if (local_addr -> addrtype != AF_INET) {
+ omapi_connection_dereference (&obj, MDL);
+ return ISC_R_INVALIDARG;
+ }
+ local_sin.sin_port = htons (local_addr -> port);
+ memcpy (&local_sin.sin_addr,
+ local_addr -> address,
+ local_addr -> addrlen);
+#if defined (HAVE_SA_LEN)
+ local_sin.sin_len = sizeof local_addr;
+#endif
+ local_sin.sin_family = AF_INET;
+ memset (&local_sin.sin_zero, 0,
+ sizeof local_sin.sin_zero);
+
+ if (bind (obj -> socket, (struct sockaddr *)&local_sin,
+ sizeof local_sin) < 0) {
+ omapi_object_dereference ((omapi_object_t **)
+ &obj, MDL);
+ if (errno == EADDRINUSE)
+ return ISC_R_ADDRINUSE;
+ if (errno == EADDRNOTAVAIL)
+ return ISC_R_ADDRNOTAVAIL;
+ if (errno == EACCES)
+ return ISC_R_NOPERM;
+ return ISC_R_UNEXPECTED;
+ }
+ obj -> local_addr = local_sin;
+ }
+
+#if defined (HAVE_SETFD)
+ if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
+ close (obj -> socket);
+ omapi_connection_dereference (&obj, MDL);
+ return ISC_R_UNEXPECTED;
+ }
+#endif
+
+ /* 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_connection_dereference (&obj, MDL);
+ return ISC_R_UNEXPECTED;
+ }
+
+ /* Set the file to nonblocking mode. */
+ if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) {
+ omapi_connection_dereference (&obj, MDL);
+ return ISC_R_UNEXPECTED;
+ }
+
+ status = (omapi_register_io_object
+ ((omapi_object_t *)obj,
+ 0, omapi_connection_writefd,
+ 0, omapi_connection_connect,
+ omapi_connection_reaper));
+ if (status != ISC_R_SUCCESS)
+ goto out;
+ status = omapi_connection_connect_internal ((omapi_object_t *)
+ obj);
+#if defined (TRACING)
+ }
+ omapi_connection_register (obj, MDL);
+#endif
+
+ out:
+ omapi_connection_dereference (&obj, MDL);
+ return status;
+}
+
+#if defined (TRACING)
+omapi_array_t *omapi_connections;
+
+OMAPI_ARRAY_TYPE(omapi_connection, omapi_connection_object_t)
+
+void omapi_connection_trace_setup (void) {
+ trace_connect = trace_type_register ("connect", (void *)0,
+ trace_connect_input,
+ trace_connect_stop, MDL);
+ trace_disconnect = trace_type_register ("disconnect", (void *)0,
+ trace_disconnect_input,
+ trace_disconnect_stop, MDL);
+}
+
+void omapi_connection_register (omapi_connection_object_t *obj,
+ const char *file, int line)
+{
+ isc_result_t status;
+ trace_iov_t iov [6];
+ int iov_count = 0;
+ int32_t connect_index, listener_index;
+ static int32_t index;
+
+ if (!omapi_connections) {
+ status = omapi_connection_array_allocate (&omapi_connections,
+ file, line);
+ if (status != ISC_R_SUCCESS)
+ return;
+ }
+
+ status = omapi_connection_array_extend (omapi_connections, obj,
+ (int *)0, file, line);
+ if (status != ISC_R_SUCCESS) {
+ obj -> index = -1;
+ return;
+ }
+
+#if defined (TRACING)
+ if (trace_record ()) {
+ /* Connection registration packet:
+
+ int32_t index
+ int32_t listener_index [-1 means no listener]
+ u_int16_t remote_port
+ u_int16_t local_port
+ u_int32_t remote_addr
+ u_int32_t local_addr */
+
+ connect_index = htonl (index);
+ index++;
+ if (obj -> listener)
+ listener_index = htonl (obj -> listener -> index);
+ else
+ listener_index = htonl (-1);
+ iov [iov_count].buf = (char *)&connect_index;
+ iov [iov_count++].len = sizeof connect_index;
+ iov [iov_count].buf = (char *)&listener_index;
+ iov [iov_count++].len = sizeof listener_index;
+ iov [iov_count].buf = (char *)&obj -> remote_addr.sin_port;
+ iov [iov_count++].len = sizeof obj -> remote_addr.sin_port;
+ iov [iov_count].buf = (char *)&obj -> local_addr.sin_port;
+ iov [iov_count++].len = sizeof obj -> local_addr.sin_port;
+ iov [iov_count].buf = (char *)&obj -> remote_addr.sin_addr;
+ iov [iov_count++].len = sizeof obj -> remote_addr.sin_addr;
+ iov [iov_count].buf = (char *)&obj -> local_addr.sin_addr;
+ iov [iov_count++].len = sizeof obj -> local_addr.sin_addr;
+
+ status = trace_write_packet_iov (trace_connect,
+ iov_count, iov, file, line);
+ }
+#endif
+}
+
+static void trace_connect_input (trace_type_t *ttype,
+ unsigned length, char *buf)
+{
+ struct sockaddr_in remote, local;
+ int32_t connect_index, listener_index;
+ char *s = buf;
+ omapi_connection_object_t *obj;
+ isc_result_t status;
+ int i;
+
+ if (length != ((sizeof connect_index) +
+ (sizeof remote.sin_port) +
+ (sizeof remote.sin_addr)) * 2) {
+ log_error ("Trace connect: invalid length %d", length);
+ return;
+ }
+
+ memset (&remote, 0, sizeof remote);
+ memset (&local, 0, sizeof local);
+ memcpy (&connect_index, s, sizeof connect_index);
+ s += sizeof connect_index;
+ memcpy (&listener_index, s, sizeof listener_index);
+ s += sizeof listener_index;
+ memcpy (&remote.sin_port, s, sizeof remote.sin_port);
+ s += sizeof remote.sin_port;
+ memcpy (&local.sin_port, s, sizeof local.sin_port);
+ s += sizeof local.sin_port;
+ memcpy (&remote.sin_addr, s, sizeof remote.sin_addr);
+ s += sizeof remote.sin_addr;
+ memcpy (&local.sin_addr, s, sizeof local.sin_addr);
+ s += sizeof local.sin_addr;
+
+ connect_index = ntohl (connect_index);
+ listener_index = ntohl (listener_index);
+
+ /* If this was a connect to a listener, then we just slap together
+ a new connection. */
+ if (listener_index != -1) {
+ omapi_listener_object_t *listener;
+ listener = (omapi_listener_object_t *)0;
+ omapi_array_foreach_begin (trace_listeners,
+ omapi_listener_object_t, lp) {
+ if (lp -> address.sin_port == local.sin_port) {
+ omapi_listener_reference (&listener, lp, MDL);
+ omapi_listener_dereference (&lp, MDL);
+ break;
+ }
+ } omapi_array_foreach_end (trace_listeners,
+ omapi_listener_object_t, lp);
+ if (!listener) {
+ log_error ("%s%ld, addr %s, port %d",
+ "Spurious traced listener connect - index ",
+ (long int)listener_index,
+ inet_ntoa (local.sin_addr),
+ ntohs (local.sin_port));
+ return;
+ }
+ obj = (omapi_connection_object_t *)0;
+ status = omapi_listener_connect (&obj, listener, -1, &remote);
+ if (status != ISC_R_SUCCESS) {
+ log_error ("traced listener connect: %s",
+ isc_result_totext (status));
+ }
+ if (obj)
+ omapi_connection_dereference (&obj, MDL);
+ omapi_listener_dereference (&listener, MDL);
+ return;
+ }
+
+ /* Find the matching connect object, if there is one. */
+ omapi_array_foreach_begin (omapi_connections,
+ omapi_connection_object_t, lp) {
+ for (i = 0; (lp -> connect_list &&
+ i < lp -> connect_list -> count); i++) {
+ if (!memcmp (&remote.sin_addr,
+ &lp -> connect_list -> addresses [i].address,
+ sizeof remote.sin_addr) &&
+ (ntohs (remote.sin_port) ==
+ lp -> connect_list -> addresses [i].port))
+ lp -> state = omapi_connection_connected;
+ lp -> remote_addr = remote;
+ lp -> remote_addr.sin_family = AF_INET;
+#if defined (HAVE_SIN_LEN)
+ lp -> remote_addr.sin_len = sizeof remote;
+#endif
+ omapi_addr_list_dereference (&lp -> connect_list, MDL);
+ lp -> index = connect_index;
+ status = omapi_signal_in ((omapi_object_t *)lp,
+ "connect");
+ omapi_connection_dereference (&lp, MDL);
+ return;
+ }
+ } omapi_array_foreach_end (omapi_connections,
+ omapi_connection_object_t, lp);
+
+ log_error ("Spurious traced connect - index %ld, addr %s, port %d",
+ (long int)connect_index, inet_ntoa (remote.sin_addr),
+ ntohs (remote.sin_port));
+ return;
+}
+
+static void trace_connect_stop (trace_type_t *ttype) { }
+
+static void trace_disconnect_input (trace_type_t *ttype,
+ unsigned length, char *buf)
+{
+ int32_t *index;
+ if (length != sizeof *index) {
+ log_error ("trace disconnect: wrong length %d", length);
+ return;
+ }
+
+ index = (int32_t *)buf;
+
+ omapi_array_foreach_begin (omapi_connections,
+ omapi_connection_object_t, lp) {
+ if (lp -> index == ntohl (*index)) {
+ omapi_disconnect ((omapi_object_t *)lp, 1);
+ omapi_connection_dereference (&lp, MDL);
+ return;
+ }
+ } omapi_array_foreach_end (omapi_connections,
+ omapi_connection_object_t, lp);
+
+ log_error ("trace disconnect: no connection matching index %ld",
+ (long int)ntohl (*index));
+}
+
+static void trace_disconnect_stop (trace_type_t *ttype) { }
+#endif
+
+/* 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;
+ isc_result_t status;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_disconnect(%s)", force ? "force" : "");
+#endif
+
+ c = (omapi_connection_object_t *)h;
+ if (c -> type != omapi_type_connection)
+ return ISC_R_INVALIDARG;
+
+#if defined (TRACING)
+ if (trace_record ()) {
+ int32_t index;
+
+ index = htonl (c -> index);
+ status = trace_write_packet (trace_disconnect,
+ sizeof index, (char *)&index,
+ MDL);
+ if (status != ISC_R_SUCCESS) {
+ trace_stop ();
+ log_error ("trace_write_packet: %s",
+ isc_result_totext (status));
+ }
+ }
+ if (!trace_playback ()) {
+#endif
+ 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);
+#if defined (TRACING)
+ }
+#endif
+ c -> state = omapi_connection_closed;
+
+ /* Disconnect from I/O object, if any. */
+ if (h -> outer) {
+ if (h -> outer -> inner)
+ omapi_object_dereference (&h -> outer -> inner, MDL);
+ omapi_object_dereference (&h -> outer, MDL);
+ }
+
+ /* 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 -> state == omapi_connection_connecting)
+ return c -> socket;
+ if (c -> out_bytes)
+ return c -> socket;
+ else
+ return -1;
+}
+
+isc_result_t omapi_connection_connect (omapi_object_t *h)
+{
+ isc_result_t status;
+
+ status = omapi_connection_connect_internal (h);
+ if (status != ISC_R_SUCCESS)
+ omapi_signal (h, "status", status);
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t omapi_connection_connect_internal (omapi_object_t *h)
+{
+ int error;
+ omapi_connection_object_t *c;
+ SOCKLEN_T sl;
+ isc_result_t status;
+
+ if (h -> type != omapi_type_connection)
+ return ISC_R_INVALIDARG;
+ c = (omapi_connection_object_t *)h;
+
+ if (c -> state == omapi_connection_connecting) {
+ sl = sizeof error;
+ if (getsockopt (c -> socket, SOL_SOCKET, SO_ERROR,
+ (char *)&error, &sl) < 0) {
+ omapi_disconnect (h, 1);
+ return ISC_R_SUCCESS;
+ }
+ if (!error)
+ c -> state = omapi_connection_connected;
+ }
+ if (c -> state == omapi_connection_connecting ||
+ c -> state == omapi_connection_unconnected) {
+ if (c -> cptr >= c -> connect_list -> count) {
+ switch (error) {
+ case ECONNREFUSED:
+ status = ISC_R_CONNREFUSED;
+ break;
+ case ENETUNREACH:
+ status = ISC_R_NETUNREACH;
+ break;
+ default:
+ status = uerr2isc (error);
+ break;
+ }
+ omapi_disconnect (h, 1);
+ return status;
+ }
+
+ if (c -> connect_list -> addresses [c -> cptr].addrtype !=
+ AF_INET) {
+ omapi_disconnect (h, 1);
+ return ISC_R_INVALIDARG;
+ }
+
+ memcpy (&c -> remote_addr.sin_addr,
+ &c -> connect_list -> addresses [c -> cptr].address,
+ sizeof c -> remote_addr.sin_addr);
+ c -> remote_addr.sin_family = AF_INET;
+ c -> remote_addr.sin_port =
+ htons (c -> connect_list -> addresses [c -> cptr].port);
+#if defined (HAVE_SA_LEN)
+ c -> remote_addr.sin_len = sizeof c -> remote_addr;
+#endif
+ memset (&c -> remote_addr.sin_zero, 0,
+ sizeof c -> remote_addr.sin_zero);
+ ++c -> cptr;
+
+ error = connect (c -> socket,
+ (struct sockaddr *)&c -> remote_addr,
+ sizeof c -> remote_addr);
+ if (error < 0) {
+ error = errno;
+ if (error != EINPROGRESS) {
+ omapi_disconnect (h, 1);
+ switch (error) {
+ case ECONNREFUSED:
+ status = ISC_R_CONNREFUSED;
+ break;
+ case ENETUNREACH:
+ status = ISC_R_NETUNREACH;
+ break;
+ default:
+ status = uerr2isc (error);
+ break;
+ }
+ return status;
+ }
+ c -> state = omapi_connection_connecting;
+ return ISC_R_INCOMPLETE;
+ }
+ c -> state = omapi_connection_connected;
+ }
+
+ /* I don't know why this would fail, so I'm tempted not to test
+ the return value. */
+ sl = sizeof (c -> local_addr);
+ if (getsockname (c -> socket,
+ (struct sockaddr *)&c -> local_addr, &sl) < 0) {
+ }
+
+ /* Disconnect from I/O object, if any. */
+ if (h -> outer)
+ omapi_unregister_io_object (h);
+
+ status = omapi_register_io_object (h,
+ omapi_connection_readfd,
+ omapi_connection_writefd,
+ omapi_connection_reader,
+ omapi_connection_writer,
+ omapi_connection_reaper);
+
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect (h, 1);
+ return status;
+ }
+
+ omapi_signal_in (h, "connect");
+ omapi_addr_list_dereference (&c -> connect_list, MDL);
+ return ISC_R_SUCCESS;
+}
+
+/* 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) {
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_connection_reaper(): disconnect");
+#endif
+ omapi_disconnect (h, 1);
+ }
+ if (c -> state == omapi_connection_closed) {
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_connection_reaper(): closed");
+#endif
+ return ISC_R_NOTCONNECTED;
+ }
+ return ISC_R_SUCCESS;
+}
+
+static isc_result_t make_dst_key (DST_KEY **dst_key, omapi_object_t *a) {
+ omapi_value_t *name = (omapi_value_t *)0;
+ omapi_value_t *algorithm = (omapi_value_t *)0;
+ omapi_value_t *key = (omapi_value_t *)0;
+ int algorithm_id;
+ char *name_str;
+ isc_result_t status = ISC_R_SUCCESS;
+
+ if (status == ISC_R_SUCCESS)
+ status = omapi_get_value_str
+ (a, (omapi_object_t *)0, "name", &name);
+
+ if (status == ISC_R_SUCCESS)
+ status = omapi_get_value_str
+ (a, (omapi_object_t *)0, "algorithm", &algorithm);
+
+ if (status == ISC_R_SUCCESS)
+ status = omapi_get_value_str
+ (a, (omapi_object_t *)0, "key", &key);
+
+ if (status == ISC_R_SUCCESS) {
+ if ((algorithm -> value -> type == omapi_datatype_data ||
+ algorithm -> value -> type == omapi_datatype_string) &&
+ strncasecmp ((char *)algorithm -> value -> u.buffer.value,
+ NS_TSIG_ALG_HMAC_MD5 ".",
+ algorithm -> value -> u.buffer.len) == 0) {
+ algorithm_id = KEY_HMAC_MD5;
+ } else {
+ status = ISC_R_INVALIDARG;
+ }
+ }
+
+ if (status == ISC_R_SUCCESS) {
+ name_str = dmalloc (name -> value -> u.buffer.len + 1, MDL);
+ if (!name_str)
+ status = ISC_R_NOMEMORY;
+ }
+
+ if (status == ISC_R_SUCCESS) {
+ memcpy (name_str,
+ name -> value -> u.buffer.value,
+ name -> value -> u.buffer.len);
+ name_str [name -> value -> u.buffer.len] = 0;
+
+ *dst_key = dst_buffer_to_key (name_str, algorithm_id, 0, 0,
+ key -> value -> u.buffer.value,
+ key -> value -> u.buffer.len);
+ if (!*dst_key)
+ status = ISC_R_NOMEMORY;
+ }
+
+ if (name_str)
+ dfree (name_str, MDL);
+ if (key)
+ omapi_value_dereference (&key, MDL);
+ if (algorithm)
+ omapi_value_dereference (&algorithm, MDL);
+ if (name)
+ omapi_value_dereference (&name, MDL);
+
+ return status;
+}
+
+isc_result_t omapi_connection_sign_data (int mode,
+ DST_KEY *key,
+ void **context,
+ const unsigned char *data,
+ const unsigned len,
+ omapi_typed_data_t **result)
+{
+ omapi_typed_data_t *td = (omapi_typed_data_t *)0;
+ isc_result_t status;
+ int r;
+
+ if (mode & SIG_MODE_FINAL) {
+ status = omapi_typed_data_new (MDL, &td,
+ omapi_datatype_data,
+ dst_sig_size (key));
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ r = dst_sign_data (mode, key, context, data, len,
+ td ? td -> u.buffer.value : (u_char *)0,
+ td ? td -> u.buffer.len : 0);
+
+ /* dst_sign_data() really should do this for us, shouldn't it? */
+ if (mode & SIG_MODE_FINAL)
+ *context = (void *)0;
+
+ if (r < 0) {
+ if (td)
+ omapi_typed_data_dereference (&td, MDL);
+ return ISC_R_INVALIDKEY;
+ }
+
+ if (result && td) {
+ omapi_typed_data_reference (result, td, MDL);
+ }
+
+ if (td)
+ omapi_typed_data_dereference (&td, MDL);
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_connection_output_auth_length (omapi_object_t *h,
+ unsigned *l)
+{
+ omapi_connection_object_t *c;
+
+ if (h -> type != omapi_type_connection)
+ return ISC_R_INVALIDARG;
+ c = (omapi_connection_object_t *)h;
+
+ if (!c -> out_key)
+ return ISC_R_NOTFOUND;
+
+ *l = dst_sig_size (c -> out_key);
+ 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)
+{
+ omapi_connection_object_t *c;
+ isc_result_t status;
+
+ if (h -> type != omapi_type_connection)
+ return ISC_R_INVALIDARG;
+ c = (omapi_connection_object_t *)h;
+
+ if (omapi_ds_strcmp (name, "input-authenticator") == 0) {
+ if (value && value -> type != omapi_datatype_object)
+ return ISC_R_INVALIDARG;
+
+ if (c -> in_context) {
+ omapi_connection_sign_data (SIG_MODE_FINAL,
+ c -> in_key,
+ &c -> in_context,
+ 0, 0,
+ (omapi_typed_data_t **) 0);
+ }
+
+ if (c -> in_key) {
+ dst_free_key (c -> in_key);
+ c -> in_key = (DST_KEY *)0;
+ }
+
+ if (value) {
+ status = make_dst_key (&c -> in_key,
+ value -> u.object);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+ }
+ else if (omapi_ds_strcmp (name, "output-authenticator") == 0) {
+ if (value && value -> type != omapi_datatype_object)
+ return ISC_R_INVALIDARG;
+
+ if (c -> out_context) {
+ omapi_connection_sign_data (SIG_MODE_FINAL,
+ c -> out_key,
+ &c -> out_context,
+ 0, 0,
+ (omapi_typed_data_t **) 0);
+ }
+
+ if (c -> out_key) {
+ dst_free_key (c -> out_key);
+ c -> out_key = (DST_KEY *)0;
+ }
+
+ if (value) {
+ status = make_dst_key (&c -> out_key,
+ value -> u.object);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ }
+
+ return ISC_R_SUCCESS;
+ }
+
+ 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)
+{
+ omapi_connection_object_t *c;
+ omapi_typed_data_t *td = (omapi_typed_data_t *)0;
+ isc_result_t status;
+
+ if (h -> type != omapi_type_connection)
+ return ISC_R_INVALIDARG;
+ c = (omapi_connection_object_t *)h;
+
+ if (omapi_ds_strcmp (name, "input-signature") == 0) {
+ if (!c -> in_key || !c -> in_context)
+ return ISC_R_NOTFOUND;
+
+ status = omapi_connection_sign_data (SIG_MODE_FINAL,
+ c -> in_key,
+ &c -> in_context,
+ 0, 0, &td);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_make_value (value, name, td, MDL);
+ omapi_typed_data_dereference (&td, MDL);
+ return status;
+
+ } else if (omapi_ds_strcmp (name, "input-signature-size") == 0) {
+ if (!c -> in_key)
+ return ISC_R_NOTFOUND;
+
+ return omapi_make_int_value (value, name,
+ dst_sig_size (c -> in_key), MDL);
+
+ } else if (omapi_ds_strcmp (name, "output-signature") == 0) {
+ if (!c -> out_key || !c -> out_context)
+ return ISC_R_NOTFOUND;
+
+ status = omapi_connection_sign_data (SIG_MODE_FINAL,
+ c -> out_key,
+ &c -> out_context,
+ 0, 0, &td);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ status = omapi_make_value (value, name, td, MDL);
+ omapi_typed_data_dereference (&td, MDL);
+ return status;
+
+ } else if (omapi_ds_strcmp (name, "output-signature-size") == 0) {
+ if (!c -> out_key)
+ return ISC_R_NOTFOUND;
+
+ return omapi_make_int_value (value, name,
+ dst_sig_size (c -> out_key), MDL);
+ }
+
+ if (h -> inner && h -> inner -> type -> get_value)
+ return (*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_connection_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ omapi_connection_object_t *c;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_connection_destroy()");
+#endif
+
+ 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_listener_dereference (&c -> listener, file, line);
+ if (c -> connect_list)
+ omapi_addr_list_dereference (&c -> connect_list, file, line);
+ 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;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_connection_signal_handler(%s)", name);
+#endif
+
+ 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;
+}